diff options
author | Sven Gothel <[email protected]> | 2015-03-21 23:01:12 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2015-03-21 23:01:12 +0100 |
commit | 0c5c4be020c2d55540058a49b2a879f46d5a1e13 (patch) | |
tree | 00f84c2ca18cc233b826014094b9cad0769a3ea5 /LibOVR/Src/Displays/OVR_Linux_Display.cpp | |
parent | cbbd775b6c754927632c333ff01424a0d2048c7c (diff) | |
parent | e490c3c7f7bb5461cfa78a214827aa534fb43a3e (diff) |
Merge branch 'vanilla_0.4.4' and resolve conflicts
TODO: Validate for removed patches due to relocation
Resolved Conflicts:
LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp
LibOVR/Src/OVR_Linux_HMDDevice.cpp
LibOVR/Src/OVR_OSX_HMDDevice.cpp
LibOVR/Src/OVR_Profile.cpp
LibOVR/Src/OVR_Sensor2Impl.cpp
LibOVR/Src/OVR_SensorFusion.cpp
LibOVR/Src/OVR_SensorImpl.cpp
LibOVR/Src/OVR_Win32_DeviceStatus.cpp
LibOVR/Src/OVR_Win32_HIDDevice.cpp
LibOVR/Src/OVR_Win32_HIDDevice.h
LibOVR/Src/OVR_Win32_HMDDevice.cpp
Diffstat (limited to 'LibOVR/Src/Displays/OVR_Linux_Display.cpp')
-rw-r--r-- | LibOVR/Src/Displays/OVR_Linux_Display.cpp | 494 |
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 |