ovr_sdk
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/LibOVR/Src/Displays/OVR_Linux_Display.cpp Wed Jan 14 06:51:16 2015 +0200 1.3 @@ -0,0 +1,494 @@ 1.4 +/************************************************************************************ 1.5 + 1.6 +Filename : OVR_Linux_Display.cpp 1.7 +Content : Linux-specific Display declarations 1.8 +Created : July 2, 2014 1.9 +Authors : James Hughes 1.10 + 1.11 +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. 1.12 + 1.13 +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); 1.14 +you may not use the Oculus VR Rift SDK except in compliance with the License, 1.15 +which is provided at the time of installation or download, or which 1.16 +otherwise accompanies this software in either electronic or hard copy form. 1.17 + 1.18 +You may obtain a copy of the License at 1.19 + 1.20 +http://www.oculusvr.com/licenses/LICENSE-3.2 1.21 + 1.22 +Unless required by applicable law or agreed to in writing, the Oculus VR SDK 1.23 +distributed under the License is distributed on an "AS IS" BASIS, 1.24 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.25 +See the License for the specific language governing permissions and 1.26 +limitations under the License. 1.27 + 1.28 +*************************************************************************************/ 1.29 + 1.30 +#include "OVR_Linux_Display.h" 1.31 +#include "../Kernel/OVR_Log.h" 1.32 + 1.33 +#include "../../../3rdParty/EDID/edid.h" 1.34 + 1.35 +#include <X11/Xlib.h> 1.36 +#include <X11/Xutil.h> 1.37 +#include <X11/extensions/Xrandr.h> 1.38 +#include <X11/Xatom.h> 1.39 + 1.40 +//------------------------------------------------------------------------------------- 1.41 +// ***** Display enumeration Helpers 1.42 + 1.43 +namespace OVR { 1.44 + 1.45 +static const uint8_t edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff, 1.46 + 0xff, 0xff, 0xff, 0x00 }; 1.47 + 1.48 +static const uint8_t edid_v1_descriptor_flag[] = { 0x00, 0x00 }; 1.49 + 1.50 +static const int DESCRIPTOR_DATA = 5; 1.51 +static const int UNKNOWN_DESCRIPTOR = -1; 1.52 +static const int DETAILED_TIMING_BLOCK = -2; 1.53 + 1.54 +// The following three functions were pulled from OVR_Linux_Display.cpp 1.55 +// and modified slightly. 1.56 +static int blockType(uint8_t* block) 1.57 +{ 1.58 + if (!strncmp((const char*)edid_v1_descriptor_flag, (const char*)block, 2)) 1.59 + { 1.60 + if (block[2] != 0) 1.61 + { 1.62 + return UNKNOWN_DESCRIPTOR; 1.63 + } 1.64 + else 1.65 + { 1.66 + return block[3]; 1.67 + } 1.68 + } 1.69 + else 1.70 + { 1.71 + return DETAILED_TIMING_BLOCK; 1.72 + } 1.73 +} 1.74 + 1.75 +static char* getMonitorName(const uint8_t* block) 1.76 +{ 1.77 + static char name[13]; 1.78 + uint8_t const* ptr = block + DESCRIPTOR_DATA; 1.79 + 1.80 + for (int i = 0; i < 13; i++, ptr++) 1.81 + { 1.82 + if (*ptr == 0xa) 1.83 + { 1.84 + name[i] = 0; 1.85 + return name; 1.86 + } 1.87 + 1.88 + name[i] = *ptr; 1.89 + } 1.90 + 1.91 + return name; 1.92 +} 1.93 + 1.94 +// Returns -1 on failure, 0 otherwise. 1.95 +static int parseEdid(uint8_t* edid, Linux::DisplayEDID& edidResult) 1.96 +{ 1.97 + const int EDID_LENGTH = 0x80; 1.98 + const int EDID_HEADER = 0x00; 1.99 + const int EDID_HEADER_END = 0x07; 1.100 + 1.101 + // const int EDID_STRUCT_VERSION = 0x12; 1.102 + // const int EDID_STRUCT_REVISION = 0x13; 1.103 + 1.104 + const int MONITOR_NAME = 0xfc; 1.105 + // const int MONITOR_LIMITS = 0xfd; 1.106 + const int MONITOR_SERIAL = 0xff; 1.107 + 1.108 + // const int ESTABLISHED_TIMING_1 = 0x23; 1.109 + // const int ESTABLISHED_TIMING_2 = 0x24; 1.110 + // const int MANUFACTURERS_TIMINGS = 0x25; 1.111 + 1.112 + const int DETAILED_TIMING_DESCRIPTIONS_START = 0x36; 1.113 + const int DETAILED_TIMING_DESCRIPTION_SIZE = 18; 1.114 + const int NO_DETAILED_TIMING_DESCRIPTIONS = 4; 1.115 + 1.116 + // const int DETAILED_TIMING_DESCRIPTION_1 = 0x36; 1.117 + // const int DETAILED_TIMING_DESCRIPTION_2 = 0x48; 1.118 + // const int DETAILED_TIMING_DESCRIPTION_3 = 0x5a; 1.119 + // const int DETAILED_TIMING_DESCRIPTION_4 = 0x6c; 1.120 + 1.121 + const char* monitorName = "Unknown"; 1.122 + uint8_t* block = NULL; 1.123 + uint8_t checksum = 0; 1.124 + 1.125 + for (int i = 0; i < EDID_LENGTH; i++) 1.126 + { 1.127 + checksum += edid[i]; 1.128 + } 1.129 + 1.130 + // Bad checksum, fail EDID 1.131 + if (checksum != 0) 1.132 + { 1.133 + return -1; 1.134 + } 1.135 + 1.136 + if (strncmp((const char*)edid + EDID_HEADER, 1.137 + (const char*)edid_v1_header, EDID_HEADER_END + 1)) 1.138 + { 1.139 + // First bytes don't match EDID version 1 header 1.140 + return -1; 1.141 + } 1.142 + 1.143 + // Monitor name and timings 1.144 + char serialNumber[14]; 1.145 + memset(serialNumber, 0, 14); 1.146 + 1.147 + block = edid + DETAILED_TIMING_DESCRIPTIONS_START; 1.148 + 1.149 + for (int i = 0; i < NO_DETAILED_TIMING_DESCRIPTIONS; 1.150 + i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) 1.151 + { 1.152 + 1.153 + if (blockType(block) == MONITOR_NAME) 1.154 + { 1.155 + monitorName = getMonitorName(block); 1.156 + } 1.157 + 1.158 + if (blockType(block) == MONITOR_SERIAL) 1.159 + { 1.160 + memcpy(serialNumber, block + 5, 13); 1.161 + break; 1.162 + } 1.163 + } 1.164 + 1.165 + uint8_t vendorString[4] = {0}; 1.166 + 1.167 + vendorString[0] = (edid[8] >> 2 & 31) + 64; 1.168 + vendorString[1] = (((edid[8] & 3) << 3) | (edid[9] >> 5)) + 64; 1.169 + vendorString[2] = (edid[9] & 31) + 64; 1.170 + 1.171 + edidResult.ModelNumber = *(uint16_t*)&edid[10]; 1.172 + edidResult.MonitorName = monitorName; 1.173 + edidResult.VendorName = reinterpret_cast<const char*>(vendorString); 1.174 + edidResult.SerialNumber = serialNumber; 1.175 + 1.176 + // FIXME: Get timings as well 1.177 + 1.178 + // std::cout << "# EDID version " << static_cast<int>(edid[EDID_STRUCT_VERSION]) 1.179 + // << " revision " << static_cast<int>(edid[EDID_STRUCT_REVISION]) 1.180 + // << std::endl; 1.181 + 1.182 + return 0; 1.183 +} 1.184 + 1.185 + 1.186 +// Returns -1 in the case of failure, 0 otherwise. 1.187 +// Parameters: 1.188 +// data OUT This pointer is modified to point to the output from 1.189 +// XRRGetOutputProperty. You *must* call XFree on this pointer. 1.190 +// dataLen OUT The length of the data returned in 'data'. 1.191 +static int getXRRProperty(struct _XDisplay* display, RROutput output, Atom atom, 1.192 + uint8_t** data, int* dataLen) 1.193 +{ 1.194 + unsigned long nitems; 1.195 + unsigned long bytesAfter; 1.196 + int actualFormat; 1.197 + Atom actualType; 1.198 + 1.199 + int ret = XRRGetOutputProperty(display, output, atom, 0, 100, 1.200 + False, False, AnyPropertyType, 1.201 + &actualType, &actualFormat, &nitems, 1.202 + &bytesAfter, data); 1.203 + 1.204 + if (None != ret) 1.205 + { 1.206 + *dataLen = nitems; 1.207 + return 0; 1.208 + } 1.209 + else 1.210 + { 1.211 + return -1; 1.212 + } 1.213 +} 1.214 + 1.215 +static XRRModeInfo* findModeByXID(XRRScreenResources* screen, RRMode xid) 1.216 +{ 1.217 + for (int m = 0; m < screen->nmode; ++m) 1.218 + { 1.219 + XRRModeInfo* mode = &screen->modes[m]; 1.220 + if (xid == mode->id) 1.221 + { 1.222 + return mode; 1.223 + } 1.224 + } 1.225 + return NULL; 1.226 +} 1.227 + 1.228 +static int discoverExtendedRifts(OVR::Linux::DisplayDesc* descriptorArray, int inputArraySize, bool /*edidInfo*/) 1.229 +{ 1.230 + int result = 0; 1.231 + 1.232 + struct _XDisplay* display = XOpenDisplay(NULL); 1.233 + 1.234 + if (display == NULL) 1.235 + { 1.236 + OVR::LogError("[Linux Display] Unable to open X Display!"); 1.237 + return 0; 1.238 + } 1.239 + 1.240 + Atom EDIDAtom = XInternAtom(display, RR_PROPERTY_RANDR_EDID, False); 1.241 + int numScreens = XScreenCount(display); 1.242 + for (int i = 0; i < numScreens; ++i) 1.243 + { 1.244 + Window sr = XRootWindow(display, i); 1.245 + XRRScreenResources* screen = XRRGetScreenResources(display, sr); 1.246 + 1.247 + for (int ii = 0; ii < screen->ncrtc; ++ii) 1.248 + { 1.249 + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screen, screen->crtcs[ii]); 1.250 + 1.251 + if (0 == crtcInfo->noutput) 1.252 + { 1.253 + XRRFreeCrtcInfo(crtcInfo); 1.254 + continue; 1.255 + } 1.256 + 1.257 + bool foundOutput = false; 1.258 + RROutput output = crtcInfo->outputs[0]; 1.259 + for (int k = 0; k < crtcInfo->noutput; ++k) 1.260 + { 1.261 + XRROutputInfo* outputInfo = 1.262 + XRRGetOutputInfo(display, screen, crtcInfo->outputs[k]); 1.263 + 1.264 + for (int kk = 0; kk < outputInfo->nmode; ++kk) 1.265 + { 1.266 + if (outputInfo->modes[kk] == crtcInfo->mode) 1.267 + { 1.268 + output = crtcInfo->outputs[k]; 1.269 + foundOutput = true; 1.270 + break; 1.271 + } 1.272 + } 1.273 + XRRFreeOutputInfo(outputInfo); 1.274 + if (foundOutput) 1.275 + { 1.276 + break; 1.277 + } 1.278 + } 1.279 + 1.280 + if (!foundOutput) 1.281 + { 1.282 + XRRFreeCrtcInfo(crtcInfo); 1.283 + continue; 1.284 + } 1.285 + 1.286 + XRROutputInfo* outputInfo = XRRGetOutputInfo(display, screen, output); 1.287 + if (RR_Connected != outputInfo->connection) 1.288 + { 1.289 + XRRFreeOutputInfo(outputInfo); 1.290 + XRRFreeCrtcInfo(crtcInfo); 1.291 + continue; 1.292 + } 1.293 + 1.294 + // Read EDID associated with crtc. 1.295 + uint8_t* data = NULL; 1.296 + int dataLen = 0; 1.297 + if (getXRRProperty(display, output, EDIDAtom, &data, &dataLen) != 0) 1.298 + { 1.299 + // Identify rifts based on EDID. 1.300 + Linux::DisplayEDID edid; 1.301 + parseEdid(data, edid); 1.302 + XFree(data); 1.303 + data = NULL; 1.304 + 1.305 + // TODO: Remove either this 3rdParty call to read EDID data 1.306 + // or remove our own parsing of the EDID. Probably opt 1.307 + // to remove our parsing. 1.308 + MonitorInfo* mi = read_edid_data(display, output); 1.309 + if (mi == NULL) 1.310 + { 1.311 + XRRFreeOutputInfo(outputInfo); 1.312 + XRRFreeCrtcInfo(crtcInfo); 1.313 + continue; 1.314 + } 1.315 + 1.316 + if (edid.VendorName == "OVR") 1.317 + { 1.318 + if( result >= inputArraySize ) 1.319 + { 1.320 + delete mi; 1.321 + XRRFreeOutputInfo(outputInfo); 1.322 + XRRFreeCrtcInfo(crtcInfo); 1.323 + return result; 1.324 + } 1.325 + 1.326 + XRRModeInfo* modeInfo = findModeByXID(screen, crtcInfo->mode); 1.327 + 1.328 + int width = modeInfo->width; 1.329 + int height = modeInfo->height; 1.330 + 1.331 + if ( crtcInfo->rotation == RR_Rotate_90 1.332 + || crtcInfo->rotation == RR_Rotate_270 ) 1.333 + { 1.334 + width = modeInfo->height; 1.335 + height = modeInfo->width; 1.336 + } 1.337 + 1.338 + int x = crtcInfo->x; 1.339 + int y = crtcInfo->y; 1.340 + 1.341 + // Generate a device ID string similar Windows does it 1.342 + char device_id[32]; 1.343 + OVR_sprintf(device_id, 32, "%s%04d-%d", 1.344 + mi->manufacturer_code, mi->product_code, 1.345 + screen->crtcs[ii]); 1.346 + 1.347 + OVR::Linux::DisplayDesc& desc = descriptorArray[result++]; 1.348 + desc.DisplayID = device_id; 1.349 + desc.ModelName = edid.MonitorName; 1.350 + desc.EdidSerialNumber = edid.SerialNumber; 1.351 + desc.LogicalResolutionInPixels = Sizei(width, height); 1.352 + desc.DesktopDisplayOffset = Vector2i(x, y); 1.353 + 1.354 + switch (mi->product_code) 1.355 + { 1.356 + case 3: desc.DeviceTypeGuess = HmdType_DK2; break; 1.357 + case 2: desc.DeviceTypeGuess = HmdType_DKHDProto; break; 1.358 + case 1: desc.DeviceTypeGuess = HmdType_DK1; break; 1.359 + 1.360 + default: 1.361 + case 0: desc.DeviceTypeGuess = HmdType_Unknown; break; 1.362 + } 1.363 + 1.364 + // Hard-coded defaults in case the device doesn't have the 1.365 + // data itself. DK2 prototypes (0003) or DK HD Prototypes (0002). 1.366 + if ( desc.DeviceTypeGuess == HmdType_DK2 1.367 + || desc.DeviceTypeGuess == HmdType_DKHDProto) 1.368 + { 1.369 + desc.LogicalResolutionInPixels = Sizei(1920, 1080); 1.370 + desc.NativeResolutionInPixels = Sizei(1080, 1920); 1.371 + } 1.372 + else 1.373 + { 1.374 + desc.LogicalResolutionInPixels = Sizei(width, height); 1.375 + desc.NativeResolutionInPixels = Sizei(width, height); 1.376 + } 1.377 + } 1.378 + 1.379 + delete mi; 1.380 + mi = NULL; 1.381 + } 1.382 + 1.383 + XRRFreeOutputInfo(outputInfo); 1.384 + XRRFreeCrtcInfo(crtcInfo); 1.385 + } 1.386 + 1.387 + XRRFreeScreenResources(screen); 1.388 + } 1.389 + 1.390 + XCloseDisplay(display); 1.391 + 1.392 + return result; 1.393 +} 1.394 + 1.395 + 1.396 +//------------------------------------------------------------------------------------- 1.397 +// ***** Display 1.398 + 1.399 +bool Display::Initialize() 1.400 +{ 1.401 + // Nothing to initialize. OS X only supports compatibility mode. 1.402 + return true; 1.403 +} 1.404 + 1.405 +bool Display::GetDriverMode(bool& driverInstalled, bool& compatMode, bool& hideDK1Mode) 1.406 +{ 1.407 + driverInstalled = false; 1.408 + compatMode = true; 1.409 + hideDK1Mode = false; 1.410 + return true; 1.411 +} 1.412 + 1.413 +bool Display::SetDriverMode(bool /*compatMode*/, bool /*hideDK1Mode*/) 1.414 +{ 1.415 + return false; 1.416 +} 1.417 + 1.418 +DisplaySearchHandle* Display::GetDisplaySearchHandle() 1.419 +{ 1.420 + return new Linux::LinuxDisplaySearchHandle(); 1.421 +} 1.422 + 1.423 +bool Display::InCompatibilityMode( bool displaySearch ) 1.424 +{ 1.425 + OVR_UNUSED( displaySearch ); 1.426 + return true; 1.427 +} 1.428 + 1.429 +int Display::GetDisplayCount(DisplaySearchHandle* handle, bool extended, bool applicationOnly, bool edidInfo) 1.430 +{ 1.431 + OVR_UNUSED4(handle, extended, applicationOnly, edidInfo); 1.432 + 1.433 + static int extendedCount = -1; 1.434 + 1.435 + Linux::LinuxDisplaySearchHandle* localHandle = (Linux::LinuxDisplaySearchHandle*)handle; 1.436 + if (localHandle == NULL) 1.437 + { 1.438 + OVR::LogError("[Linux Display] No search handle passed into GetDisplayCount. Return 0 rifts."); 1.439 + return 0; 1.440 + } 1.441 + 1.442 + if (extendedCount == -1 || extended) 1.443 + { 1.444 + extendedCount = discoverExtendedRifts(localHandle->cachedDescriptorArray, Linux::LinuxDisplaySearchHandle::DescArraySize, edidInfo); 1.445 + } 1.446 + 1.447 + localHandle->extended = true; 1.448 + localHandle->extendedDisplayCount = extendedCount; 1.449 + int totalCount = extendedCount; 1.450 + 1.451 + /// FIXME: Implement application mode for OS X. 1.452 + localHandle->application = false; 1.453 + localHandle->applicationDisplayCount = 0; 1.454 + 1.455 + localHandle->displayCount = totalCount; 1.456 + 1.457 + return totalCount; 1.458 +} 1.459 + 1.460 + 1.461 +Ptr<Display> Display::GetDisplay( int index, DisplaySearchHandle* handle ) 1.462 +{ 1.463 + Ptr<Display> result = NULL; 1.464 + 1.465 + if (index < 0) 1.466 + { 1.467 + OVR::LogError("[Linux Display] Invalid index given to GetDisplay."); 1.468 + return NULL; 1.469 + } 1.470 + 1.471 + Linux::LinuxDisplaySearchHandle* localHandle = (Linux::LinuxDisplaySearchHandle*)handle; 1.472 + if (localHandle == NULL) 1.473 + { 1.474 + OVR::LogError("[Linux Display] No search handle passed into GetDisplay. Return 0 rifts."); 1.475 + return NULL; 1.476 + } 1.477 + 1.478 + if (localHandle->extended) 1.479 + { 1.480 + if (index >= 0 && index < (int)localHandle->extendedDisplayCount) 1.481 + { 1.482 + return *new Linux::LinuxDisplayGeneric(localHandle->cachedDescriptorArray[index]); 1.483 + } 1.484 + 1.485 + index -= localHandle->extendedDisplayCount; 1.486 + } 1.487 + 1.488 + if (localHandle->application) 1.489 + { 1.490 + OVR::LogError("[Linux Display] Mac does not support application displays."); 1.491 + } 1.492 + 1.493 + return result; 1.494 +} 1.495 + 1.496 + 1.497 +} // namespace OVR