nuclear@1: /************************************************************************************ nuclear@1: nuclear@1: Filename : OVR_Linux_HMDDevice.h nuclear@1: Content : Linux HMDDevice implementation nuclear@1: Created : June 17, 2013 nuclear@1: Authors : Brant Lewis nuclear@1: nuclear@1: Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. nuclear@1: nuclear@1: Use of this software is subject to the terms of the Oculus license nuclear@1: agreement provided at the time of installation or download, or which nuclear@1: otherwise accompanies this software in either electronic or hard copy form. nuclear@1: nuclear@1: *************************************************************************************/ nuclear@1: nuclear@1: #include "OVR_Linux_HMDDevice.h" nuclear@1: nuclear@1: #include "OVR_Linux_DeviceManager.h" nuclear@1: nuclear@1: #include "OVR_Profile.h" nuclear@1: nuclear@1: #include nuclear@1: #include nuclear@1: nuclear@1: namespace OVR { namespace Linux { nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: nuclear@1: HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, const String& displayDeviceName, long dispId) nuclear@1: : DeviceCreateDesc(factory, Device_HMD), nuclear@1: DisplayDeviceName(displayDeviceName), nuclear@1: DesktopX(0), DesktopY(0), Contents(0), nuclear@1: HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0), nuclear@1: DisplayId(dispId) nuclear@1: { nuclear@1: DeviceId = DisplayDeviceName; nuclear@1: } nuclear@1: nuclear@1: HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other) nuclear@1: : DeviceCreateDesc(other.pFactory, Device_HMD), nuclear@1: DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName), nuclear@1: DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents), nuclear@1: HResolution(other.HResolution), VResolution(other.VResolution), nuclear@1: HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize), nuclear@1: DisplayId(other.DisplayId) nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other, nuclear@1: DeviceCreateDesc** pcandidate) const nuclear@1: { nuclear@1: if ((other.Type != Device_HMD) || (other.pFactory != pFactory)) nuclear@1: return Match_None; nuclear@1: nuclear@1: // There are several reasons we can come in here: nuclear@1: // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc nuclear@1: // - Require exact device DeviceId/DeviceName match nuclear@1: // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc nuclear@1: // - This DeviceId is empty; becomes candidate nuclear@1: // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc nuclear@1: // - This other.DeviceId is empty; becomes candidate nuclear@1: nuclear@1: const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; nuclear@1: nuclear@1: if ((DeviceId == s2.DeviceId) && nuclear@1: (DisplayId == s2.DisplayId)) nuclear@1: { nuclear@1: // Non-null DeviceId may match while size is different if screen size was overwritten nuclear@1: // by SensorDisplayInfo in prior iteration. nuclear@1: if (!DeviceId.IsEmpty() || nuclear@1: ((HScreenSize == s2.HScreenSize) && nuclear@1: (VScreenSize == s2.VScreenSize)) ) nuclear@1: { nuclear@1: *pcandidate = 0; nuclear@1: return Match_Found; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // DisplayInfo takes precedence, although we try to match it first. nuclear@1: if ((HResolution == s2.HResolution) && nuclear@1: (VResolution == s2.VResolution) && nuclear@1: (HScreenSize == s2.HScreenSize) && nuclear@1: (VScreenSize == s2.VScreenSize)) nuclear@1: { nuclear@1: if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty()) nuclear@1: { nuclear@1: *pcandidate = const_cast((const DeviceCreateDesc*)this); nuclear@1: return Match_Candidate; nuclear@1: } nuclear@1: nuclear@1: *pcandidate = 0; nuclear@1: return Match_Found; nuclear@1: } nuclear@1: nuclear@1: // SensorDisplayInfo may override resolution settings, so store as candidate. nuclear@1: if (s2.DeviceId.IsEmpty()) nuclear@1: { nuclear@1: *pcandidate = const_cast((const DeviceCreateDesc*)this); nuclear@1: return Match_Candidate; nuclear@1: } nuclear@1: // OTHER HMD Monitor desc may initialize DeviceName/Id nuclear@1: else if (DeviceId.IsEmpty()) nuclear@1: { nuclear@1: *pcandidate = const_cast((const DeviceCreateDesc*)this); nuclear@1: return Match_Candidate; nuclear@1: } nuclear@1: nuclear@1: return Match_None; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, nuclear@1: bool* newDeviceFlag) nuclear@1: { nuclear@1: // This candidate was the the "best fit" to apply sensor DisplayInfo to. nuclear@1: OVR_ASSERT(other.Type == Device_HMD); nuclear@1: nuclear@1: const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; nuclear@1: nuclear@1: // Force screen size on resolution from SensorDisplayInfo. nuclear@1: // We do this because USB detection is more reliable as compared to HDMI EDID, nuclear@1: // which may be corrupted by splitter reporting wrong monitor nuclear@1: if (s2.DeviceId.IsEmpty()) nuclear@1: { nuclear@1: HScreenSize = s2.HScreenSize; nuclear@1: VScreenSize = s2.VScreenSize; nuclear@1: Contents |= Contents_Screen; nuclear@1: nuclear@1: if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion) nuclear@1: { nuclear@1: memcpy(DistortionK, s2.DistortionK, sizeof(float)*4); nuclear@1: Contents |= Contents_Distortion; nuclear@1: } nuclear@1: DeviceId = s2.DeviceId; nuclear@1: DisplayId = s2.DisplayId; nuclear@1: DisplayDeviceName = s2.DisplayDeviceName; nuclear@1: if (newDeviceFlag) *newDeviceFlag = true; nuclear@1: } nuclear@1: else if (DeviceId.IsEmpty()) nuclear@1: { nuclear@1: DeviceId = s2.DeviceId; nuclear@1: DisplayId = s2.DisplayId; nuclear@1: DisplayDeviceName = s2.DisplayDeviceName; nuclear@1: nuclear@1: // ScreenSize and Resolution are NOT assigned here, since they may have nuclear@1: // come from a sensor DisplayInfo (which has precedence over HDMI). nuclear@1: nuclear@1: if (newDeviceFlag) *newDeviceFlag = true; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: if (newDeviceFlag) *newDeviceFlag = false; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HMDDeviceCreateDesc::MatchDevice(const String& path) nuclear@1: { nuclear@1: return DeviceId.CompareNoCase(path) == 0; nuclear@1: } nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** HMDDeviceFactory nuclear@1: nuclear@1: HMDDeviceFactory HMDDeviceFactory::Instance; nuclear@1: nuclear@1: void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) nuclear@1: { nuclear@1: // For now we'll assume the Rift DK1 is attached in extended monitor mode. Ultimately we need to nuclear@1: // use XFree86 to enumerate X11 screens in case the Rift is attached as a separate screen. We also nuclear@1: // need to be able to read the EDID manufacturer product code to be able to differentiate between nuclear@1: // Rift models. nuclear@1: nuclear@1: bool foundHMD = false; nuclear@1: nuclear@1: Display* display = XOpenDisplay(NULL); nuclear@1: if (display && XineramaIsActive(display)) nuclear@1: { nuclear@1: int numberOfScreens; nuclear@1: XineramaScreenInfo* screens = XineramaQueryScreens(display, &numberOfScreens); nuclear@1: nuclear@1: for (int i = 0; i < numberOfScreens; i++) nuclear@1: { nuclear@1: XineramaScreenInfo screenInfo = screens[i]; nuclear@1: nuclear@1: if (screenInfo.width == 1280 && screenInfo.height == 800) nuclear@1: { nuclear@1: String deviceName = "OVR0001"; nuclear@1: nuclear@1: HMDDeviceCreateDesc hmdCreateDesc(this, deviceName, i); nuclear@1: hmdCreateDesc.SetScreenParameters(screenInfo.x_org, screenInfo.y_org, 1280, 800, 0.14976f, 0.0936f); nuclear@1: nuclear@1: OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %s - %d\n", nuclear@1: deviceName.ToCStr(), i)); nuclear@1: nuclear@1: // Notify caller about detected device. This will call EnumerateAddDevice nuclear@1: // if the this is the first time device was detected. nuclear@1: visitor.Visit(hmdCreateDesc); nuclear@1: foundHMD = true; nuclear@1: break; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: XFree(screens); nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // Real HMD device is not found; however, we still may have a 'fake' HMD nuclear@1: // device created via SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo. nuclear@1: // Need to find it and set 'Enumerated' to true to avoid Removal notification. nuclear@1: if (!foundHMD) nuclear@1: { nuclear@1: Ptr hmdDevDesc = getManager()->FindDevice("", Device_HMD); nuclear@1: if (hmdDevDesc) nuclear@1: hmdDevDesc->Enumerated = true; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance() nuclear@1: { nuclear@1: return new HMDDevice(this); nuclear@1: } nuclear@1: nuclear@1: bool HMDDeviceCreateDesc::Is7Inch() const nuclear@1: { nuclear@1: return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch); nuclear@1: } nuclear@1: nuclear@1: Profile* HMDDeviceCreateDesc::GetProfileAddRef() const nuclear@1: { nuclear@1: // Create device may override profile name, so get it from there is possible. nuclear@1: ProfileManager* profileManager = GetManagerImpl()->GetProfileManager(); nuclear@1: ProfileType profileType = GetProfileType(); nuclear@1: const char * profileName = pDevice ? nuclear@1: ((HMDDevice*)pDevice)->GetProfileName() : nuclear@1: profileManager->GetDefaultProfileName(profileType); nuclear@1: nuclear@1: return profileName ? nuclear@1: profileManager->LoadProfile(profileType, profileName) : nuclear@1: profileManager->GetDeviceDefaultProfile(profileType); nuclear@1: } nuclear@1: nuclear@1: nuclear@1: bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const nuclear@1: { nuclear@1: if ((info->InfoClassType != Device_HMD) && nuclear@1: (info->InfoClassType != Device_None)) nuclear@1: return false; nuclear@1: nuclear@1: bool is7Inch = Is7Inch(); nuclear@1: nuclear@1: OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, nuclear@1: is7Inch ? "Oculus Rift DK1" : nuclear@1: ((HResolution >= 1920) ? "Oculus Rift DK HD" : "Oculus Rift DK1-Prototype") ); nuclear@1: OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR"); nuclear@1: info->Type = Device_HMD; nuclear@1: info->Version = 0; nuclear@1: nuclear@1: // Display detection. nuclear@1: if (info->InfoClassType == Device_HMD) nuclear@1: { nuclear@1: HMDInfo* hmdInfo = static_cast(info); nuclear@1: nuclear@1: hmdInfo->DesktopX = DesktopX; nuclear@1: hmdInfo->DesktopY = DesktopY; nuclear@1: hmdInfo->HResolution = HResolution; nuclear@1: hmdInfo->VResolution = VResolution; nuclear@1: hmdInfo->HScreenSize = HScreenSize; nuclear@1: hmdInfo->VScreenSize = VScreenSize; nuclear@1: hmdInfo->VScreenCenter = VScreenSize * 0.5f; nuclear@1: hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable. nuclear@1: hmdInfo->LensSeparationDistance = 0.0635f; nuclear@1: nuclear@1: // Obtain IPD from profile. nuclear@1: Ptr profile = *GetProfileAddRef(); nuclear@1: nuclear@1: if (profile) nuclear@1: { nuclear@1: hmdInfo->InterpupillaryDistance = profile->GetIPD(); nuclear@1: // TBD: Switch on EyeCup type. nuclear@1: } nuclear@1: nuclear@1: if (Contents & Contents_Distortion) nuclear@1: { nuclear@1: memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: if (is7Inch) nuclear@1: { nuclear@1: // 7" screen. nuclear@1: hmdInfo->DistortionK[0] = 1.0f; nuclear@1: hmdInfo->DistortionK[1] = 0.22f; nuclear@1: hmdInfo->DistortionK[2] = 0.24f; nuclear@1: hmdInfo->EyeToScreenDistance = 0.041f; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: hmdInfo->DistortionK[0] = 1.0f; nuclear@1: hmdInfo->DistortionK[1] = 0.18f; nuclear@1: hmdInfo->DistortionK[2] = 0.115f; nuclear@1: nuclear@1: if (HResolution == 1920) nuclear@1: hmdInfo->EyeToScreenDistance = 0.040f; nuclear@1: else nuclear@1: hmdInfo->EyeToScreenDistance = 0.0387f; nuclear@1: } nuclear@1: nuclear@1: hmdInfo->ChromaAbCorrection[0] = 0.996f; nuclear@1: hmdInfo->ChromaAbCorrection[1] = -0.004f; nuclear@1: hmdInfo->ChromaAbCorrection[2] = 1.014f; nuclear@1: hmdInfo->ChromaAbCorrection[3] = 0.0f; nuclear@1: } nuclear@1: nuclear@1: OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName), nuclear@1: DisplayDeviceName.ToCStr()); nuclear@1: hmdInfo->DisplayId = DisplayId; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** HMDDevice nuclear@1: nuclear@1: HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc) nuclear@1: : OVR::DeviceImpl(createDesc, 0) nuclear@1: { nuclear@1: } nuclear@1: HMDDevice::~HMDDevice() nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: bool HMDDevice::Initialize(DeviceBase* parent) nuclear@1: { nuclear@1: pParent = parent; nuclear@1: nuclear@1: // Initialize user profile to default for device. nuclear@1: ProfileManager* profileManager = GetManager()->GetProfileManager(); nuclear@1: ProfileName = profileManager->GetDefaultProfileName(getDesc()->GetProfileType()); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: void HMDDevice::Shutdown() nuclear@1: { nuclear@1: ProfileName.Clear(); nuclear@1: pCachedProfile.Clear(); nuclear@1: pParent.Clear(); nuclear@1: } nuclear@1: nuclear@1: Profile* HMDDevice::GetProfile() const nuclear@1: { nuclear@1: if (!pCachedProfile) nuclear@1: pCachedProfile = *getDesc()->GetProfileAddRef(); nuclear@1: return pCachedProfile.GetPtr(); nuclear@1: } nuclear@1: nuclear@1: const char* HMDDevice::GetProfileName() const nuclear@1: { nuclear@1: return ProfileName.ToCStr(); nuclear@1: } nuclear@1: nuclear@1: bool HMDDevice::SetProfileName(const char* name) nuclear@1: { nuclear@1: pCachedProfile.Clear(); nuclear@1: if (!name) nuclear@1: { nuclear@1: ProfileName.Clear(); nuclear@1: return 0; nuclear@1: } nuclear@1: if (GetManager()->GetProfileManager()->HasProfile(getDesc()->GetProfileType(), name)) nuclear@1: { nuclear@1: ProfileName = name; nuclear@1: return true; nuclear@1: } nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: OVR::SensorDevice* HMDDevice::GetSensor() nuclear@1: { nuclear@1: // Just return first sensor found since we have no way to match it yet. nuclear@1: OVR::SensorDevice* sensor = GetManager()->EnumerateDevices().CreateDevice(); nuclear@1: if (sensor) nuclear@1: sensor->SetCoordinateFrame(SensorDevice::Coord_HMD); nuclear@1: return sensor; nuclear@1: } nuclear@1: nuclear@1: }} // namespace OVR::Linux nuclear@1: nuclear@1: