ovr_sdk
view LibOVR/Src/Displays/OVR_Linux_SDKWindow.cpp @ 0:1b39a1b46319
initial 0.4.4
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Wed, 14 Jan 2015 06:51:16 +0200 |
parents | |
children |
line source
1 /*******************************************************************************
3 Filename : OVR_Linux_SDKWindow.cpp
4 Content : SDK generated Linux window.
5 Created : October 1, 2014
6 Authors : James Hughes
8 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
10 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
11 you may not use the Oculus VR Rift SDK except in compliance with the License,
12 which is provided at the time of installation or download, or which
13 otherwise accompanies this software in either electronic or hard copy form.
15 You may obtain a copy of the License at
17 http://www.oculusvr.com/licenses/LICENSE-3.2
19 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
25 *******************************************************************************/
27 #include "OVR_Linux_SDKWindow.h"
28 #include "../Kernel/OVR_Log.h"
29 #include "../Kernel/OVR_Log.h"
30 #include "../../../3rdParty/EDID/edid.h"
32 namespace OVR {
34 // Forward declarations
35 static Window constructWindow(struct _XDisplay* display, int xscreen,
36 XVisualInfo* xvisual,
37 const LinuxDeviceScreen& screen);
39 static XRRModeInfo* findModeByXID(XRRScreenResources* screen, RRMode xid)
40 {
41 for (int m = 0; m < screen->nmode; ++m)
42 {
43 XRRModeInfo* mode = &screen->modes[m];
44 if (xid == mode->id)
45 {
46 return mode;
47 }
48 }
49 return NULL;
50 }
52 /// Retrieves a list of available device screens on which we can build
53 /// SDK windows. Returns number of devices found.
54 /// screens Array which this function will populate.
55 /// maxNumScreens Maximum number of screens to store in screens.
56 static int getDeviceScreens(LinuxDeviceScreen* screens, int maxNumDevices)
57 {
58 struct _XDisplay* disp = XOpenDisplay(NULL);
59 if (!disp)
60 {
61 OVR::LogError("[SDKWindow] Unable to open X Display.");
62 return 0;
63 }
65 int numDevices = 0;
66 int numScreens = XScreenCount(disp);
67 for (int i = 0; i < numScreens; ++i)
68 {
69 // Screen root is used to detect what video output the crtc is using.
70 Window sr = XRootWindow(disp, i);
71 XRRScreenResources* screen = XRRGetScreenResources(disp, sr);
73 for (int ii = 0; ii < screen->ncrtc; ++ii)
74 {
75 XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(disp, screen, screen->crtcs[ii]);
77 if (0 == crtcInfo->noutput)
78 {
79 XRRFreeCrtcInfo(crtcInfo);
80 continue;
81 }
83 bool foundOutput = false;
84 RROutput output = crtcInfo->outputs[0];
85 for (int k = 0; k < crtcInfo->noutput; ++k)
86 {
87 XRROutputInfo* outputInfo =
88 XRRGetOutputInfo(disp, screen, crtcInfo->outputs[k]);
89 for (int kk = 0 ; kk < outputInfo->nmode; ++kk)
90 {
91 if (outputInfo->modes[kk] == crtcInfo->mode)
92 {
93 output = crtcInfo->outputs[k];
94 foundOutput = true;
95 break;
96 }
97 }
98 XRRFreeOutputInfo(outputInfo);
99 if (foundOutput) { break; }
100 }
102 if (!foundOutput)
103 {
104 XRRFreeCrtcInfo(crtcInfo);
105 continue;
106 }
108 XRROutputInfo* outputInfo = XRRGetOutputInfo(disp, screen, output);
109 if (RR_Connected != outputInfo->connection)
110 {
111 XRRFreeOutputInfo(outputInfo);
112 XRRFreeCrtcInfo(crtcInfo);
113 continue;
114 }
116 // Read EDID associated with crtc.
117 MonitorInfo* mi = read_edid_data(disp, output);
118 if (mi == NULL)
119 {
120 XRRFreeOutputInfo(outputInfo);
121 XRRFreeCrtcInfo(crtcInfo);
122 continue;
123 }
125 if (strcmp(mi->manufacturer_code, "OVR") == 0)
126 {
127 XRRModeInfo* modeInfo = findModeByXID(screen, crtcInfo->mode);
129 DistortionRotation desiredRot = DistRotateNone;
130 if (mi->product_code == 3)
131 {
132 // This is a DK2, we may have to rotate our output.
133 // If we don't have to, we should alert the user that
134 // rotating the display using the DM or graphics
135 // card settings is highly non-optimal.
136 desiredRot = DistRotateCCW90;
137 if (crtcInfo->rotation != RR_Rotate_0)
138 {
139 OVR::LogError("Please do not rotate your rift's screen.");
141 if (crtcInfo->rotation == RR_Rotate_90)
142 {
143 // The user has manually rotated the screen.
144 // So apply no rotation on our end.
145 desiredRot = DistRotateNone;
146 }
147 }
148 }
149 else
150 {
151 if (crtcInfo->rotation != RR_Rotate_0)
152 {
153 OVR::LogError("Please do not rotate your rift's screen.");
154 }
155 }
157 int width = modeInfo->width;
158 int height = modeInfo->height;
160 // Swap width / height if display is rotated (shouldn't be on linux).
161 if ( crtcInfo->rotation == RR_Rotate_90
162 || crtcInfo->rotation == RR_Rotate_270)
163 {
164 width = modeInfo->height;
165 height = modeInfo->width;
166 }
168 // Push detected monitor.
169 screens[numDevices].set(i, screen->crtcs[ii], desiredRot,
170 mi->product_code, width, height,
171 crtcInfo->x, crtcInfo->y);
172 ++numDevices;
173 }
175 delete mi;
177 if (numDevices == maxNumDevices)
178 {
179 XRRFreeOutputInfo(outputInfo);
180 XRRFreeCrtcInfo(crtcInfo);
181 XRRFreeScreenResources(screen);
182 OVR::LogError("[SDKWindow] Maxed out number of devices..");
183 XCloseDisplay(disp);
184 return numDevices;
185 }
187 XRRFreeOutputInfo(outputInfo);
188 XRRFreeCrtcInfo(crtcInfo);
189 }
191 XRRFreeScreenResources(screen);
192 }
193 XCloseDisplay(disp);
194 return numDevices;
195 }
198 LinuxDeviceScreen SDKWindow::findDevScreenForHMD(const ovrHmd& hmd)
199 {
200 return findDevScreenForDevID(hmd->DisplayDeviceName);
201 }
203 LinuxDeviceScreen SDKWindow::findDevScreenForDevID(const char* deviceIDIn)
204 {
205 const int maxNumDevices = 5;
206 LinuxDeviceScreen screens[maxNumDevices];
207 int numDevices = getDeviceScreens(screens, maxNumDevices);
209 if (numDevices > 0)
210 {
211 // Identify target for SDK window via hmd info.
212 for (int i = 0; i < numDevices; ++i)
213 {
214 LinuxDeviceScreen& screen = screens[i];
216 char deviceID[32];
217 OVR_sprintf(deviceID, 32, "OVR%04d-%d",
218 screen.productCode, screen.crtcid);
220 if (strcmp(deviceIDIn, deviceID) == 0)
221 {
222 return screen;
223 }
224 }
225 }
227 return LinuxDeviceScreen();
228 }
230 DistortionRotation SDKWindow::getRotation(const ovrHmd& hmd)
231 {
232 LinuxDeviceScreen screen = findDevScreenForHMD(hmd);
233 if (screen.isValid())
234 {
235 return screen.rotation;
236 }
237 else
238 {
239 return DistRotateNone;
240 }
241 }
244 bool SDKWindow::getVisualFromDrawable(GLXDrawable drawable, XVisualInfo* vinfoOut)
245 {
246 struct _XDisplay* display = glXGetCurrentDisplay();
248 unsigned int value;
249 glXQueryDrawable(display, drawable, GLX_FBCONFIG_ID, &value);
250 const int attribs[] = {GLX_FBCONFIG_ID, (int)value, None};
251 int screen;
252 glXQueryContext(display, glXGetCurrentContext(), GLX_SCREEN, &screen);
253 int numElems;
254 GLXFBConfig* config = glXChooseFBConfig(display, screen, attribs, &numElems);
255 if (numElems > 0)
256 {
257 XVisualInfo* chosen = glXGetVisualFromFBConfig(display, *config);
258 *vinfoOut = *chosen;
259 XFree(config);
260 return true;
261 }
262 return false;
263 }
265 SDKWindow::SDKWindow(const ovrHmd& hmd) :
266 mXDisplay(NULL),
267 mXScreen(-1),
268 mXVisual(NULL),
269 mXUniqueContext(-1),
270 mXWindow(0),
271 mFBConfigID(0)
272 {
273 OVR_UNUSED(hmd);
274 }
276 SDKWindow::~SDKWindow()
277 {
278 if (mXWindow)
279 {
280 XDeleteContext(mXDisplay, mXWindow, mXUniqueContext);
281 XUnmapWindow(mXDisplay, mXWindow);
282 XDestroyWindow(mXDisplay, mXWindow);
283 mXWindow = static_cast<Window>(0);
284 }
286 if (mXVisual)
287 {
288 XFree(mXVisual);
289 mXVisual = NULL;
290 }
292 if (mXDisplay)
293 {
294 XCloseDisplay(mXDisplay);
295 }
296 }
298 void SDKWindow::buildVisualAndWindow(const LinuxDeviceScreen& devScreen)
299 {
300 mXDisplay = XOpenDisplay(NULL);
301 mXUniqueContext = XUniqueContext();
302 mXScreen = devScreen.screen;
303 mFBConfigID = chooseFBConfigID(mXDisplay, mXScreen);
305 mXVisual = getVisual(mXDisplay, mFBConfigID, mXScreen);
306 if (mXVisual != NULL)
307 {
308 mXWindow = constructWindow(mXDisplay, mXScreen, mXVisual, devScreen);
309 mDeviceScreen = devScreen;
310 }
311 }
313 // Used in chooseVisual. May need to expose this to the end use so they can
314 // choose an appropriate framebuffer configuration.
315 struct FBConfig
316 {
317 FBConfig() :
318 redBits(8),
319 greenBits(8),
320 blueBits(8),
321 alphaBits(8),
322 depthBits(8),
323 stencilBits(-1),
324 doubleBuffer(true),
325 auxBuffers(-1)
326 {}
328 int redBits;
329 int greenBits;
330 int blueBits;
331 int alphaBits;
332 int depthBits;
333 int stencilBits;
334 bool doubleBuffer;
335 int auxBuffers;
337 int xcfg;
338 };
340 static int fbCalcContrib(int desired, int current)
341 {
342 int diff = desired - current;
343 if (current != -1) { return diff * diff; }
344 else { return 0; }
345 }
347 // Choose frame buffer configuration and return fbConfigID.
348 int SDKWindow::chooseFBConfigID(struct _XDisplay* display, int xscreen)
349 {
350 int nativeCount = 0;
351 GLXFBConfig* nativeConfigs =
352 glXGetFBConfigs(display, xscreen, &nativeCount);
353 if (!nativeCount)
354 {
355 OVR::LogError("[SDKWindow] No valid frame buffer configurations found.");
356 return 0;
357 }
359 FBConfig* usables = static_cast<FBConfig*>(calloc(nativeCount, sizeof(FBConfig)));
360 int numUsables = 0;
362 for (int i = 0; i < nativeCount; ++i)
363 {
364 GLXFBConfig native = nativeConfigs[i];
365 FBConfig* usable = &usables[numUsables];
366 int v = 0;
368 // Only frame buffer configcs with attached visuals.
369 glXGetFBConfigAttrib(display, native, GLX_VISUAL_ID, &v);
370 if (!v) { continue; }
372 // Only RGBA frame buffers.
373 glXGetFBConfigAttrib(display, native, GLX_RENDER_TYPE, &v);
374 if (!(v & GLX_RGBA_BIT)) { continue; }
376 glXGetFBConfigAttrib(display, native, GLX_DRAWABLE_TYPE, &v);
377 if (!(v & GLX_WINDOW_BIT)) { continue; }
379 glXGetFBConfigAttrib(display, native, GLX_DEPTH_SIZE, &usable->depthBits);
380 glXGetFBConfigAttrib(display, native, GLX_STENCIL_SIZE, &usable->stencilBits);
382 glXGetFBConfigAttrib(display, native, GLX_RED_SIZE, &usable->redBits);
383 glXGetFBConfigAttrib(display, native, GLX_GREEN_SIZE, &usable->greenBits);
384 glXGetFBConfigAttrib(display, native, GLX_BLUE_SIZE, &usable->blueBits);
385 glXGetFBConfigAttrib(display, native, GLX_ALPHA_SIZE, &usable->alphaBits);
387 glXGetFBConfigAttrib(display, native, GLX_ALPHA_SIZE, &usable->auxBuffers);
389 glXGetFBConfigAttrib(display, native, GLX_DOUBLEBUFFER, &v);
390 usable->doubleBuffer = v ? true : false;
392 glXGetFBConfigAttrib(display, native, GLX_FBCONFIG_ID, &usable->xcfg);
394 ++numUsables;
395 }
397 // We really want std::numeric_limits<int>::max() instead of hardcoded vals.
398 const int MostMissing = 100;
399 int leastMissing = MostMissing;
400 int leastBias = MostMissing;
402 const FBConfig* closest = NULL;
404 // Desired is currently the default config built by constructor.
405 FBConfig desired;
407 for (int i = 0; i < numUsables; ++i)
408 {
409 const FBConfig* cur = &usables[i];
411 if (desired.doubleBuffer != cur->doubleBuffer) { continue; }
413 int missing = 0;
414 if (desired.alphaBits > 0 && cur->alphaBits == 0) { ++missing; }
415 if (desired.depthBits > 0 && cur->depthBits == 0) { ++missing; }
416 if (desired.stencilBits > 0 && cur->stencilBits == 0) { ++missing; }
417 if (desired.redBits > 0 && desired.redBits != cur->redBits) { ++missing; }
418 if (desired.greenBits > 0 && desired.greenBits != cur->greenBits) { ++missing; }
419 if (desired.blueBits > 0 && desired.blueBits != cur->blueBits) { ++missing; }
421 int bias = fbCalcContrib(desired.redBits, cur->redBits)
422 + fbCalcContrib(desired.greenBits, cur->greenBits)
423 + fbCalcContrib(desired.blueBits, cur->blueBits)
424 + fbCalcContrib(desired.alphaBits, cur->alphaBits)
425 + fbCalcContrib(desired.depthBits, cur->depthBits)
426 + fbCalcContrib(desired.stencilBits, cur->stencilBits);
428 if (missing < leastMissing)
429 {
430 closest = cur;
431 }
432 else if (missing == leastMissing)
433 {
434 // Now select against squared differences.
435 if (bias < leastBias)
436 {
437 closest = cur;
438 }
439 }
441 if (closest == cur)
442 {
443 leastMissing = missing;
444 leastBias = bias;
445 }
446 }
448 if (closest == NULL)
449 {
450 OVR::LogError("[SDKWindow] Failed to select appropriate frame buffer.");
451 XFree(nativeConfigs);
452 free(usables);
453 return 0;
454 }
456 int retVal = closest->xcfg;
458 XFree(nativeConfigs);
459 free(usables);
461 return retVal;
462 }
464 // Obtain visual from frame buffer configuration ID.
465 XVisualInfo* SDKWindow::getVisual(struct _XDisplay* display,
466 int fbConfigID, int xscreen)
467 {
468 GLXFBConfig* cfg = getGLXFBConfig(display, fbConfigID, xscreen);
469 XVisualInfo* viOut = NULL;
470 if (cfg != NULL)
471 {
472 viOut = glXGetVisualFromFBConfig(display, *cfg);
473 XFree(cfg);
474 cfg = NULL;
475 }
476 else
477 {
478 OVR::LogError("Unable to find fb config ID.");
479 }
480 return viOut;
481 }
483 // GLXFBConfig pointer from frame buffer configuration ID. You must call
484 // XFree on the GLXFBConfig pointer.
485 GLXFBConfig* SDKWindow::getGLXFBConfig(struct _XDisplay* display,
486 int fbConfigID, int xscreen)
487 {
488 const int attribs[] = {GLX_FBCONFIG_ID, (int)fbConfigID, None};
489 int numElems;
491 GLXFBConfig* config = glXChooseFBConfig(display, xscreen, attribs, &numElems);
492 if (numElems > 0)
493 {
494 return config;
495 }
496 else
497 {
498 return NULL;
499 }
500 }
503 static int gXLastError = -1;
504 static int handleXError(struct _XDisplay* display, XErrorEvent* event)
505 {
506 OVR_UNUSED(display);
507 gXLastError = event->error_code;
508 return 0;
509 }
511 static void obtainXErrorHandler()
512 {
513 gXLastError = Success;
514 XSetErrorHandler(handleXError);
515 }
517 static void releaseXErrorHandler(struct _XDisplay* display)
518 {
519 XSync(display, False);
520 XSetErrorHandler(NULL);
521 }
523 // Returns 0 on error, otherwise a valid X window is returned.
524 static Window constructWindow(struct _XDisplay* xDisp, int xScreen,
525 XVisualInfo* xVisual,
526 const LinuxDeviceScreen& devScreen)
527 {
528 XSetWindowAttributes wa;
530 Window root = XRootWindow(xDisp, xScreen);
531 Window xWindowOut = 0;
533 // Create Window
534 {
535 Colormap xWinColorMapOut = XCreateColormap(
536 xDisp, root, xVisual->visual, AllocNone);
537 unsigned long wamask = CWBorderPixel | CWColormap | CWEventMask;
539 wa.colormap = xWinColorMapOut;
540 wa.border_pixel = 0;
541 wa.event_mask = StructureNotifyMask | ExposureMask | FocusChangeMask
542 | VisibilityChangeMask | EnterWindowMask | LeaveWindowMask
543 | PropertyChangeMask;
545 obtainXErrorHandler();
547 xWindowOut = XCreateWindow(xDisp, root,
548 0, 0,
549 devScreen.width, devScreen.height,
550 0,
551 xVisual->depth,
552 InputOutput,
553 xVisual->visual,
554 wamask,
555 &wa);
557 releaseXErrorHandler(xDisp);
559 if (!xWindowOut)
560 {
561 OVR::LogError("[SDKWindow] Failed to create SDK window.");
562 return 0;
563 }
565 XFreeColormap(xDisp, xWinColorMapOut);
566 }
568 // OVERRIDE REDIRECT.
569 XSetWindowAttributes attributes;
570 attributes.override_redirect = True;
571 XChangeWindowAttributes(xDisp, xWindowOut,
572 CWOverrideRedirect, &attributes);
574 // Show the window (do this in full screen or windowed).
575 XMapRaised(xDisp, xWindowOut);
576 XFlush(xDisp);
578 // Position ourselves manually since there should be no WM managing us.
579 XRaiseWindow(xDisp, xWindowOut);
580 XMoveWindow(xDisp, xWindowOut, devScreen.offsetX, devScreen.offsetY);
581 XResizeWindow(xDisp, xWindowOut, devScreen.width, devScreen.height);
583 XFlush(xDisp);
585 // WM Backup in case there still exists a WM managing us...
586 Atom NET_WM_BYPASS_COMPOSITOR =
587 XInternAtom(xDisp, "_NET_WM_BYPASS_COMPOSITOR", False);
588 Atom NET_WM_STATE =
589 XInternAtom(xDisp, "_NET_WM_STATE", False);
590 Atom NET_WM_STATE_FULLSCREEN =
591 XInternAtom(xDisp, "_NET_WM_STATE_FULLSCREEN", False);
592 Atom NET_ACTIVE_WINDOW =
593 XInternAtom(xDisp, "_NET_ACTIVE_WINDOW", False);
595 // Bypass compositor if we are under a compositing WM.
596 // Just in case a WM ignores our override_redirect.
597 if (NET_WM_BYPASS_COMPOSITOR)
598 {
599 const unsigned long bypass = 1;
600 XChangeProperty(xDisp, xWindowOut,
601 NET_WM_BYPASS_COMPOSITOR,
602 XA_CARDINAL, 32, PropModeReplace,
603 (unsigned char*)&bypass, 1);
604 }
606 if (NET_WM_STATE && NET_WM_STATE_FULLSCREEN)
607 {
608 // BACKUP: If we are still managed by a WM we want fullscreen.
609 const int EWMH_STATE_ADD = 1;
611 if (NET_ACTIVE_WINDOW)
612 {
613 XEvent event;
614 memset(&event, 0, sizeof(event));
616 event.type = ClientMessage;
617 event.xclient.window = xWindowOut;
618 event.xclient.format = 32;
619 event.xclient.message_type = NET_ACTIVE_WINDOW;
620 event.xclient.data.l[0] = 1;
621 event.xclient.data.l[1] = 0;
623 XSendEvent(xDisp, root, False,
624 SubstructureNotifyMask | SubstructureRedirectMask,
625 &event);
626 }
628 XEvent event;
629 memset(&event, 0, sizeof(event));
631 event.type = ClientMessage;
632 event.xclient.window = xWindowOut;
633 event.xclient.format = 32;
634 event.xclient.message_type = NET_WM_STATE;
635 event.xclient.data.l[0] = EWMH_STATE_ADD;
636 event.xclient.data.l[1] = NET_WM_STATE_FULLSCREEN;
637 event.xclient.data.l[2] = 0;
638 event.xclient.data.l[3] = 1;
640 XSendEvent(xDisp, root, False,
641 SubstructureNotifyMask | SubstructureRedirectMask,
642 &event);
643 }
645 return xWindowOut;
646 }
648 } // namespace OVR