aboutsummaryrefslogtreecommitdiffstats
path: root/Samples/CommonSrc/Platform
diff options
context:
space:
mode:
Diffstat (limited to 'Samples/CommonSrc/Platform')
-rw-r--r--Samples/CommonSrc/Platform/Linux_Gamepad.cpp453
-rw-r--r--Samples/CommonSrc/Platform/Linux_Gamepad.h83
-rw-r--r--Samples/CommonSrc/Platform/Linux_Platform.cpp784
-rw-r--r--Samples/CommonSrc/Platform/Linux_Platform.h147
4 files changed, 1467 insertions, 0 deletions
diff --git a/Samples/CommonSrc/Platform/Linux_Gamepad.cpp b/Samples/CommonSrc/Platform/Linux_Gamepad.cpp
new file mode 100644
index 0000000..1546768
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Linux_Gamepad.cpp
@@ -0,0 +1,453 @@
+/************************************************************************************
+
+Filename : Linux_Gamepad.cpp
+Content : Linux implementation of Platform app infrastructure
+Created : May 6, 2013
+Authors : Lee Cooper, Simon Hallam
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <glob.h>
+#include <linux/joystick.h>
+#include "Linux_Gamepad.h"
+
+
+namespace OVR { namespace Platform { namespace Linux {
+
+const char* pNameXbox360Wireless = "Xbox 360";
+const char* pNameXbox360Wired = "Microsoft X-Box 360";
+
+
+GamepadManager::GamepadManager() :
+ pDevice(NULL)
+{
+}
+
+GamepadManager::~GamepadManager()
+{
+ // if we have an open device, close it
+ if (pDevice)
+ {
+ pDevice->Close();
+ pDevice = NULL;
+ }
+}
+
+UInt32 GamepadManager::GetGamepadCount()
+{
+ return 1;
+}
+
+bool GamepadManager::GetGamepadState(UInt32 index, GamepadState *pState)
+{
+ if (!pDevice)
+ {
+ // get a list of paths to all the connected joystick devices
+ glob_t joystickGlobBuffer;
+ glob("/dev/input/js*", 0, NULL, &joystickGlobBuffer);
+
+ // open each joystick device, until we find one that will work for our needs
+ for (UInt32 i = 0; i < joystickGlobBuffer.gl_pathc; i++)
+ {
+ pDevice = new Gamepad();
+ if (pDevice->Open(joystickGlobBuffer.gl_pathv[i]))
+ {
+
+ if (pDevice->IsSupportedType())
+ {
+ break;
+ }
+ }
+
+ // we don't know why the device was not useable, make sure it gets closed cleanly
+ pDevice->Close();
+ pDevice = NULL;
+ }
+
+ }
+
+ if (pDevice)
+ {
+ // we have a device, so update it
+ pDevice->UpdateState();
+
+ // copy the device state into the struct param
+ memcpy(pState, pDevice->GetState(), sizeof(GamepadState));
+
+ // TODO: is the device still active/connected? if not, we should close it
+ // and clear pDevice, so that another device can take over
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+Gamepad::Gamepad() :
+ IsInitialized(false),
+ Name(String("Undefined")),
+ Type(UNDEFINED)
+{
+}
+
+Gamepad::~Gamepad()
+{
+ this->Close();
+}
+
+bool Gamepad::Open(const String& devicePathName)
+{
+ Name = "Undefined";
+ Type = UNDEFINED;
+
+ FileDescriptor = ::open(devicePathName.ToCStr(), O_RDONLY | O_NONBLOCK);
+ if (FileDescriptor == -1)
+ {
+ return false;
+ }
+
+ // get the device name
+ char name[128];
+ if (ioctl(FileDescriptor, JSIOCGNAME(sizeof(name)), name) < 0)
+ {
+ return false;
+ }
+
+ Name = name;
+
+ // see if device name matches one of our supported devices
+ static const UInt32 Wireless360Len = String(pNameXbox360Wireless).GetLength();
+ static const UInt32 Wired360Len = String(pNameXbox360Wired).GetLength();
+ if (Name.Substring(0, Wireless360Len) == pNameXbox360Wireless)
+ {
+ Type = XBOX360GAMEPADWIRELESS;
+ return true;
+ }
+ else if(Name.Substring(0, Wired360Len) == pNameXbox360Wired)
+ {
+ Type = XBOX360GAMEPADWIRED;
+ return true;
+ }
+
+ return false;
+}
+
+bool Gamepad::Close()
+{
+ IsInitialized = false;
+ Name = "Undefined";
+ Type = UNDEFINED;
+ return !::close(FileDescriptor);
+}
+
+void Gamepad::UpdateState()
+{
+ GamepadState *pState = &State;
+ js_event gamepadEvent;
+
+ // read the latest batch of events
+ while (read(FileDescriptor, &gamepadEvent, sizeof(struct js_event)) != -1)
+ {
+ switch (gamepadEvent.type)
+ {
+ case JS_EVENT_BUTTON:
+ IsInitialized = true;
+ SetStateButton(pState, gamepadEvent.number, gamepadEvent.value);
+ break;
+
+ case JS_EVENT_AXIS:
+ IsInitialized = true;
+ SetStateAxis(pState, gamepadEvent.number, gamepadEvent.value);
+ break;
+
+ case JS_EVENT_BUTTON | JS_EVENT_INIT:
+ if (IsInitialized) // skip the fake values during device event initialization
+ {
+ SetStateButton(pState, gamepadEvent.number, gamepadEvent.value);
+ }
+ break;
+
+ case JS_EVENT_AXIS | JS_EVENT_INIT:
+ if (IsInitialized) // skip the fake values during device event initialization
+ {
+ SetStateAxis(pState, gamepadEvent.number, gamepadEvent.value);
+ }
+ break;
+
+ default:
+ LogText("OVR::Linux::UpdateState unknown event type\n");
+ }
+ }
+}
+
+const GamepadState* Gamepad::GetState()
+{
+ return &State;
+}
+
+
+bool Gamepad::IsSupportedType()
+{
+ return Type != UNDEFINED;
+}
+
+const String& Gamepad::GetIdentifier()
+{
+ return Name;
+}
+
+static inline float NormalizeGamepadStickXbox360(SInt32 in)
+{
+ float v;
+ if (abs(in) < 9000) return 0;
+ else if (in > 9000) v = (float)in - 9000;
+ else v = (float)in + 9000;
+ return v / (32767 - 9000);
+}
+
+static inline float NormalizeGamepadTriggerXbox360(SInt32 in,
+ SInt32 offset,
+ SInt32 deadBand,
+ float divisor)
+{
+ in += offset;
+
+ if (in < deadBand)
+ {
+ return 0;
+ }
+ else
+ {
+ return float(in - deadBand) / divisor;
+ }
+}
+
+static inline void UpdateButtonMaskAndBitfield(GamepadState *pState,
+ SInt32 value,
+ UInt32 buttonBitfield)
+{
+ if (value)
+ {
+ pState->Buttons |= buttonBitfield;
+ }
+ else
+ {
+ pState->Buttons = pState->Buttons & (0xFFFFFFFF ^ buttonBitfield);
+ }
+}
+
+void Gamepad::SetStateAxis(GamepadState *pState, UInt32 axis, SInt32 value)
+{
+ // some pads/sticks have lots in common with one another,
+ // handle those shared cases first
+ switch (Type)
+ {
+ case XBOX360GAMEPADWIRELESS:
+ case XBOX360GAMEPADWIRED:
+ switch (axis)
+ {
+ case 0:
+ pState->LX = NormalizeGamepadStickXbox360(value);
+ break;
+
+ case 1:
+ pState->LY = -NormalizeGamepadStickXbox360(value);
+ break;
+
+ case 3:
+ pState->RX = NormalizeGamepadStickXbox360(value);
+ break;
+
+ case 4:
+ pState->RY = -NormalizeGamepadStickXbox360(value);
+ break;
+ }
+ break;
+
+ case UNDEFINED:
+ default:
+ break;
+ }
+
+ // handle the special cases, or pads/sticks which are unique
+ switch (Type)
+ {
+ case XBOX360GAMEPADWIRELESS:
+ switch (axis)
+ {
+ case 2:
+ pState->LT = NormalizeGamepadTriggerXbox360(value, 0, 500, 32267);
+ break;
+
+ case 5:
+ pState->RT = NormalizeGamepadTriggerXbox360(value, 0, 500, 32267);
+ break;
+ }
+ break;
+
+ case XBOX360GAMEPADWIRED:
+ switch (axis)
+ {
+ case 2:
+ pState->LT = NormalizeGamepadTriggerXbox360(value, 32767, 1000, 64535);
+ break;
+
+ case 5:
+ pState->RT = NormalizeGamepadTriggerXbox360(value, 32767, 1000, 64535);
+ break;
+
+ case 6:
+ if (value == 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 0, Gamepad_Left);
+ UpdateButtonMaskAndBitfield(pState, 0, Gamepad_Right);
+ }
+ else if (value < 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 1, Gamepad_Left);
+ }
+ else if (value > 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 1, Gamepad_Right);
+ }
+ break;
+
+ case 7:
+ if (value == 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 0, Gamepad_Up);
+ UpdateButtonMaskAndBitfield(pState, 0, Gamepad_Down);
+ }
+ else if (value < 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 1, Gamepad_Up);
+ }
+ else if (value > 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 1, Gamepad_Down);
+ }
+ break;
+ }
+ break;
+
+ case UNDEFINED:
+ default:
+ break;
+ }
+}
+
+void Gamepad::SetStateButton(GamepadState *pState, UInt32 button, SInt32 value)
+{
+ // some pads/sticks have lots in common with one another,
+ // handle those shared cases first
+ switch (Type)
+ {
+ case XBOX360GAMEPADWIRELESS:
+ case XBOX360GAMEPADWIRED:
+ switch (button)
+ {
+ case 0:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_A);
+ break;
+
+ case 1:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_B);
+ break;
+
+ case 2:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_X);
+ break;
+
+ case 3:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Y);
+ break;
+
+ case 4:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_L1);
+ break;
+
+ case 5:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_R1);
+ break;
+
+ case 6:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Back);
+ break;
+
+ case 7:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Start);
+ break;
+
+ case 8:
+ // we have no value defined for the Xbox/power button
+ break;
+
+ case 9:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_LStick);
+ break;
+
+ case 10:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_RStick);
+ break;
+ }
+ break;
+
+ case UNDEFINED:
+ default:
+ break;
+ }
+
+ // handle the special cases, or pads/sticks which are unique
+ switch (Type)
+ {
+ case XBOX360GAMEPADWIRELESS:
+ switch (button)
+ {
+ case 11:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Left);
+ break;
+
+ case 12:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Right);
+ break;
+
+ case 13:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Up);
+ break;
+
+ case 14:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Down);
+ break;
+ }
+
+ case XBOX360GAMEPADWIRED:
+ break;
+
+ case UNDEFINED:
+ default:
+ break;
+ }
+}
+
+}}} // OVR::Platform::Linux
+
diff --git a/Samples/CommonSrc/Platform/Linux_Gamepad.h b/Samples/CommonSrc/Platform/Linux_Gamepad.h
new file mode 100644
index 0000000..762d7a9
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Linux_Gamepad.h
@@ -0,0 +1,83 @@
+/************************************************************************************
+
+Filename : Linux_Gamepad.h
+Content : Linux implementation of Gamepad functionality.
+Created : May 6, 2013
+Authors : Lee Cooper, Simon Hallam
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Linux_Gamepad_h
+#define OVR_Linux_Gamepad_h
+
+#include "Gamepad.h"
+
+namespace OVR { namespace Platform { namespace Linux {
+
+class Gamepad; // forward declaration for readability
+
+class GamepadManager : public Platform::GamepadManager
+{
+public:
+
+ GamepadManager();
+ ~GamepadManager();
+
+ virtual UInt32 GetGamepadCount();
+ virtual bool GetGamepadState(UInt32 index, GamepadState *pState);
+
+private:
+
+ Gamepad *pDevice;
+};
+
+class Gamepad
+{
+public:
+
+ Gamepad();
+ virtual ~Gamepad();
+
+ bool Open(const String& devicePathName);
+ bool Close();
+ bool IsSupportedType();
+ const String& GetIdentifier();
+ void UpdateState();
+ const GamepadState* GetState();
+
+private:
+
+ void SetStateAxis(GamepadState *pState, UInt32 axis, SInt32 value);
+ void SetStateButton(GamepadState *pState, UInt32 button, SInt32 value);
+
+ enum GamepadType
+ {
+ UNDEFINED,
+ XBOX360GAMEPADWIRELESS,
+ XBOX360GAMEPADWIRED
+ };
+
+ UInt32 FileDescriptor;
+ bool IsInitialized;
+ String Name;
+ GamepadType Type;
+ GamepadState State;
+};
+
+}}}
+
+#endif // OVR_Linux_Gamepad_h
diff --git a/Samples/CommonSrc/Platform/Linux_Platform.cpp b/Samples/CommonSrc/Platform/Linux_Platform.cpp
new file mode 100644
index 0000000..bf84270
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Linux_Platform.cpp
@@ -0,0 +1,784 @@
+/************************************************************************************
+
+Filename : Platform_Linux.cpp
+Content : Linux (X11) implementation of Platform app infrastructure
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "Kernel/OVR_System.h"
+#include "Kernel/OVR_Array.h"
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_Timer.h"
+#include "OVR_CAPI_GL.h"
+
+#include "Linux_Platform.h"
+#include "Linux_Gamepad.h"
+
+// Renderers
+#include "../Render/Render_GL_Device.h"
+
+#include "../../3rdParty/EDID/edid.h"
+#include <X11/extensions/Xinerama.h>
+#include <X11/extensions/Xrandr.h>
+
+
+namespace OVR { namespace Platform { namespace Linux {
+
+static const char *AtomNames[] = {"WM_PROTOCOLS", "WM_DELETE_WINDOW"};
+
+PlatformCore::PlatformCore(Application* app)
+ : Platform::PlatformCore(app), Disp(NULL), Win(0), Vis(NULL), Quit(0), MMode(Mouse_Normal)
+ , StartVP(0, 0, 0, 0)
+{
+ pGamepadManager = *new Linux::GamepadManager();
+}
+PlatformCore::~PlatformCore()
+{
+ XFreeCursor(Disp, InvisibleCursor);
+
+ if (Disp)
+ XCloseDisplay(Disp);
+}
+
+// Setup an X11 window in windowed mode.
+bool PlatformCore::SetupWindow(int w, int h)
+{
+
+ if (!Disp)
+ {
+ XInitThreads();
+
+ Disp = XOpenDisplay(NULL);
+ if (!Disp)
+ {
+ OVR_DEBUG_LOG(("XOpenDisplay failed."));
+ return false;
+ }
+
+ XInternAtoms(Disp, const_cast<char**>(AtomNames), NumAtoms, false, Atoms);
+ }
+
+ XSetWindowAttributes winattr;
+ unsigned attrmask = CWEventMask | CWBorderPixel;
+
+ winattr.event_mask = ButtonPressMask|ButtonReleaseMask|KeyPressMask|KeyReleaseMask|ButtonMotionMask|PointerMotionMask|
+ /*PointerMotionHintMask|*/StructureNotifyMask;//|ExposureMask;
+ winattr.border_pixel = 0;
+
+ int screenNumber = DefaultScreen(Disp);
+
+ if (!Vis)
+ {
+ int attr[16];
+ int nattr = 2;
+
+ attr[0] = GLX_RGBA;
+ attr[1] = GLX_DOUBLEBUFFER;
+ attr[nattr++] = GLX_DEPTH_SIZE;
+ attr[nattr++] = 24;
+ attr[nattr] = 0;
+
+ Vis = glXChooseVisual(Disp, screenNumber, attr);
+
+ if (!Vis)
+ {
+ OVR_DEBUG_LOG(("glXChooseVisual failed."));
+ return false;
+ }
+ }
+
+ Window rootWindow = XRootWindow(Disp, Vis->screen);
+
+ winattr.colormap = XCreateColormap(Disp, rootWindow, Vis->visual, AllocNone);
+ attrmask |= CWColormap;
+
+
+ Win = XCreateWindow(Disp, rootWindow, 0, 0, w, h, 0, Vis->depth,
+ InputOutput, Vis->visual, attrmask, &winattr);
+
+ if (!Win)
+ {
+ OVR_DEBUG_LOG(("XCreateWindow failed."));
+ return false;
+ }
+
+
+ XStoreName(Disp, Win, "OVR App");
+ XSetWMProtocols(Disp, Win, &Atoms[WM_DELETE_WINDOW], 1);
+
+ // Intialize empty cursor for show/hide.
+ XColor black;
+ static char noData[] = { 0,0,0,0,0,0,0,0 };
+ black.red = black.green = black.blue = 0;
+
+ Pixmap bitmapNoData = XCreateBitmapFromData(Disp, Win, noData, 8, 8);
+ InvisibleCursor = XCreatePixmapCursor(Disp, bitmapNoData, bitmapNoData,
+ &black, &black, 0, 0);
+ XDefineCursor(Disp, Win, InvisibleCursor);
+
+ Width = w;
+ Height = h;
+
+ return true;
+}
+
+void PlatformCore::SetMouseMode(MouseMode mm)
+{
+ if (mm == MMode)
+ return;
+
+ if (Win)
+ {
+ if (mm == Mouse_Relative)
+ {
+ XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
+ }
+ else
+ {
+ //if (MMode == Mouse_Relative)
+ // ShowCursor(TRUE);
+ }
+ }
+ MMode = mm;
+}
+
+void PlatformCore::GetWindowSize(int* w, int* h) const
+{
+ *w = Width;
+ *h = Height;
+}
+
+void PlatformCore::SetWindowTitle(const char* title)
+{
+ XStoreName(Disp, Win, title);
+}
+
+void PlatformCore::ShowWindow(bool show)
+{
+ if (show)
+ XRaiseWindow(Disp, Win);
+ else
+ XIconifyWindow(Disp, Win, 0);
+}
+
+void PlatformCore::DestroyWindow()
+{
+ if (Win)
+ XDestroyWindow(Disp, Win);
+ Win = 0;
+}
+
+
+static int KeyMap[][2] =
+{
+ { XK_BackSpace, Key_Backspace },
+ { XK_Tab, Key_Tab },
+ { XK_Clear, Key_Clear },
+ { XK_Return, Key_Return },
+ { XK_Shift_L, Key_Shift },
+ { XK_Control_L, Key_Control },
+ { XK_Alt_L, Key_Alt },
+ { XK_Shift_R, Key_Shift },
+ { XK_Control_R, Key_Control },
+ { XK_Alt_R, Key_Alt },
+ { XK_Pause, Key_Pause },
+ { XK_Caps_Lock, Key_CapsLock },
+ { XK_Escape, Key_Escape },
+ { XK_space, Key_Space },
+ { XK_Page_Up, Key_PageUp },
+ { XK_Page_Down, Key_PageDown },
+ { XK_Prior, Key_PageUp },
+ { XK_Next, Key_PageDown },
+ { XK_End, Key_End },
+ { XK_Home, Key_Home },
+ { XK_Left, Key_Left },
+ { XK_Up, Key_Up },
+ { XK_Right, Key_Right },
+ { XK_Down, Key_Down },
+ { XK_Insert, Key_Insert },
+ { XK_Delete, Key_Delete },
+ { XK_Help, Key_Help },
+ { XK_Num_Lock, Key_NumLock },
+ { XK_Scroll_Lock, Key_ScrollLock },
+};
+
+
+static KeyCode MapXKToKeyCode(unsigned vk)
+{
+ unsigned key = Key_None;
+
+ if ((vk >= 'a') && (vk <= 'z'))
+ {
+ key = vk - 'a' + Key_A;
+ }
+ else if ((vk >= ' ') && (vk <= '~'))
+ {
+ key = vk;
+ }
+ else if ((vk >= XK_KP_0) && (vk <= XK_KP_9))
+ {
+ key = vk - XK_KP_0 + Key_KP_0;
+ }
+ else if ((vk >= XK_F1) && (vk <= XK_F15))
+ {
+ key = vk - XK_F1 + Key_F1;
+ }
+ else
+ {
+ for (unsigned i = 0; i< (sizeof(KeyMap) / sizeof(KeyMap[1])); i++)
+ {
+ if (vk == KeyMap[i][0])
+ {
+ key = KeyMap[i][1];
+ break;
+ }
+ }
+ }
+
+ return (KeyCode)key;
+}
+
+static int MapModifiers(int xmod)
+{
+ int mod = 0;
+ if (xmod & ShiftMask)
+ mod |= Mod_Shift;
+ if (xmod & ControlMask)
+ mod |= Mod_Control;
+ if (xmod & Mod1Mask)
+ mod |= Mod_Alt;
+ if (xmod & Mod4Mask)
+ mod |= Mod_Meta;
+ return mod;
+}
+
+void PlatformCore::processEvent(XEvent& event)
+{
+ switch (event.xany.type)
+ {
+ case ConfigureNotify:
+ if (event.xconfigure.width != Width || event.xconfigure.height != Height)
+ {
+ Width = event.xconfigure.width;
+ Height = event.xconfigure.height;
+ pApp->OnResize(Width, Height);
+
+ if (pRender)
+ pRender->SetWindowSize(Width, Height);
+ }
+ break;
+
+ case KeyPress:
+ case KeyRelease:
+ {
+ char chars[8] = {0};
+ KeySym xk;
+ XComposeStatus comp;
+ XLookupString(&event.xkey, chars, sizeof(chars), &xk, &comp);
+ if (xk != XK_VoidSymbol)
+ pApp->OnKey(MapXKToKeyCode((unsigned)xk), chars[0], event.xany.type == KeyPress, MapModifiers(event.xkey.state));
+ if (xk == XK_Escape && MMode == Mouse_Relative)
+ {
+ //ungrab
+ MMode = Mouse_RelativeEscaped;
+ showCursor(true);
+ }
+ }
+ break;
+
+ case MotionNotify:
+ if (MMode == Mouse_Relative)
+ {
+ int dx = event.xmotion.x - Width/2;
+ int dy = event.xmotion.y - Height/2;
+
+ // do not remove this check, WarpPointer generates events too.
+ if (dx == 0 && dy == 0)
+ break;
+
+ XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
+ pApp->OnMouseMove(dx, dy, Mod_MouseRelative|MapModifiers(event.xmotion.state));
+ }
+ else
+ {
+ pApp->OnMouseMove(event.xmotion.x, event.xmotion.y, MapModifiers(event.xmotion.state));
+ }
+ break;
+
+ case MapNotify:
+ if (MMode == Mouse_Relative)
+ {
+ XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
+ showCursor(false);
+ }
+ break;
+
+ case ButtonPress:
+ if (event.xbutton.button == 1)
+ {
+ //grab
+
+ if (MMode == Mouse_RelativeEscaped)
+ {
+ XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
+ showCursor(false);
+ MMode = Mouse_Relative;
+ }
+ }
+ break;
+
+ case FocusOut:
+ if (MMode == Mouse_Relative)
+ {
+ MMode = Mouse_RelativeEscaped;
+ showCursor(true);
+ }
+ break;
+
+ case ClientMessage:
+ if (event.xclient.message_type == Atoms[WM_PROTOCOLS] &&
+ Atom(event.xclient.data.l[0]) == Atoms[WM_DELETE_WINDOW])
+ pApp->OnQuitRequest();
+ break;
+ }
+}
+
+int PlatformCore::Run()
+{
+ while (!Quit)
+ {
+ if (XPending(Disp))
+ {
+ XEvent event;
+ XNextEvent(Disp, &event);
+
+ if (pApp && event.xany.window == Win)
+ processEvent(event);
+ }
+ else
+ {
+ pApp->OnIdle();
+ }
+ }
+
+ return ExitCode;
+}
+
+bool PlatformCore::determineScreenOffset(int screenId, int* screenOffsetX, int* screenOffsetY)
+{
+ Display* display = XOpenDisplay(NULL);
+
+ bool foundScreen = false;
+
+ if (display)
+ {
+ int numberOfScreens;
+ XineramaScreenInfo* screens = XineramaQueryScreens(display, &numberOfScreens);
+
+ if (screenId < numberOfScreens)
+ {
+ XineramaScreenInfo screenInfo = screens[screenId];
+ *screenOffsetX = screenInfo.x_org;
+ *screenOffsetY = screenInfo.y_org;
+
+ foundScreen = true;
+ }
+
+ XFree(screens);
+ }
+
+ return foundScreen;
+}
+
+void PlatformCore::showWindowDecorations(bool show)
+{
+ // Declaration of 'MOTIF_WM_HINTS' struct and flags can be found at:
+ // https://people.gnome.org/~tthurman/docs/metacity/xprops_8h-source.html
+ typedef struct WMHints
+ {
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+ long inputMode;
+ unsigned long status;
+ } Hints;
+
+ #define MWM_DECOR_ALL (1L << 0)
+ #define MWM_DECOR_BORDER (1L << 1)
+ #define MWM_DECOR_RESIZEH (1L << 2)
+ #define MWM_DECOR_TITLE (1L << 3)
+ #define MWM_DECOR_MENU (1L << 4)
+ #define MWM_DECOR_MINIMIZE (1L << 5)
+ #define MWM_DECOR_MAXIMIZE (1L << 6)
+
+ Atom property = XInternAtom(Disp, "_MOTIF_WM_HINTS", true);
+
+ Hints hints;
+ hints.flags = 2; // We only want to specify decoration.
+
+ if (show)
+ {
+ hints.decorations = MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE;
+ }
+ else
+ {
+ // Remove all window border items.
+ hints.decorations = 0;
+ }
+
+ XChangeProperty(Disp,Win,property,property,32,PropModeReplace,(unsigned char *)&hints,5);
+}
+
+int PlatformCore::IndexOf(Render::DisplayId id)
+{
+ RROutput primaryOutput = XRRGetOutputPrimary(Disp, DefaultRootWindow(Disp));
+ if (id.CgDisplayId == 0 && primaryOutput != 0)
+ return 0;
+
+ int index = (primaryOutput != 0) ? 0 : -1;
+ XRRScreenResources *screen = XRRGetScreenResources(Disp, Win);
+ for (int i = 0; i < screen->noutput && i <= id.CgDisplayId; ++i)
+ {
+ RROutput output = screen->outputs[i];
+ MonitorInfo * mi = read_edid_data(Disp, output);
+ if (mi != NULL && (primaryOutput == 0 || output != primaryOutput))
+ ++index;
+
+ delete mi;
+ }
+ XRRFreeScreenResources(screen);
+
+ return index;
+}
+
+bool PlatformCore::SetFullscreen(const Render::RendererParams& rp, int fullscreen)
+{
+ if (fullscreen == pRender->GetParams().Fullscreen)
+ return false;
+
+ int displayIndex = IndexOf(rp.Display);
+ OVR_DEBUG_LOG(("Display %d has index %d", rp.Display.CgDisplayId, displayIndex));
+
+ if (pRender->GetParams().Fullscreen == Render::Display_Window)
+ {
+ // Save the original window size and position so we can restore later.
+
+ XWindowAttributes xwa;
+ XGetWindowAttributes(Disp, Win, &xwa);
+ int x, y;
+ Window unused;
+ XTranslateCoordinates(Disp, Win, DefaultRootWindow(Disp), xwa.x, xwa.y, &x, &y, &unused);
+
+ StartVP.w = Width;
+ StartVP.h = Height;
+ StartVP.x = x;
+ StartVP.y = y;
+ }
+ else if (pRender->GetParams().Fullscreen == Render::Display_Fullscreen)
+ {
+// if (StartMode == NULL)
+// return true;
+
+// XF86VidModeSwitchToMode(Disp, 0, StartMode);
+// XF86VidModeSetViewPort(Disp, Win, 0, 0);
+ {
+ XEvent xev;
+ memset(&xev, 0, sizeof(xev));
+
+ xev.type = ClientMessage;
+ xev.xclient.window = Win;
+ xev.xclient.message_type = XInternAtom( Disp, "_NET_WM_STATE", False);
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 0;
+ xev.xclient.data.l[1] = XInternAtom( Disp, "_NET_WM_STATE_FULLSCREEN", False);
+ xev.xclient.data.l[2] = 0;
+
+ XSendEvent( Disp, DefaultRootWindow( Disp ), False, SubstructureNotifyMask, &xev);
+ }
+
+// XWindowAttributes windowattr;
+// XGetWindowAttributes(Disp, Win, &windowattr);
+// Width = windowattr.width;
+// Height = windowattr.height;
+// pApp->OnResize(Width, Height);
+
+// if (pRender)
+// pRender->SetWindowSize(Width, Height);
+
+ XWindowChanges wc;
+ wc.width = StartVP.w;
+ wc.height = StartVP.h;
+ wc.x = StartVP.x;
+ wc.y = StartVP.y;
+
+ XConfigureWindow(Disp, Win, CWWidth | CWHeight | CWX | CWY, &wc);
+
+ showWindowDecorations(false);
+ }
+
+ if (fullscreen == Render::Display_FakeFullscreen)
+ {
+ // Transitioning from windowed to fake fullscreen.
+ int xOffset;
+ int yOffset;
+
+ if (!determineScreenOffset(displayIndex, &xOffset, &yOffset))
+ {
+ return false;
+ }
+
+ showWindowDecorations(false);
+
+ XMoveWindow(Disp, Win, xOffset, yOffset);
+ XMapRaised(Disp, Win);
+ }
+ else if (fullscreen == Render::Display_Window)
+ {
+ // Transitioning from fake fullscreen to windowed.
+ showWindowDecorations(true);
+
+ XMoveWindow(Disp, Win, StartVP.x, StartVP.y);
+ XMapRaised(Disp, Win);
+ }
+ else if (fullscreen == Render::Display_Fullscreen)
+ {
+ // Move, size, and decorate the window for fullscreen.
+
+ int xOffset;
+ int yOffset;
+
+ if (!determineScreenOffset(displayIndex, &xOffset, &yOffset))
+ return false;
+
+ showWindowDecorations(false);
+
+ XWindowChanges wc;
+ wc.x = xOffset;
+ wc.y = yOffset;
+ wc.stack_mode = 0;
+
+ XConfigureWindow(Disp, Win, CWX | CWY | CWStackMode, &wc);
+
+ // Change the display mode.
+
+// XF86VidModeModeInfo **modes = NULL;
+// int modeNum = 0;
+// if (!XF86VidModeGetAllModeLines(Disp, 0, &modeNum, &modes))
+// return false;
+
+// OVR_ASSERT(modeNum > 0);
+// StartMode = modes[0];
+
+// int bestMode = -1;
+// for (int i = 0; i < modeNum; i++)
+// {
+// if ((modes[i]->hdisplay == Width) && (modes[i]->vdisplay == Height))
+// bestMode = i;
+// }
+
+// if (bestMode == -1)
+// return false;
+
+// XF86VidModeSwitchToMode(Disp, 0, modes[bestMode]);
+ //XF86VidModeSetViewPort(Disp, Win, 0, 0);
+
+ // Make the window fullscreen in the window manager.
+
+ {
+ XEvent xev;
+ memset(&xev, 0, sizeof(xev));
+
+ xev.type = ClientMessage;
+ xev.xclient.window = Win;
+ xev.xclient.message_type = XInternAtom( Disp, "_NET_WM_STATE", False);
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 1;
+ xev.xclient.data.l[1] = XInternAtom( Disp, "_NET_WM_STATE_FULLSCREEN", False);
+ xev.xclient.data.l[2] = 0;
+
+ XSendEvent( Disp, DefaultRootWindow( Disp ), False, SubstructureNotifyMask, &xev);
+ }
+ }
+
+ XMapRaised(Disp, Win);
+
+ Platform::PlatformCore::SetFullscreen(rp, fullscreen);
+ return true;
+}
+
+int PlatformCore::GetDisplayCount()
+{
+ int numberOfScreens = 0;
+ XineramaScreenInfo* screens = XineramaQueryScreens(Disp, &numberOfScreens);
+ XFree(screens);
+ return numberOfScreens;
+}
+
+Render::DisplayId PlatformCore::GetDisplay(int screenId)
+{
+ char device_id[32] = "";
+ int displayId = 0;
+
+ int screensPassed = 0;
+
+ RROutput primaryOutput = XRRGetOutputPrimary(Disp, DefaultRootWindow(Disp));
+
+ XRRScreenResources *screen = XRRGetScreenResources(Disp, Win);
+ for (int i = 0; i < screen->noutput; ++i)
+ {
+ RROutput output = screen->outputs[i];
+ MonitorInfo * mi = read_edid_data(Disp, output);
+ if (mi == NULL)
+ continue;
+
+ bool isMyScreen =
+ (primaryOutput == 0) ? screensPassed++ > screenId :
+ (output == primaryOutput) ? screenId == 0 :
+ ++screensPassed >= screenId;
+ if (isMyScreen)
+ {
+ OVR_sprintf(device_id, 32, "%s%04d", mi->manufacturer_code, mi->product_code);
+ displayId = (screenId == 0 && primaryOutput != 0) ? 0 : i; // Require 0 to return the primary display
+ delete mi;
+ break;
+ }
+
+ delete mi;
+ }
+ XRRFreeScreenResources(screen);
+
+ return Render::DisplayId(device_id, displayId);
+}
+
+RenderDevice* PlatformCore::SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc,
+ const char* type, const Render::RendererParams& rp)
+{
+ const SetupGraphicsDeviceSet* setupDesc = setupGraphicsDesc.PickSetupDevice(type);
+ OVR_ASSERT(setupDesc);
+
+ pRender = *setupDesc->pCreateDevice(rp, this);
+ if (pRender)
+ pRender->SetWindowSize(Width, Height);
+
+ return pRender.GetPtr();
+}
+
+void PlatformCore::showCursor(bool show)
+{
+ if (show)
+ {
+ XUndefineCursor(Disp, Win);
+ }
+ else
+ {
+ XDefineCursor(Disp, Win, InvisibleCursor);
+ }
+}
+
+}}
+
+// GL
+namespace Render { namespace GL { namespace Linux {
+
+ovrRenderAPIConfig RenderDevice::Get_ovrRenderAPIConfig() const
+{
+ static ovrGLConfig cfg;
+ cfg.OGL.Header.API = ovrRenderAPI_OpenGL;
+ cfg.OGL.Header.RTSize = Sizei(WindowWidth, WindowHeight);
+ cfg.OGL.Header.Multisample = Params.Multisample;
+ cfg.OGL.Win = Win;
+ cfg.OGL.Disp = Disp;
+ return cfg.Config;
+}
+
+Render::RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* oswnd)
+{
+ Platform::Linux::PlatformCore* PC = (Platform::Linux::PlatformCore*)oswnd;
+
+ GLXContext context = glXCreateContext(PC->Disp, PC->Vis, 0, GL_TRUE);
+
+ if (!context)
+ return NULL;
+
+ if (!glXMakeCurrent(PC->Disp, PC->Win, context))
+ {
+ glXDestroyContext(PC->Disp, context);
+ return NULL;
+ }
+
+ XMapRaised(PC->Disp, PC->Win);
+
+ InitGLExtensions();
+
+ XSync(PC->Disp, 0);
+
+ for (XWindowAttributes attribute; attribute.map_state != IsViewable; )
+ XGetWindowAttributes(PC->Disp,PC->Win,&attribute);
+
+ XSetInputFocus(PC->Disp, PC->Win, RevertToParent, CurrentTime);
+
+ return new Render::GL::Linux::RenderDevice(rp, PC->Disp, PC->Win, context);
+}
+
+void RenderDevice::Present(bool withVsync)
+{
+ unsigned swapInterval = (withVsync) ? 1 : 0;
+ GLuint currentSwapInterval = 0;
+ glXQueryDrawable(Disp, Win, GLX_SWAP_INTERVAL_EXT, &currentSwapInterval);
+ if (currentSwapInterval != swapInterval)
+ glXSwapIntervalEXT(Disp, Win, swapInterval);
+
+ glXSwapBuffers(Disp, Win);
+}
+
+void RenderDevice::Shutdown()
+{
+ if (Context)
+ {
+ glXMakeCurrent(Disp, 0, NULL);
+ glXDestroyContext(Disp, Context);
+ Context = NULL;
+ Win = 0;
+ }
+}
+
+}}}}
+
+
+int main(int argc, const char* argv[])
+{
+ using namespace OVR;
+ using namespace OVR::Platform;
+
+ // CreateApplication must be the first call since it does OVR::System::Initialize.
+ Application* app = Application::CreateApplication();
+ Linux::PlatformCore* platform = new Linux::PlatformCore(app);
+ // The platform attached to an app will be deleted by DestroyApplication.
+ app->SetPlatformCore(platform);
+
+ int exitCode = app->OnStartup(argc, argv);
+ if (!exitCode)
+ exitCode = platform->Run();
+
+ // No OVR functions involving memory are allowed after this.
+ Application::DestroyApplication(app);
+ app = 0;
+
+ return exitCode;
+}
diff --git a/Samples/CommonSrc/Platform/Linux_Platform.h b/Samples/CommonSrc/Platform/Linux_Platform.h
new file mode 100644
index 0000000..f2e438e
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Linux_Platform.h
@@ -0,0 +1,147 @@
+/************************************************************************************
+
+Filename : Platform_Linux.h
+Content : Linux (X11) implementation of Platform app infrastructure
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Platform_Linux_h
+#define OVR_Platform_Linux_h
+
+#include "Platform.h"
+#include "../Render/Render_GL_Device.h"
+
+#include <GL/glx.h>
+#include <X11/extensions/xf86vmode.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+namespace OVR { namespace Render {
+ class RenderDevice;
+}}
+
+namespace OVR { namespace Platform { namespace Linux {
+
+class PlatformCore : public Platform::PlatformCore
+{
+ XF86VidModeModeInfo* StartMode;
+ Recti StartVP;
+
+ int IndexOf(Render::DisplayId id);
+
+public:
+ Display* Disp;
+ XVisualInfo* Vis;
+ Window Win;
+
+ bool Quit;
+ int ExitCode;
+ int Width, Height;
+
+ MouseMode MMode;
+ Cursor InvisibleCursor;
+
+ enum
+ {
+ WM_PROTOCOLS,
+ WM_DELETE_WINDOW,
+ NumAtoms
+ };
+ Atom Atoms[NumAtoms];
+
+ void processEvent(XEvent& event);
+
+ Render::RenderDevice* SetupGraphics_GL(const Render::RendererParams& rp);
+
+ void showCursor(bool show);
+ bool determineScreenOffset(int screenId, int* screenOffsetX, int* screenOffsetY);
+ void showWindowDecorations(bool show);
+
+public:
+ PlatformCore(Application* app);
+ ~PlatformCore();
+
+ bool SetupWindow(int w, int h);
+ void Exit(int exitcode) { Quit = 1; ExitCode = exitcode; }
+
+ RenderDevice* SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc,
+ const char* gtype, const Render::RendererParams& rp);
+
+ void SetMouseMode(MouseMode mm);
+ void GetWindowSize(int* w, int* h) const;
+
+ void SetWindowTitle(const char*title);
+
+ void ShowWindow(bool show);
+ void DestroyWindow();
+ bool SetFullscreen(const Render::RendererParams& rp, int fullscreen);
+ int GetDisplayCount();
+ Render::DisplayId GetDisplay(int screen);
+
+ int Run();
+};
+
+}}
+namespace Render { namespace GL { namespace Linux {
+
+class RenderDevice : public Render::GL::RenderDevice
+{
+ Display* Disp;
+ Window Win;
+ GLXContext Context;
+
+public:
+ RenderDevice(const Render::RendererParams& p, Display* disp, Window w, GLXContext gl)
+ : GL::RenderDevice(p), Disp(disp), Win(w), Context(gl) {}
+
+ virtual void Shutdown();
+ virtual void Present(bool withVsync);
+ virtual ovrRenderAPIConfig Get_ovrRenderAPIConfig() const;
+
+ // oswnd = Linux::PlatformCore*
+ static Render::RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd);
+};
+
+}}}}
+
+
+// OVR_PLATFORM_APP_ARGS specifies the Application class to use for startup,
+// providing it with startup arguments.
+#define OVR_PLATFORM_APP_ARGS(AppClass, args) \
+ OVR::Platform::Application* OVR::Platform::Application::CreateApplication() \
+ { OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); \
+ return new AppClass args; } \
+ void OVR::Platform::Application::DestroyApplication(OVR::Platform::Application* app) \
+ { OVR::Platform::PlatformCore* platform = app->pPlatform; \
+ delete app; delete platform; OVR::System::Destroy(); };
+
+// OVR_PLATFORM_APP_ARGS specifies the Application startup class with no args.
+#define OVR_PLATFORM_APP(AppClass) OVR_PLATFORM_APP_ARGS(AppClass, ())
+
+#define OVR_PLATFORM_APP_ARGS_WITH_LOG(AppClass, LogClass, args) \
+ OVR::Platform::Application* OVR::Platform::Application::CreateApplication() \
+ { static LogClass log; OVR::System::Init(&log); \
+ return new AppClass args; } \
+ void OVR::Platform::Application::DestroyApplication(OVR::Platform::Application* app) \
+ { OVR::Platform::PlatformCore* platform = app->pPlatform; \
+ delete app; delete platform; OVR::System::Destroy(); };
+
+#define OVR_PLATFORM_APP_WITH_LOG(AppClass,LogClass) OVR_PLATFORM_APP_ARGS_WITH_LOG(AppClass,LogClass, ())
+
+#endif