forked from dolphin-emu/dolphin
		
	
		
			
	
	
		
			308 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			308 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|   | #include "X11Window.h"
 | ||
|  | 
 | ||
|  | X11Window::X11Window(int _iwidth, int _iheight) { | ||
|  |     int _twidth,  _theight; | ||
|  |     if(g_Config.bFullscreen) { | ||
|  | 	if(strlen(g_Config.iFSResolution) > 1) { | ||
|  | 	    sscanf(g_Config.iFSResolution, "%dx%d", &_twidth, &_theight); | ||
|  |         } | ||
|  |         else { // No full screen reso set, fall back to default {
 | ||
|  |             _twidth = _iwidth; | ||
|  |             _theight = _iheight; | ||
|  |         } | ||
|  |     } else  {// Going Windowed
 | ||
|  |         if(strlen(g_Config.iWindowedRes) > 1) { | ||
|  | 	    sscanf(g_Config.iWindowedRes, "%dx%d", &_twidth, &_theight); | ||
|  |         } | ||
|  |         else { // No Window reso set, fall back to default
 | ||
|  |             _twidth = _iwidth; | ||
|  |             _theight = _iheight; | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     SetSize(_twidth, _theight); | ||
|  | 
 | ||
|  | 
 | ||
|  |     // ---------------------------------------------------------------------------------------
 | ||
|  |     // Control window size and picture scaling
 | ||
|  |     // ------------------
 | ||
|  |     // nBackbufferWidth and nBackbufferHeight = Screen resolution from ini, or 640x480
 | ||
|  |     // See OpenGL_Update() for documentation of the other variables
 | ||
|  |     // ------------------
 | ||
|  |     nBackbufferWidth = _twidth; | ||
|  |     nBackbufferHeight = _theight; | ||
|  |      | ||
|  |     float FactorW  = 640.0f / (float)nBackbufferWidth; | ||
|  |     float FactorH  = 480.0f / (float)nBackbufferHeight; | ||
|  |     float Max = (FactorW < FactorH) ? FactorH : FactorW; | ||
|  |      | ||
|  |     if(g_Config.bStretchToFit) { | ||
|  | 	MValueX = 1.0f / FactorW; | ||
|  | 	MValueY = 1.0f / FactorH; | ||
|  | 	nXoff = 0; | ||
|  | 	nYoff = 0; | ||
|  |     } else { | ||
|  | 	MValueX = 1.0f / Max; | ||
|  | 	MValueY = 1.0f / Max; | ||
|  | 	nXoff = (int)((nBackbufferWidth - (640 * MValueX)) / 2); | ||
|  | 	nYoff = (int)((nBackbufferHeight - (480 * MValueY)) / 2); | ||
|  |     } | ||
|  |      | ||
|  |     XVisualInfo *vi; | ||
|  |     Colormap cmap; | ||
|  |     int dpyWidth, dpyHeight; | ||
|  |     int glxMajorVersion, glxMinorVersion; | ||
|  |     int vidModeMajorVersion, vidModeMinorVersion; | ||
|  |     Atom wmDelete; | ||
|  | 
 | ||
|  |     // attributes for a single buffered visual in RGBA format with at least
 | ||
|  |     // 8 bits per color and a 24 bit depth buffer
 | ||
|  |     int attrListSgl[] = {GLX_RGBA, GLX_RED_SIZE, 8,  | ||
|  |                          GLX_GREEN_SIZE, 8,  | ||
|  |                          GLX_BLUE_SIZE, 8,  | ||
|  |                          GLX_DEPTH_SIZE, 24, | ||
|  |                          None}; | ||
|  | 
 | ||
|  |     // attributes for a double buffered visual in RGBA format with at least 
 | ||
|  |     // 8 bits per color and a 24 bit depth buffer
 | ||
|  |     int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER,  | ||
|  |                           GLX_RED_SIZE, 8,  | ||
|  |                           GLX_GREEN_SIZE, 8,  | ||
|  |                           GLX_BLUE_SIZE, 8,  | ||
|  |                           GLX_DEPTH_SIZE, 24, | ||
|  |                           GLX_SAMPLE_BUFFERS_ARB, g_Config.iMultisampleMode, GLX_SAMPLES_ARB, 1, None }; | ||
|  |     dpy = XOpenDisplay(0); | ||
|  |     g_VideoInitialize.pWindowHandle = (HWND)dpy; | ||
|  |     screen = DefaultScreen(dpy); | ||
|  |      | ||
|  |     fs = g_Config.bFullscreen; //Set to setting in Options
 | ||
|  |      | ||
|  |     /* get an appropriate visual */ | ||
|  |     vi = glXChooseVisual(dpy, screen, attrListDbl); | ||
|  |     if (vi == NULL) { | ||
|  |         vi = glXChooseVisual(dpy, screen, attrListSgl); | ||
|  |         doubleBuffered = False; | ||
|  |         ERROR_LOG("Only Singlebuffered Visual!\n"); | ||
|  |     } else { | ||
|  |         doubleBuffered = True; | ||
|  |         ERROR_LOG("Got Doublebuffered Visual!\n"); | ||
|  |     } | ||
|  |      | ||
|  |     glXQueryVersion(dpy, &glxMajorVersion, &glxMinorVersion); | ||
|  |     ERROR_LOG("glX-Version %d.%d\n", glxMajorVersion, glxMinorVersion); | ||
|  |     /* create a GLX context */ | ||
|  |     ctx = glXCreateContext(dpy, vi, 0, GL_TRUE); | ||
|  |     if(!ctx) { | ||
|  |         ERROR_LOG("Couldn't Create GLX context.Quit"); | ||
|  |         exit(0); // TODO: Don't bring down entire Emu
 | ||
|  |     } | ||
|  | 
 | ||
|  |     /* create a color map */ | ||
|  |     cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), | ||
|  |                            vi->visual, AllocNone); | ||
|  |     attr.colormap = cmap; | ||
|  |     attr.border_pixel = 0; | ||
|  | 
 | ||
|  |     // get a connection
 | ||
|  |     XF86VidModeQueryVersion(dpy, &vidModeMajorVersion, &vidModeMinorVersion); | ||
|  | 
 | ||
|  |     if (fs) { | ||
|  |          | ||
|  |         XF86VidModeModeInfo **modes = NULL; | ||
|  |         int modeNum = 0; | ||
|  |         int bestMode = 0; | ||
|  | 	 | ||
|  |         // set best mode to current
 | ||
|  |         bestMode = 0; | ||
|  |         ERROR_LOG("XF86VidModeExtension-Version %d.%d\n", vidModeMajorVersion, vidModeMinorVersion); | ||
|  |         XF86VidModeGetAllModeLines(dpy, screen, &modeNum, &modes); | ||
|  |          | ||
|  |         if (modeNum > 0 && modes != NULL) { | ||
|  |             /* save desktop-resolution before switching modes */ | ||
|  |             deskMode = *modes[0]; | ||
|  |             /* look for mode with requested resolution */ | ||
|  |             for (int i = 0; i < modeNum; i++) { | ||
|  |                 if ((modes[i]->hdisplay == _twidth) &&  | ||
|  | 		    (modes[i]->vdisplay == _theight)) { | ||
|  |                     bestMode = i; | ||
|  |                 } | ||
|  |             }     | ||
|  | 	     | ||
|  |             XF86VidModeSwitchToMode(dpy, screen, modes[bestMode]); | ||
|  |             XF86VidModeSetViewPort(dpy, screen, 0, 0); | ||
|  |             dpyWidth = modes[bestMode]->hdisplay; | ||
|  |             dpyHeight = modes[bestMode]->vdisplay; | ||
|  |             ERROR_LOG("Resolution %dx%d\n", dpyWidth, dpyHeight); | ||
|  |             XFree(modes); | ||
|  |              | ||
|  |             /* create a fullscreen window */ | ||
|  |             attr.override_redirect = True; | ||
|  |             attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask |  | ||
|  | 		KeyReleaseMask | ButtonReleaseMask | StructureNotifyMask; | ||
|  |             win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 0, 0,  | ||
|  | 				dpyWidth, dpyHeight, 0, vi->depth, InputOutput, | ||
|  | 				vi->visual, CWBorderPixel | CWColormap |  | ||
|  | 				CWEventMask | CWOverrideRedirect, &attr); | ||
|  |             XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0); | ||
|  |             XMapRaised(dpy, win); | ||
|  |             XGrabKeyboard(dpy, win, True, GrabModeAsync,  | ||
|  | 			  GrabModeAsync, CurrentTime); | ||
|  |             XGrabPointer(dpy, win, True, ButtonPressMask, | ||
|  |                          GrabModeAsync, GrabModeAsync, win, None, CurrentTime); | ||
|  |         } | ||
|  |         else { | ||
|  |             ERROR_LOG("Failed to start fullscreen. If you received the \n" | ||
|  |                       "\"XFree86-VidModeExtension\" extension is missing, add\n" | ||
|  |                       "Load \"extmod\"\n" | ||
|  |                       "to your X configuration file (under the Module Section)\n"); | ||
|  |             fs = 0; | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     if (!fs) { | ||
|  | 	 | ||
|  |         // create a window in window mode
 | ||
|  |         attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask |  | ||
|  | 	    KeyReleaseMask | ButtonReleaseMask | | ||
|  |             StructureNotifyMask  | ResizeRedirectMask; | ||
|  |         win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 0, 0, _twidth,  | ||
|  | 			    _theight, 0, vi->depth, InputOutput, vi->visual, | ||
|  | 			    CWBorderPixel | CWColormap | CWEventMask, &attr); | ||
|  |         // only set window title and handle wm_delete_events if in windowed mode
 | ||
|  |         wmDelete = XInternAtom(dpy, "WM_DELETE_WINDOW", True); | ||
|  |         XSetWMProtocols(dpy, win, &wmDelete, 1); | ||
|  |         XSetStandardProperties(dpy, win, "GPU", | ||
|  | 			       "GPU", None, NULL, 0, NULL); | ||
|  |         XMapRaised(dpy, win); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | X11Window::~X11Window() { | ||
|  |     if (ctx) { | ||
|  |         if (!glXMakeCurrent(dpy, None, NULL)) { | ||
|  |             ERROR_LOG("Could not release drawing context.\n"); | ||
|  |         } | ||
|  |         XUnmapWindow(dpy, win); | ||
|  |         glXDestroyContext(dpy, ctx); | ||
|  |         XCloseDisplay(dpy); | ||
|  |         ctx = NULL; | ||
|  |     } | ||
|  |     /* switch back to original desktop resolution if we were in fs */ | ||
|  |     if (dpy != NULL) { | ||
|  |         if (fs) { | ||
|  | 	    XF86VidModeSwitchToMode(dpy, screen, &deskMode); | ||
|  | 	    XF86VidModeSetViewPort(dpy, screen, 0, 0); | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void X11Window::SwapBuffers() { | ||
|  |     glXSwapBuffers(dpy, win); | ||
|  | } | ||
|  | 
 | ||
|  | void X11Window::SetWindowText(const char *text) { | ||
|  |     /**
 | ||
|  |      * Tell X to ask the window manager to set the window title. (X | ||
|  |      * itself doesn't provide window title functionality.) | ||
|  |      */ | ||
|  |     XStoreName(dpy, win, text);    | ||
|  | } | ||
|  | 
 | ||
|  | bool X11Window::PeekMessages() { | ||
|  |     // TODO: implement
 | ||
|  |     return false; | ||
|  | } | ||
|  | void X11Window::Update() { | ||
|  |     // We just check all of our events here
 | ||
|  |     XEvent event; | ||
|  |     KeySym key; | ||
|  |     static bool ShiftPressed = false; | ||
|  |     static bool ControlPressed = false; | ||
|  |     static int FKeyPressed = -1; | ||
|  |     int x,y; | ||
|  |     u32 w,h,depth; | ||
|  | 
 | ||
|  |     int num_events; | ||
|  |     for (num_events = XPending(dpy);num_events > 0;num_events--) { | ||
|  |         XNextEvent(dpy, &event); | ||
|  |         switch(event.type) { | ||
|  | 	case KeyRelease: | ||
|  | 	    key = XLookupKeysym((XKeyEvent*)&event, 0); | ||
|  | 	    if(key >= XK_F1 && key <= XK_F9) { | ||
|  | 		g_VideoInitialize.pKeyPress(FKeyPressed, ShiftPressed, ControlPressed); | ||
|  | 		FKeyPressed = -1; | ||
|  | 	    } else { | ||
|  | 		if(key == XK_Shift_L || key == XK_Shift_R) | ||
|  | 		    ShiftPressed = false; | ||
|  | 		else if(key == XK_Control_L || key == XK_Control_R) | ||
|  | 		    ControlPressed = false; | ||
|  | 		else | ||
|  | 		    XPutBackEvent(dpy, &event); | ||
|  | 	    } | ||
|  | 	    break; | ||
|  | 	case KeyPress: | ||
|  | 	    key = XLookupKeysym((XKeyEvent*)&event, 0); | ||
|  | 	    if(key >= XK_F1 && key <= XK_F9)  | ||
|  | 		FKeyPressed = key - 0xff4e; | ||
|  | 	    else { | ||
|  | 		if(key == XK_Shift_L || key == XK_Shift_R)  | ||
|  | 		    ShiftPressed = true; | ||
|  | 		else if(key == XK_Control_L || key == XK_Control_R) | ||
|  | 		    ControlPressed = true; | ||
|  | 		else | ||
|  | 		    XPutBackEvent(dpy, &event); | ||
|  | 	    } | ||
|  | 	    break; | ||
|  | 	case ButtonPress: | ||
|  | 	case ButtonRelease: | ||
|  | 	    XPutBackEvent(dpy, &event); | ||
|  | 	    break; | ||
|  | 	case ConfigureNotify: | ||
|  | 	    Window winDummy; | ||
|  | 	    unsigned int borderDummy; | ||
|  | 	    XGetGeometry(dpy, win, &winDummy, &x, &y, | ||
|  | 			 &w, &h, &borderDummy, &depth); | ||
|  | 	    SetSize(w, h); | ||
|  | 	    nBackbufferWidth = w; | ||
|  | 	    nBackbufferHeight = h; | ||
|  | 	    break; | ||
|  | 	case ClientMessage: //TODO: We aren't reading this correctly, It could be anything, highest chance is that it's a close event though
 | ||
|  | 	    Video_Shutdown(); // Calling from here since returning false does nothing
 | ||
|  | 	    return;  | ||
|  | 	    break; | ||
|  | 	default: | ||
|  | 	    //TODO: Should we put the event back if we don't handle it?
 | ||
|  | 	    // I think we handle all the needed ones, the rest shouldn't matter
 | ||
|  | 	    // But to be safe, let's but them back anyway
 | ||
|  | 	    //XPutBackEvent(dpy, &event);
 | ||
|  | 	    break; | ||
|  | 	} | ||
|  |     } | ||
|  |     return; | ||
|  | } | ||
|  | 
 | ||
|  | bool X11Window::MakeCurrent() { | ||
|  | 
 | ||
|  |     Window winDummy; | ||
|  |     unsigned int borderDummy; | ||
|  |     int x,y; | ||
|  |     u32 w,h,depth; | ||
|  |     // connect the glx-context to the window
 | ||
|  |     glXMakeCurrent(dpy, win, ctx); | ||
|  |     XGetGeometry(dpy, win, &winDummy, &x, &y, | ||
|  |                  &w, &h, &borderDummy, &depth); | ||
|  |     SetSize(w, h); | ||
|  |     ERROR_LOG("GLWin Depth %d", depth); | ||
|  |     if (glXIsDirect(dpy, ctx))  | ||
|  |         ERROR_LOG("you have Direct Rendering!"); | ||
|  |     else | ||
|  |         ERROR_LOG("no Direct Rendering possible!"); | ||
|  | 
 | ||
|  |     // better for pad plugin key input (thc)
 | ||
|  |     XSelectInput(dpy, win, ExposureMask | KeyPressMask | ButtonPressMask |  | ||
|  | 		 KeyReleaseMask | ButtonReleaseMask | StructureNotifyMask |  | ||
|  | 		 EnterWindowMask | LeaveWindowMask | FocusChangeMask); | ||
|  |      | ||
|  |     return true; | ||
|  | } |