Linux_Platform.cpp
Go to the documentation of this file.
00001 /************************************************************************************
00002 
00003 Filename    :   Platform_Linux.cpp
00004 Content     :   Linux (X11) implementation of Platform app infrastructure
00005 Created     :   September 6, 2012
00006 Authors     :   Andrew Reisse
00007 
00008 Copyright   :   Copyright 2012 Oculus VR, Inc. All Rights reserved.
00009 
00010 Use of this software is subject to the terms of the Oculus LLC license
00011 agreement provided at the time of installation or download, or which
00012 otherwise accompanies this software in either electronic or hard copy form.
00013 
00014 ************************************************************************************/
00015 
00016 #include "Kernel/OVR_System.h"
00017 #include "Kernel/OVR_Array.h"
00018 #include "Kernel/OVR_String.h"
00019 #include "Kernel/OVR_Timer.h"
00020 
00021 #include "Linux_Platform.h"
00022 #include "Linux_Gamepad.h"
00023 
00024 // Renderers
00025 #include "../Render/Render_GL_Device.h"
00026 
00027 #include <X11/extensions/Xinerama.h>
00028 
00029 
00030 namespace OVR { namespace Platform { namespace Linux {
00031 
00032 static const char *AtomNames[] = {"WM_PROTOCOLS", "WM_DELETE_WINDOW"};
00033 
00034 PlatformCore::PlatformCore(Application* app)
00035     : Platform::PlatformCore(app), Disp(NULL), Win(0), Vis(NULL), Quit(0), MMode(Mouse_Normal)
00036 {
00037     pGamepadManager = *new Linux::GamepadManager();
00038 }
00039 PlatformCore::~PlatformCore()
00040 {
00041     XFreeCursor(Disp, InvisibleCursor);
00042 
00043     if (Disp)
00044         XCloseDisplay(Disp);
00045 }
00046 
00047 // Setup an X11 window in windowed mode.
00048 bool PlatformCore::SetupWindow(int w, int h)
00049 {
00050 
00051     if (!Disp)
00052     {
00053         XInitThreads();
00054 
00055         Disp = XOpenDisplay(NULL);
00056         if (!Disp)
00057         {
00058             OVR_DEBUG_LOG(("XOpenDisplay failed."));
00059             return false;
00060         }
00061 
00062         XInternAtoms(Disp, const_cast<char**>(AtomNames), NumAtoms, false, Atoms);
00063     }
00064 
00065     XSetWindowAttributes winattr;
00066     unsigned attrmask = CWEventMask | CWBorderPixel;
00067 
00068     winattr.event_mask = ButtonPressMask|ButtonReleaseMask|KeyPressMask|KeyReleaseMask|ButtonMotionMask|PointerMotionMask|
00069         /*PointerMotionHintMask|*/StructureNotifyMask;//|ExposureMask;
00070     winattr.border_pixel = 0;
00071 
00072     int screenNumber = DefaultScreen(Disp);
00073 
00074     if (!Vis)
00075     {
00076         int attr[16];
00077         int nattr = 2;
00078 
00079         attr[0] = GLX_RGBA;
00080         attr[1] = GLX_DOUBLEBUFFER;
00081         attr[nattr++] = GLX_DEPTH_SIZE;
00082         attr[nattr++] = 24;
00083         attr[nattr] = 0;
00084 
00085         Vis = glXChooseVisual(Disp, screenNumber, attr);
00086 
00087         if (!Vis)
00088         {
00089             OVR_DEBUG_LOG(("glXChooseVisual failed."));
00090             return false;
00091         }
00092     }
00093 
00094     Window rootWindow = XRootWindow(Disp, Vis->screen);
00095 
00096     winattr.colormap = XCreateColormap(Disp, rootWindow, Vis->visual, AllocNone);
00097     attrmask |= CWColormap;
00098 
00099 
00100     Win = XCreateWindow(Disp, rootWindow, 0, 0, w, h, 0, Vis->depth,
00101                         InputOutput, Vis->visual, attrmask, &winattr);
00102 
00103     if (!Win)
00104     {
00105         OVR_DEBUG_LOG(("XCreateWindow failed."));
00106         return false;
00107     }
00108 
00109 
00110     XStoreName(Disp, Win, "OVR App");
00111     XSetWMProtocols(Disp, Win, &Atoms[WM_DELETE_WINDOW], 1);
00112 
00113     // Intialize empty cursor for show/hide.
00114     XColor black;
00115     static char noData[] = { 0,0,0,0,0,0,0,0 };
00116     black.red = black.green = black.blue = 0;
00117 
00118     Pixmap bitmapNoData = XCreateBitmapFromData(Disp, Win, noData, 8, 8);
00119     InvisibleCursor = XCreatePixmapCursor(Disp, bitmapNoData, bitmapNoData,
00120                                          &black, &black, 0, 0);
00121     XDefineCursor(Disp, Win, InvisibleCursor);
00122 
00123     Width = w;
00124     Height = h;
00125 
00126     return true;
00127 }
00128 
00129 void PlatformCore::SetMouseMode(MouseMode mm)
00130 {
00131     if (mm == MMode)
00132         return;
00133 
00134     if (Win)
00135     {
00136         if (mm == Mouse_Relative)
00137         {
00138             XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
00139         }
00140         else
00141         {
00142             //if (MMode == Mouse_Relative)
00143             //  ShowCursor(TRUE);
00144         }
00145     }
00146     MMode = mm;
00147 }
00148 
00149 void PlatformCore::GetWindowSize(int* w, int* h) const
00150 {
00151     *w = Width;
00152     *h = Height;
00153 }
00154 
00155 void PlatformCore::SetWindowTitle(const char* title)
00156 {
00157     XStoreName(Disp, Win, title);
00158 }
00159     
00160 void PlatformCore::ShowWindow(bool show)
00161 {
00162     if (show)
00163         XRaiseWindow(Disp, Win);
00164     else
00165         XIconifyWindow(Disp, Win, 0);
00166 }
00167 
00168 void PlatformCore::DestroyWindow()
00169 {
00170     if (Win)
00171         XDestroyWindow(Disp, Win);
00172     Win = 0;
00173 }
00174 
00175 
00176 static int KeyMap[][2] =
00177 {
00178     { XK_BackSpace,      Key_Backspace },
00179     { XK_Tab,       Key_Tab },
00180     { XK_Clear,     Key_Clear },
00181     { XK_Return,    Key_Return },
00182     { XK_Shift_L,     Key_Shift },
00183     { XK_Control_L,   Key_Control },
00184     { XK_Alt_L,      Key_Alt },
00185     { XK_Shift_R,     Key_Shift },
00186     { XK_Control_R,   Key_Control },
00187     { XK_Alt_R,      Key_Alt },
00188     { XK_Pause,     Key_Pause },
00189     { XK_Caps_Lock,   Key_CapsLock },
00190     { XK_Escape,    Key_Escape },
00191     { XK_space,     Key_Space },
00192     { XK_Page_Up,     Key_PageUp },
00193     { XK_Page_Down,      Key_PageDown },
00194     { XK_Prior,     Key_PageUp },
00195     { XK_Next,      Key_PageDown },
00196     { XK_End,       Key_End },
00197     { XK_Home,      Key_Home },
00198     { XK_Left,      Key_Left },
00199     { XK_Up,        Key_Up },
00200     { XK_Right,     Key_Right },
00201     { XK_Down,      Key_Down },
00202     { XK_Insert,    Key_Insert },
00203     { XK_Delete,    Key_Delete },
00204     { XK_Help,      Key_Help },
00205     { XK_Num_Lock,   Key_NumLock },
00206     { XK_Scroll_Lock,    Key_ScrollLock },
00207 };
00208 
00209 
00210 static KeyCode MapXKToKeyCode(unsigned vk)
00211 {
00212     unsigned key = Key_None;
00213 
00214     if ((vk >= 'a') && (vk <= 'z'))
00215     {
00216         key = vk - 'a' + Key_A;
00217     }
00218     else if ((vk >= ' ') && (vk <= '~'))
00219     {
00220         key = vk;
00221     }
00222     else if ((vk >= XK_KP_0) && (vk <= XK_KP_9))
00223     {
00224         key = vk - XK_KP_0 + Key_KP_0;
00225     }
00226     else if ((vk >= XK_F1) && (vk <= XK_F15))
00227     {
00228         key = vk - XK_F1 + Key_F1;
00229     }
00230     else 
00231     {
00232         for (unsigned i = 0; i< (sizeof(KeyMap) / sizeof(KeyMap[1])); i++)
00233         {
00234             if (vk == KeyMap[i][0])
00235             {                
00236                 key = KeyMap[i][1];
00237                 break;
00238             }
00239         }
00240     }
00241 
00242     return (KeyCode)key;
00243 }
00244 
00245 static int MapModifiers(int xmod)
00246 {
00247     int mod = 0;
00248     if (xmod & ShiftMask)
00249         mod |= Mod_Shift;
00250     if (xmod & ControlMask)
00251         mod |= Mod_Control;
00252     if (xmod & Mod1Mask)
00253         mod |= Mod_Alt;
00254     if (xmod & Mod4Mask)
00255         mod |= Mod_Meta;
00256     return mod;
00257 }
00258 
00259 void PlatformCore::processEvent(XEvent& event)
00260 {
00261     switch (event.xany.type)
00262     {
00263     case ConfigureNotify:
00264         if (event.xconfigure.width != Width || event.xconfigure.height != Height)
00265         {
00266             Width = event.xconfigure.width;
00267             Height = event.xconfigure.height;
00268             pApp->OnResize(Width, Height);
00269 
00270             if (pRender)
00271                 pRender->SetWindowSize(Width, Height);
00272         }
00273         break;
00274 
00275     case KeyPress:
00276     case KeyRelease:
00277         {
00278             char chars[8] = {0};
00279             KeySym xk;
00280             XComposeStatus comp;
00281             XLookupString(&event.xkey, chars, sizeof(chars), &xk, &comp);
00282             if (xk != XK_VoidSymbol)
00283                 pApp->OnKey(MapXKToKeyCode((unsigned)xk), chars[0], event.xany.type == KeyPress, MapModifiers(event.xkey.state));
00284             if (xk == XK_Escape && MMode == Mouse_Relative)
00285             {
00286                 //ungrab
00287                 MMode = Mouse_RelativeEscaped;
00288                 showCursor(true);
00289             }
00290         }
00291         break;
00292 
00293     case MotionNotify:
00294         if (MMode == Mouse_Relative)
00295         {
00296             int dx = event.xmotion.x - Width/2;
00297             int dy = event.xmotion.y - Height/2;
00298 
00299             // do not remove this check, WarpPointer generates events too.
00300             if (dx == 0 && dy == 0)
00301                 break;
00302 
00303             XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
00304             pApp->OnMouseMove(dx, dy, Mod_MouseRelative|MapModifiers(event.xmotion.state));
00305         }
00306         else
00307         {
00308             pApp->OnMouseMove(event.xmotion.x, event.xmotion.y, MapModifiers(event.xmotion.state));
00309         }
00310         break;
00311 
00312     case MapNotify:
00313         if (MMode == Mouse_Relative)
00314         {            
00315             XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
00316             showCursor(false);
00317         }
00318         break;
00319 
00320     case ButtonPress:
00321         if (event.xbutton.button == 1)
00322         {
00323             //grab
00324 
00325             if (MMode == Mouse_RelativeEscaped)
00326             {            
00327                 XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
00328                 showCursor(false);
00329                 MMode = Mouse_Relative;
00330             }
00331         }
00332         break;
00333 
00334     case FocusOut:
00335         if (MMode == Mouse_Relative)
00336         {            
00337             MMode = Mouse_RelativeEscaped;
00338             showCursor(true);
00339         }
00340         break;
00341 
00342     case ClientMessage:
00343         if (event.xclient.message_type == Atoms[WM_PROTOCOLS] &&
00344             Atom(event.xclient.data.l[0]) == Atoms[WM_DELETE_WINDOW])
00345             pApp->OnQuitRequest();
00346         break;
00347     }
00348 }
00349 
00350 int PlatformCore::Run()
00351 {
00352     while (!Quit)
00353     {
00354         if (XPending(Disp))
00355         {
00356             XEvent event;
00357             XNextEvent(Disp, &event);
00358 
00359             if (pApp && event.xany.window == Win)
00360                 processEvent(event);
00361         }
00362         else
00363         {
00364             pApp->OnIdle();
00365         }
00366     }
00367 
00368     return ExitCode;
00369 }
00370 
00371 bool PlatformCore::determineScreenOffset(int screenId, int* screenOffsetX, int* screenOffsetY)
00372 {
00373     Display* display = XOpenDisplay(NULL);
00374 
00375     bool foundScreen = false;
00376 
00377     if (display)
00378     {
00379         int numberOfScreens;
00380         XineramaScreenInfo* screens = XineramaQueryScreens(display, &numberOfScreens);
00381 
00382         if (screenId < numberOfScreens)
00383         {
00384             XineramaScreenInfo screenInfo = screens[screenId];
00385             *screenOffsetX = screenInfo.x_org;
00386             *screenOffsetY = screenInfo.y_org;
00387 
00388             foundScreen = true;
00389         }
00390 
00391         XFree(screens);
00392     }
00393 
00394     return foundScreen;
00395 }
00396 
00397 void PlatformCore::showWindowDecorations(bool show)
00398 {
00399     // Declaration of 'MOTIF_WM_HINTS' struct and flags can be found at:
00400     // https://people.gnome.org/~tthurman/docs/metacity/xprops_8h-source.html
00401     typedef struct WMHints
00402     {
00403         unsigned long   flags;
00404         unsigned long   functions;
00405         unsigned long   decorations;
00406         long            inputMode;
00407         unsigned long   status;
00408     } Hints;
00409 
00410     #define MWM_DECOR_ALL           (1L << 0)
00411     #define MWM_DECOR_BORDER        (1L << 1)
00412     #define MWM_DECOR_RESIZEH       (1L << 2)
00413     #define MWM_DECOR_TITLE         (1L << 3)
00414     #define MWM_DECOR_MENU          (1L << 4)
00415     #define MWM_DECOR_MINIMIZE      (1L << 5)
00416     #define MWM_DECOR_MAXIMIZE      (1L << 6)
00417 
00418     Atom property = XInternAtom(Disp, "_MOTIF_WM_HINTS", true);
00419 
00420     Hints hints;
00421     hints.flags = 2;    // We only want to specify decoration.
00422 
00423     if (show)
00424     {
00425         hints.decorations = MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE;
00426     }
00427     else
00428     {
00429         // Remove all window border items.
00430         hints.decorations = 0;
00431     }
00432 
00433     XChangeProperty(Disp,Win,property,property,32,PropModeReplace,(unsigned char *)&hints,5);
00434 }
00435 
00436 bool PlatformCore::SetFullscreen(const Render::RendererParams& rp, int fullscreen)
00437 {
00438     if (rp.Fullscreen == Render::Display_Window && fullscreen == Render::Display_FakeFullscreen)
00439     {
00440         // Transitioning from windowed to fake fullscreen.
00441         int xOffset;
00442         int yOffset;
00443 
00444         if (!determineScreenOffset(rp.Display.CgDisplayId, &xOffset, &yOffset))
00445         {
00446             return false;
00447         }
00448 
00449         showWindowDecorations(false);
00450 
00451         XMoveWindow(Disp, Win, xOffset, yOffset);
00452         XMapRaised(Disp, Win);
00453 
00454         Platform::PlatformCore::SetFullscreen(rp, fullscreen);
00455         return true;
00456     }
00457     else if (rp.Fullscreen == Render::Display_FakeFullscreen && fullscreen == Render::Display_Window)
00458     {
00459         // Transitioning from fake fullscreen to windowed.
00460         showWindowDecorations(true);
00461 
00462         XMoveWindow(Disp, Win, 0, 0);
00463         XMapRaised(Disp, Win);
00464 
00465         Platform::PlatformCore::SetFullscreen(rp, fullscreen);
00466         return true;
00467     }
00468     else if (fullscreen == Render::Display_Fullscreen)
00469     {
00470         return false;
00471     }
00472 }
00473 
00474 RenderDevice* PlatformCore::SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc,
00475                                           const char* type, const Render::RendererParams& rp)
00476 {
00477     const SetupGraphicsDeviceSet* setupDesc = setupGraphicsDesc.PickSetupDevice(type);
00478     OVR_ASSERT(setupDesc);
00479         
00480     pRender = *setupDesc->pCreateDevice(rp, this);
00481     if (pRender)
00482         pRender->SetWindowSize(Width, Height);
00483         
00484     return pRender.GetPtr();
00485 }
00486 
00487 void PlatformCore::showCursor(bool show)
00488 {
00489     if (show)
00490     {
00491         XUndefineCursor(Disp, Win);
00492     }
00493     else
00494     {
00495         XDefineCursor(Disp, Win, InvisibleCursor);
00496     }
00497 }
00498 
00499 }}
00500 
00501 // GL
00502 namespace Render { namespace GL { namespace Linux {
00503 
00504 Render::RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* oswnd)
00505 {
00506     Platform::Linux::PlatformCore* PC = (Platform::Linux::PlatformCore*)oswnd;
00507 
00508     GLXContext context = glXCreateContext(PC->Disp, PC->Vis, 0, GL_TRUE);
00509 
00510     if (!context)
00511         return NULL;
00512 
00513     if (!glXMakeCurrent(PC->Disp, PC->Win, context))
00514     {
00515         glXDestroyContext(PC->Disp, context);
00516         return NULL;
00517     }
00518 
00519     XMapRaised(PC->Disp, PC->Win);
00520 
00521     return new Render::GL::Linux::RenderDevice(rp, PC->Disp, PC->Win, context);
00522 }
00523 
00524 void RenderDevice::Present()
00525 {
00526     glXSwapBuffers(Disp, Win);
00527 }
00528 
00529 void RenderDevice::Shutdown()
00530 {
00531     if (Context)
00532     {
00533         glXMakeCurrent(Disp, 0, NULL);
00534         glXDestroyContext(Disp, Context);
00535         Context = NULL;
00536         Win = 0;
00537     }
00538 }
00539 
00540 }}}}
00541 
00542 
00543 int main(int argc, const char* argv[])
00544 {
00545     using namespace OVR;
00546     using namespace OVR::Platform;
00547 
00548     // CreateApplication must be the first call since it does OVR::System::Initialize.
00549     Application*       app = Application::CreateApplication();
00550     Linux::PlatformCore* platform = new Linux::PlatformCore(app);
00551     // The platform attached to an app will be deleted by DestroyApplication.
00552     app->SetPlatformCore(platform);
00553 
00554     int exitCode = app->OnStartup(argc, argv);
00555     if (!exitCode)
00556         exitCode = platform->Run();
00557 
00558     // No OVR functions involving memory are allowed after this.
00559     Application::DestroyApplication(app);
00560     app = 0;
00561 
00562     return exitCode;
00563 }


oculus_sdk
Author(s):
autogenerated on Mon Oct 6 2014 03:01:18