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