ovr_sdk

view LibOVR/Src/Displays/OVR_Linux_Display.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_Display.cpp
4 Content : Linux-specific Display declarations
5 Created : July 2, 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_Display.h"
28 #include "../Kernel/OVR_Log.h"
30 #include "../../../3rdParty/EDID/edid.h"
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/extensions/Xrandr.h>
35 #include <X11/Xatom.h>
37 //-------------------------------------------------------------------------------------
38 // ***** Display enumeration Helpers
40 namespace OVR {
42 static const uint8_t edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
43 0xff, 0xff, 0xff, 0x00 };
45 static const uint8_t edid_v1_descriptor_flag[] = { 0x00, 0x00 };
47 static const int DESCRIPTOR_DATA = 5;
48 static const int UNKNOWN_DESCRIPTOR = -1;
49 static const int DETAILED_TIMING_BLOCK = -2;
51 // The following three functions were pulled from OVR_Linux_Display.cpp
52 // and modified slightly.
53 static int blockType(uint8_t* block)
54 {
55 if (!strncmp((const char*)edid_v1_descriptor_flag, (const char*)block, 2))
56 {
57 if (block[2] != 0)
58 {
59 return UNKNOWN_DESCRIPTOR;
60 }
61 else
62 {
63 return block[3];
64 }
65 }
66 else
67 {
68 return DETAILED_TIMING_BLOCK;
69 }
70 }
72 static char* getMonitorName(const uint8_t* block)
73 {
74 static char name[13];
75 uint8_t const* ptr = block + DESCRIPTOR_DATA;
77 for (int i = 0; i < 13; i++, ptr++)
78 {
79 if (*ptr == 0xa)
80 {
81 name[i] = 0;
82 return name;
83 }
85 name[i] = *ptr;
86 }
88 return name;
89 }
91 // Returns -1 on failure, 0 otherwise.
92 static int parseEdid(uint8_t* edid, Linux::DisplayEDID& edidResult)
93 {
94 const int EDID_LENGTH = 0x80;
95 const int EDID_HEADER = 0x00;
96 const int EDID_HEADER_END = 0x07;
98 // const int EDID_STRUCT_VERSION = 0x12;
99 // const int EDID_STRUCT_REVISION = 0x13;
101 const int MONITOR_NAME = 0xfc;
102 // const int MONITOR_LIMITS = 0xfd;
103 const int MONITOR_SERIAL = 0xff;
105 // const int ESTABLISHED_TIMING_1 = 0x23;
106 // const int ESTABLISHED_TIMING_2 = 0x24;
107 // const int MANUFACTURERS_TIMINGS = 0x25;
109 const int DETAILED_TIMING_DESCRIPTIONS_START = 0x36;
110 const int DETAILED_TIMING_DESCRIPTION_SIZE = 18;
111 const int NO_DETAILED_TIMING_DESCRIPTIONS = 4;
113 // const int DETAILED_TIMING_DESCRIPTION_1 = 0x36;
114 // const int DETAILED_TIMING_DESCRIPTION_2 = 0x48;
115 // const int DETAILED_TIMING_DESCRIPTION_3 = 0x5a;
116 // const int DETAILED_TIMING_DESCRIPTION_4 = 0x6c;
118 const char* monitorName = "Unknown";
119 uint8_t* block = NULL;
120 uint8_t checksum = 0;
122 for (int i = 0; i < EDID_LENGTH; i++)
123 {
124 checksum += edid[i];
125 }
127 // Bad checksum, fail EDID
128 if (checksum != 0)
129 {
130 return -1;
131 }
133 if (strncmp((const char*)edid + EDID_HEADER,
134 (const char*)edid_v1_header, EDID_HEADER_END + 1))
135 {
136 // First bytes don't match EDID version 1 header
137 return -1;
138 }
140 // Monitor name and timings
141 char serialNumber[14];
142 memset(serialNumber, 0, 14);
144 block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
146 for (int i = 0; i < NO_DETAILED_TIMING_DESCRIPTIONS;
147 i++, block += DETAILED_TIMING_DESCRIPTION_SIZE)
148 {
150 if (blockType(block) == MONITOR_NAME)
151 {
152 monitorName = getMonitorName(block);
153 }
155 if (blockType(block) == MONITOR_SERIAL)
156 {
157 memcpy(serialNumber, block + 5, 13);
158 break;
159 }
160 }
162 uint8_t vendorString[4] = {0};
164 vendorString[0] = (edid[8] >> 2 & 31) + 64;
165 vendorString[1] = (((edid[8] & 3) << 3) | (edid[9] >> 5)) + 64;
166 vendorString[2] = (edid[9] & 31) + 64;
168 edidResult.ModelNumber = *(uint16_t*)&edid[10];
169 edidResult.MonitorName = monitorName;
170 edidResult.VendorName = reinterpret_cast<const char*>(vendorString);
171 edidResult.SerialNumber = serialNumber;
173 // FIXME: Get timings as well
175 // std::cout << "# EDID version " << static_cast<int>(edid[EDID_STRUCT_VERSION])
176 // << " revision " << static_cast<int>(edid[EDID_STRUCT_REVISION])
177 // << std::endl;
179 return 0;
180 }
183 // Returns -1 in the case of failure, 0 otherwise.
184 // Parameters:
185 // data OUT This pointer is modified to point to the output from
186 // XRRGetOutputProperty. You *must* call XFree on this pointer.
187 // dataLen OUT The length of the data returned in 'data'.
188 static int getXRRProperty(struct _XDisplay* display, RROutput output, Atom atom,
189 uint8_t** data, int* dataLen)
190 {
191 unsigned long nitems;
192 unsigned long bytesAfter;
193 int actualFormat;
194 Atom actualType;
196 int ret = XRRGetOutputProperty(display, output, atom, 0, 100,
197 False, False, AnyPropertyType,
198 &actualType, &actualFormat, &nitems,
199 &bytesAfter, data);
201 if (None != ret)
202 {
203 *dataLen = nitems;
204 return 0;
205 }
206 else
207 {
208 return -1;
209 }
210 }
212 static XRRModeInfo* findModeByXID(XRRScreenResources* screen, RRMode xid)
213 {
214 for (int m = 0; m < screen->nmode; ++m)
215 {
216 XRRModeInfo* mode = &screen->modes[m];
217 if (xid == mode->id)
218 {
219 return mode;
220 }
221 }
222 return NULL;
223 }
225 static int discoverExtendedRifts(OVR::Linux::DisplayDesc* descriptorArray, int inputArraySize, bool /*edidInfo*/)
226 {
227 int result = 0;
229 struct _XDisplay* display = XOpenDisplay(NULL);
231 if (display == NULL)
232 {
233 OVR::LogError("[Linux Display] Unable to open X Display!");
234 return 0;
235 }
237 Atom EDIDAtom = XInternAtom(display, RR_PROPERTY_RANDR_EDID, False);
238 int numScreens = XScreenCount(display);
239 for (int i = 0; i < numScreens; ++i)
240 {
241 Window sr = XRootWindow(display, i);
242 XRRScreenResources* screen = XRRGetScreenResources(display, sr);
244 for (int ii = 0; ii < screen->ncrtc; ++ii)
245 {
246 XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screen, screen->crtcs[ii]);
248 if (0 == crtcInfo->noutput)
249 {
250 XRRFreeCrtcInfo(crtcInfo);
251 continue;
252 }
254 bool foundOutput = false;
255 RROutput output = crtcInfo->outputs[0];
256 for (int k = 0; k < crtcInfo->noutput; ++k)
257 {
258 XRROutputInfo* outputInfo =
259 XRRGetOutputInfo(display, screen, crtcInfo->outputs[k]);
261 for (int kk = 0; kk < outputInfo->nmode; ++kk)
262 {
263 if (outputInfo->modes[kk] == crtcInfo->mode)
264 {
265 output = crtcInfo->outputs[k];
266 foundOutput = true;
267 break;
268 }
269 }
270 XRRFreeOutputInfo(outputInfo);
271 if (foundOutput)
272 {
273 break;
274 }
275 }
277 if (!foundOutput)
278 {
279 XRRFreeCrtcInfo(crtcInfo);
280 continue;
281 }
283 XRROutputInfo* outputInfo = XRRGetOutputInfo(display, screen, output);
284 if (RR_Connected != outputInfo->connection)
285 {
286 XRRFreeOutputInfo(outputInfo);
287 XRRFreeCrtcInfo(crtcInfo);
288 continue;
289 }
291 // Read EDID associated with crtc.
292 uint8_t* data = NULL;
293 int dataLen = 0;
294 if (getXRRProperty(display, output, EDIDAtom, &data, &dataLen) != 0)
295 {
296 // Identify rifts based on EDID.
297 Linux::DisplayEDID edid;
298 parseEdid(data, edid);
299 XFree(data);
300 data = NULL;
302 // TODO: Remove either this 3rdParty call to read EDID data
303 // or remove our own parsing of the EDID. Probably opt
304 // to remove our parsing.
305 MonitorInfo* mi = read_edid_data(display, output);
306 if (mi == NULL)
307 {
308 XRRFreeOutputInfo(outputInfo);
309 XRRFreeCrtcInfo(crtcInfo);
310 continue;
311 }
313 if (edid.VendorName == "OVR")
314 {
315 if( result >= inputArraySize )
316 {
317 delete mi;
318 XRRFreeOutputInfo(outputInfo);
319 XRRFreeCrtcInfo(crtcInfo);
320 return result;
321 }
323 XRRModeInfo* modeInfo = findModeByXID(screen, crtcInfo->mode);
325 int width = modeInfo->width;
326 int height = modeInfo->height;
328 if ( crtcInfo->rotation == RR_Rotate_90
329 || crtcInfo->rotation == RR_Rotate_270 )
330 {
331 width = modeInfo->height;
332 height = modeInfo->width;
333 }
335 int x = crtcInfo->x;
336 int y = crtcInfo->y;
338 // Generate a device ID string similar Windows does it
339 char device_id[32];
340 OVR_sprintf(device_id, 32, "%s%04d-%d",
341 mi->manufacturer_code, mi->product_code,
342 screen->crtcs[ii]);
344 OVR::Linux::DisplayDesc& desc = descriptorArray[result++];
345 desc.DisplayID = device_id;
346 desc.ModelName = edid.MonitorName;
347 desc.EdidSerialNumber = edid.SerialNumber;
348 desc.LogicalResolutionInPixels = Sizei(width, height);
349 desc.DesktopDisplayOffset = Vector2i(x, y);
351 switch (mi->product_code)
352 {
353 case 3: desc.DeviceTypeGuess = HmdType_DK2; break;
354 case 2: desc.DeviceTypeGuess = HmdType_DKHDProto; break;
355 case 1: desc.DeviceTypeGuess = HmdType_DK1; break;
357 default:
358 case 0: desc.DeviceTypeGuess = HmdType_Unknown; break;
359 }
361 // Hard-coded defaults in case the device doesn't have the
362 // data itself. DK2 prototypes (0003) or DK HD Prototypes (0002).
363 if ( desc.DeviceTypeGuess == HmdType_DK2
364 || desc.DeviceTypeGuess == HmdType_DKHDProto)
365 {
366 desc.LogicalResolutionInPixels = Sizei(1920, 1080);
367 desc.NativeResolutionInPixels = Sizei(1080, 1920);
368 }
369 else
370 {
371 desc.LogicalResolutionInPixels = Sizei(width, height);
372 desc.NativeResolutionInPixels = Sizei(width, height);
373 }
374 }
376 delete mi;
377 mi = NULL;
378 }
380 XRRFreeOutputInfo(outputInfo);
381 XRRFreeCrtcInfo(crtcInfo);
382 }
384 XRRFreeScreenResources(screen);
385 }
387 XCloseDisplay(display);
389 return result;
390 }
393 //-------------------------------------------------------------------------------------
394 // ***** Display
396 bool Display::Initialize()
397 {
398 // Nothing to initialize. OS X only supports compatibility mode.
399 return true;
400 }
402 bool Display::GetDriverMode(bool& driverInstalled, bool& compatMode, bool& hideDK1Mode)
403 {
404 driverInstalled = false;
405 compatMode = true;
406 hideDK1Mode = false;
407 return true;
408 }
410 bool Display::SetDriverMode(bool /*compatMode*/, bool /*hideDK1Mode*/)
411 {
412 return false;
413 }
415 DisplaySearchHandle* Display::GetDisplaySearchHandle()
416 {
417 return new Linux::LinuxDisplaySearchHandle();
418 }
420 bool Display::InCompatibilityMode( bool displaySearch )
421 {
422 OVR_UNUSED( displaySearch );
423 return true;
424 }
426 int Display::GetDisplayCount(DisplaySearchHandle* handle, bool extended, bool applicationOnly, bool edidInfo)
427 {
428 OVR_UNUSED4(handle, extended, applicationOnly, edidInfo);
430 static int extendedCount = -1;
432 Linux::LinuxDisplaySearchHandle* localHandle = (Linux::LinuxDisplaySearchHandle*)handle;
433 if (localHandle == NULL)
434 {
435 OVR::LogError("[Linux Display] No search handle passed into GetDisplayCount. Return 0 rifts.");
436 return 0;
437 }
439 if (extendedCount == -1 || extended)
440 {
441 extendedCount = discoverExtendedRifts(localHandle->cachedDescriptorArray, Linux::LinuxDisplaySearchHandle::DescArraySize, edidInfo);
442 }
444 localHandle->extended = true;
445 localHandle->extendedDisplayCount = extendedCount;
446 int totalCount = extendedCount;
448 /// FIXME: Implement application mode for OS X.
449 localHandle->application = false;
450 localHandle->applicationDisplayCount = 0;
452 localHandle->displayCount = totalCount;
454 return totalCount;
455 }
458 Ptr<Display> Display::GetDisplay( int index, DisplaySearchHandle* handle )
459 {
460 Ptr<Display> result = NULL;
462 if (index < 0)
463 {
464 OVR::LogError("[Linux Display] Invalid index given to GetDisplay.");
465 return NULL;
466 }
468 Linux::LinuxDisplaySearchHandle* localHandle = (Linux::LinuxDisplaySearchHandle*)handle;
469 if (localHandle == NULL)
470 {
471 OVR::LogError("[Linux Display] No search handle passed into GetDisplay. Return 0 rifts.");
472 return NULL;
473 }
475 if (localHandle->extended)
476 {
477 if (index >= 0 && index < (int)localHandle->extendedDisplayCount)
478 {
479 return *new Linux::LinuxDisplayGeneric(localHandle->cachedDescriptorArray[index]);
480 }
482 index -= localHandle->extendedDisplayCount;
483 }
485 if (localHandle->application)
486 {
487 OVR::LogError("[Linux Display] Mac does not support application displays.");
488 }
490 return result;
491 }
494 } // namespace OVR