summaryrefslogtreecommitdiffstats
path: root/LibOVR/Src/Displays/OVR_Linux_Display.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'LibOVR/Src/Displays/OVR_Linux_Display.cpp')
-rw-r--r--LibOVR/Src/Displays/OVR_Linux_Display.cpp494
1 files changed, 494 insertions, 0 deletions
diff --git a/LibOVR/Src/Displays/OVR_Linux_Display.cpp b/LibOVR/Src/Displays/OVR_Linux_Display.cpp
new file mode 100644
index 0000000..4b840dd
--- /dev/null
+++ b/LibOVR/Src/Displays/OVR_Linux_Display.cpp
@@ -0,0 +1,494 @@
+/************************************************************************************
+
+Filename : OVR_Linux_Display.cpp
+Content : Linux-specific Display declarations
+Created : July 2, 2014
+Authors : James Hughes
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+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 "OVR_Linux_Display.h"
+#include "../Kernel/OVR_Log.h"
+
+#include "../../../3rdParty/EDID/edid.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/Xatom.h>
+
+//-------------------------------------------------------------------------------------
+// ***** Display enumeration Helpers
+
+namespace OVR {
+
+static const uint8_t edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x00 };
+
+static const uint8_t edid_v1_descriptor_flag[] = { 0x00, 0x00 };
+
+static const int DESCRIPTOR_DATA = 5;
+static const int UNKNOWN_DESCRIPTOR = -1;
+static const int DETAILED_TIMING_BLOCK = -2;
+
+// The following three functions were pulled from OVR_Linux_Display.cpp
+// and modified slightly.
+static int blockType(uint8_t* block)
+{
+ if (!strncmp((const char*)edid_v1_descriptor_flag, (const char*)block, 2))
+ {
+ if (block[2] != 0)
+ {
+ return UNKNOWN_DESCRIPTOR;
+ }
+ else
+ {
+ return block[3];
+ }
+ }
+ else
+ {
+ return DETAILED_TIMING_BLOCK;
+ }
+}
+
+static char* getMonitorName(const uint8_t* block)
+{
+ static char name[13];
+ uint8_t const* ptr = block + DESCRIPTOR_DATA;
+
+ for (int i = 0; i < 13; i++, ptr++)
+ {
+ if (*ptr == 0xa)
+ {
+ name[i] = 0;
+ return name;
+ }
+
+ name[i] = *ptr;
+ }
+
+ return name;
+}
+
+// Returns -1 on failure, 0 otherwise.
+static int parseEdid(uint8_t* edid, Linux::DisplayEDID& edidResult)
+{
+ const int EDID_LENGTH = 0x80;
+ const int EDID_HEADER = 0x00;
+ const int EDID_HEADER_END = 0x07;
+
+ // const int EDID_STRUCT_VERSION = 0x12;
+ // const int EDID_STRUCT_REVISION = 0x13;
+
+ const int MONITOR_NAME = 0xfc;
+ // const int MONITOR_LIMITS = 0xfd;
+ const int MONITOR_SERIAL = 0xff;
+
+ // const int ESTABLISHED_TIMING_1 = 0x23;
+ // const int ESTABLISHED_TIMING_2 = 0x24;
+ // const int MANUFACTURERS_TIMINGS = 0x25;
+
+ const int DETAILED_TIMING_DESCRIPTIONS_START = 0x36;
+ const int DETAILED_TIMING_DESCRIPTION_SIZE = 18;
+ const int NO_DETAILED_TIMING_DESCRIPTIONS = 4;
+
+ // const int DETAILED_TIMING_DESCRIPTION_1 = 0x36;
+ // const int DETAILED_TIMING_DESCRIPTION_2 = 0x48;
+ // const int DETAILED_TIMING_DESCRIPTION_3 = 0x5a;
+ // const int DETAILED_TIMING_DESCRIPTION_4 = 0x6c;
+
+ const char* monitorName = "Unknown";
+ uint8_t* block = NULL;
+ uint8_t checksum = 0;
+
+ for (int i = 0; i < EDID_LENGTH; i++)
+ {
+ checksum += edid[i];
+ }
+
+ // Bad checksum, fail EDID
+ if (checksum != 0)
+ {
+ return -1;
+ }
+
+ if (strncmp((const char*)edid + EDID_HEADER,
+ (const char*)edid_v1_header, EDID_HEADER_END + 1))
+ {
+ // First bytes don't match EDID version 1 header
+ return -1;
+ }
+
+ // Monitor name and timings
+ char serialNumber[14];
+ memset(serialNumber, 0, 14);
+
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+
+ for (int i = 0; i < NO_DETAILED_TIMING_DESCRIPTIONS;
+ i++, block += DETAILED_TIMING_DESCRIPTION_SIZE)
+ {
+
+ if (blockType(block) == MONITOR_NAME)
+ {
+ monitorName = getMonitorName(block);
+ }
+
+ if (blockType(block) == MONITOR_SERIAL)
+ {
+ memcpy(serialNumber, block + 5, 13);
+ break;
+ }
+ }
+
+ uint8_t vendorString[4] = {0};
+
+ vendorString[0] = (edid[8] >> 2 & 31) + 64;
+ vendorString[1] = (((edid[8] & 3) << 3) | (edid[9] >> 5)) + 64;
+ vendorString[2] = (edid[9] & 31) + 64;
+
+ edidResult.ModelNumber = *(uint16_t*)&edid[10];
+ edidResult.MonitorName = monitorName;
+ edidResult.VendorName = reinterpret_cast<const char*>(vendorString);
+ edidResult.SerialNumber = serialNumber;
+
+ // FIXME: Get timings as well
+
+ // std::cout << "# EDID version " << static_cast<int>(edid[EDID_STRUCT_VERSION])
+ // << " revision " << static_cast<int>(edid[EDID_STRUCT_REVISION])
+ // << std::endl;
+
+ return 0;
+}
+
+
+// Returns -1 in the case of failure, 0 otherwise.
+// Parameters:
+// data OUT This pointer is modified to point to the output from
+// XRRGetOutputProperty. You *must* call XFree on this pointer.
+// dataLen OUT The length of the data returned in 'data'.
+static int getXRRProperty(struct _XDisplay* display, RROutput output, Atom atom,
+ uint8_t** data, int* dataLen)
+{
+ unsigned long nitems;
+ unsigned long bytesAfter;
+ int actualFormat;
+ Atom actualType;
+
+ int ret = XRRGetOutputProperty(display, output, atom, 0, 100,
+ False, False, AnyPropertyType,
+ &actualType, &actualFormat, &nitems,
+ &bytesAfter, data);
+
+ if (None != ret)
+ {
+ *dataLen = nitems;
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+static XRRModeInfo* findModeByXID(XRRScreenResources* screen, RRMode xid)
+{
+ for (int m = 0; m < screen->nmode; ++m)
+ {
+ XRRModeInfo* mode = &screen->modes[m];
+ if (xid == mode->id)
+ {
+ return mode;
+ }
+ }
+ return NULL;
+}
+
+static int discoverExtendedRifts(OVR::Linux::DisplayDesc* descriptorArray, int inputArraySize, bool /*edidInfo*/)
+{
+ int result = 0;
+
+ struct _XDisplay* display = XOpenDisplay(NULL);
+
+ if (display == NULL)
+ {
+ OVR::LogError("[Linux Display] Unable to open X Display!");
+ return 0;
+ }
+
+ Atom EDIDAtom = XInternAtom(display, RR_PROPERTY_RANDR_EDID, False);
+ int numScreens = XScreenCount(display);
+ for (int i = 0; i < numScreens; ++i)
+ {
+ Window sr = XRootWindow(display, i);
+ XRRScreenResources* screen = XRRGetScreenResources(display, sr);
+
+ for (int ii = 0; ii < screen->ncrtc; ++ii)
+ {
+ XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screen, screen->crtcs[ii]);
+
+ if (0 == crtcInfo->noutput)
+ {
+ XRRFreeCrtcInfo(crtcInfo);
+ continue;
+ }
+
+ bool foundOutput = false;
+ RROutput output = crtcInfo->outputs[0];
+ for (int k = 0; k < crtcInfo->noutput; ++k)
+ {
+ XRROutputInfo* outputInfo =
+ XRRGetOutputInfo(display, screen, crtcInfo->outputs[k]);
+
+ for (int kk = 0; kk < outputInfo->nmode; ++kk)
+ {
+ if (outputInfo->modes[kk] == crtcInfo->mode)
+ {
+ output = crtcInfo->outputs[k];
+ foundOutput = true;
+ break;
+ }
+ }
+ XRRFreeOutputInfo(outputInfo);
+ if (foundOutput)
+ {
+ break;
+ }
+ }
+
+ if (!foundOutput)
+ {
+ XRRFreeCrtcInfo(crtcInfo);
+ continue;
+ }
+
+ XRROutputInfo* outputInfo = XRRGetOutputInfo(display, screen, output);
+ if (RR_Connected != outputInfo->connection)
+ {
+ XRRFreeOutputInfo(outputInfo);
+ XRRFreeCrtcInfo(crtcInfo);
+ continue;
+ }
+
+ // Read EDID associated with crtc.
+ uint8_t* data = NULL;
+ int dataLen = 0;
+ if (getXRRProperty(display, output, EDIDAtom, &data, &dataLen) != 0)
+ {
+ // Identify rifts based on EDID.
+ Linux::DisplayEDID edid;
+ parseEdid(data, edid);
+ XFree(data);
+ data = NULL;
+
+ // TODO: Remove either this 3rdParty call to read EDID data
+ // or remove our own parsing of the EDID. Probably opt
+ // to remove our parsing.
+ MonitorInfo* mi = read_edid_data(display, output);
+ if (mi == NULL)
+ {
+ XRRFreeOutputInfo(outputInfo);
+ XRRFreeCrtcInfo(crtcInfo);
+ continue;
+ }
+
+ if (edid.VendorName == "OVR")
+ {
+ if( result >= inputArraySize )
+ {
+ delete mi;
+ XRRFreeOutputInfo(outputInfo);
+ XRRFreeCrtcInfo(crtcInfo);
+ return result;
+ }
+
+ XRRModeInfo* modeInfo = findModeByXID(screen, crtcInfo->mode);
+
+ int width = modeInfo->width;
+ int height = modeInfo->height;
+
+ if ( crtcInfo->rotation == RR_Rotate_90
+ || crtcInfo->rotation == RR_Rotate_270 )
+ {
+ width = modeInfo->height;
+ height = modeInfo->width;
+ }
+
+ int x = crtcInfo->x;
+ int y = crtcInfo->y;
+
+ // Generate a device ID string similar Windows does it
+ char device_id[32];
+ OVR_sprintf(device_id, 32, "%s%04d-%d",
+ mi->manufacturer_code, mi->product_code,
+ screen->crtcs[ii]);
+
+ OVR::Linux::DisplayDesc& desc = descriptorArray[result++];
+ desc.DisplayID = device_id;
+ desc.ModelName = edid.MonitorName;
+ desc.EdidSerialNumber = edid.SerialNumber;
+ desc.LogicalResolutionInPixels = Sizei(width, height);
+ desc.DesktopDisplayOffset = Vector2i(x, y);
+
+ switch (mi->product_code)
+ {
+ case 3: desc.DeviceTypeGuess = HmdType_DK2; break;
+ case 2: desc.DeviceTypeGuess = HmdType_DKHDProto; break;
+ case 1: desc.DeviceTypeGuess = HmdType_DK1; break;
+
+ default:
+ case 0: desc.DeviceTypeGuess = HmdType_Unknown; break;
+ }
+
+ // Hard-coded defaults in case the device doesn't have the
+ // data itself. DK2 prototypes (0003) or DK HD Prototypes (0002).
+ if ( desc.DeviceTypeGuess == HmdType_DK2
+ || desc.DeviceTypeGuess == HmdType_DKHDProto)
+ {
+ desc.LogicalResolutionInPixels = Sizei(1920, 1080);
+ desc.NativeResolutionInPixels = Sizei(1080, 1920);
+ }
+ else
+ {
+ desc.LogicalResolutionInPixels = Sizei(width, height);
+ desc.NativeResolutionInPixels = Sizei(width, height);
+ }
+ }
+
+ delete mi;
+ mi = NULL;
+ }
+
+ XRRFreeOutputInfo(outputInfo);
+ XRRFreeCrtcInfo(crtcInfo);
+ }
+
+ XRRFreeScreenResources(screen);
+ }
+
+ XCloseDisplay(display);
+
+ return result;
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Display
+
+bool Display::Initialize()
+{
+ // Nothing to initialize. OS X only supports compatibility mode.
+ return true;
+}
+
+bool Display::GetDriverMode(bool& driverInstalled, bool& compatMode, bool& hideDK1Mode)
+{
+ driverInstalled = false;
+ compatMode = true;
+ hideDK1Mode = false;
+ return true;
+}
+
+bool Display::SetDriverMode(bool /*compatMode*/, bool /*hideDK1Mode*/)
+{
+ return false;
+}
+
+DisplaySearchHandle* Display::GetDisplaySearchHandle()
+{
+ return new Linux::LinuxDisplaySearchHandle();
+}
+
+bool Display::InCompatibilityMode( bool displaySearch )
+{
+ OVR_UNUSED( displaySearch );
+ return true;
+}
+
+int Display::GetDisplayCount(DisplaySearchHandle* handle, bool extended, bool applicationOnly, bool edidInfo)
+{
+ OVR_UNUSED4(handle, extended, applicationOnly, edidInfo);
+
+ static int extendedCount = -1;
+
+ Linux::LinuxDisplaySearchHandle* localHandle = (Linux::LinuxDisplaySearchHandle*)handle;
+ if (localHandle == NULL)
+ {
+ OVR::LogError("[Linux Display] No search handle passed into GetDisplayCount. Return 0 rifts.");
+ return 0;
+ }
+
+ if (extendedCount == -1 || extended)
+ {
+ extendedCount = discoverExtendedRifts(localHandle->cachedDescriptorArray, Linux::LinuxDisplaySearchHandle::DescArraySize, edidInfo);
+ }
+
+ localHandle->extended = true;
+ localHandle->extendedDisplayCount = extendedCount;
+ int totalCount = extendedCount;
+
+ /// FIXME: Implement application mode for OS X.
+ localHandle->application = false;
+ localHandle->applicationDisplayCount = 0;
+
+ localHandle->displayCount = totalCount;
+
+ return totalCount;
+}
+
+
+Ptr<Display> Display::GetDisplay( int index, DisplaySearchHandle* handle )
+{
+ Ptr<Display> result = NULL;
+
+ if (index < 0)
+ {
+ OVR::LogError("[Linux Display] Invalid index given to GetDisplay.");
+ return NULL;
+ }
+
+ Linux::LinuxDisplaySearchHandle* localHandle = (Linux::LinuxDisplaySearchHandle*)handle;
+ if (localHandle == NULL)
+ {
+ OVR::LogError("[Linux Display] No search handle passed into GetDisplay. Return 0 rifts.");
+ return NULL;
+ }
+
+ if (localHandle->extended)
+ {
+ if (index >= 0 && index < (int)localHandle->extendedDisplayCount)
+ {
+ return *new Linux::LinuxDisplayGeneric(localHandle->cachedDescriptorArray[index]);
+ }
+
+ index -= localHandle->extendedDisplayCount;
+ }
+
+ if (localHandle->application)
+ {
+ OVR::LogError("[Linux Display] Mac does not support application displays.");
+ }
+
+ return result;
+}
+
+
+} // namespace OVR