oculus1
diff libovr/Src/osx/OVR_OSX_DeviceManager.cpp @ 1:e2f9e4603129
added LibOVR and started a simple vr wrapper.
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 14 Sep 2013 16:14:59 +0300 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libovr/Src/osx/OVR_OSX_DeviceManager.cpp Sat Sep 14 16:14:59 2013 +0300 1.3 @@ -0,0 +1,349 @@ 1.4 +/************************************************************************************ 1.5 + 1.6 +Filename : OVR_OSX_DeviceManager.cpp 1.7 +Content : OSX specific DeviceManager implementation. 1.8 +Created : March 14, 2013 1.9 +Authors : Lee Cooper 1.10 + 1.11 +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. 1.12 + 1.13 +Use of this software is subject to the terms of the Oculus license 1.14 +agreement provided at the time of installation or download, or which 1.15 +otherwise accompanies this software in either electronic or hard copy form. 1.16 + 1.17 +*************************************************************************************/ 1.18 + 1.19 +#include "OVR_OSX_DeviceManager.h" 1.20 + 1.21 +// Sensor & HMD Factories 1.22 +#include "OVR_LatencyTestImpl.h" 1.23 +#include "OVR_SensorImpl.h" 1.24 +#include "OVR_OSX_HMDDevice.h" 1.25 +#include "OVR_OSX_HIDDevice.h" 1.26 + 1.27 +#include "Kernel/OVR_Timer.h" 1.28 +#include "Kernel/OVR_Std.h" 1.29 +#include "Kernel/OVR_Log.h" 1.30 + 1.31 +#include <IOKit/hid/IOHIDManager.h> 1.32 +#include <IOKit/hid/IOHIDKeys.h> 1.33 + 1.34 + 1.35 +namespace OVR { namespace OSX { 1.36 + 1.37 +//------------------------------------------------------------------------------------- 1.38 +// **** OSX::DeviceManager 1.39 + 1.40 +DeviceManager::DeviceManager() 1.41 +{ 1.42 +} 1.43 + 1.44 +DeviceManager::~DeviceManager() 1.45 +{ 1.46 + OVR_DEBUG_LOG(("OSX::DeviceManager::~DeviceManager was called")); 1.47 +} 1.48 + 1.49 +bool DeviceManager::Initialize(DeviceBase*) 1.50 +{ 1.51 + if (!DeviceManagerImpl::Initialize(0)) 1.52 + return false; 1.53 + 1.54 + // Start the background thread. 1.55 + pThread = *new DeviceManagerThread(); 1.56 + if (!pThread || !pThread->Start()) 1.57 + return false; 1.58 + 1.59 + // Wait for the thread to be fully up and running. 1.60 + pThread->StartupEvent.Wait(); 1.61 + 1.62 + // Do this now that we know the thread's run loop. 1.63 + HidDeviceManager = *HIDDeviceManager::CreateInternal(this); 1.64 + 1.65 + CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, this); 1.66 + 1.67 + pCreateDesc->pDevice = this; 1.68 + LogText("OVR::DeviceManager - initialized.\n"); 1.69 + 1.70 + return true; 1.71 +} 1.72 + 1.73 +void DeviceManager::Shutdown() 1.74 +{ 1.75 + LogText("OVR::DeviceManager - shutting down.\n"); 1.76 + 1.77 + CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, this); 1.78 + 1.79 + // Set Manager shutdown marker variable; this prevents 1.80 + // any existing DeviceHandle objects from accessing device. 1.81 + pCreateDesc->pLock->pManager = 0; 1.82 + 1.83 + // Push for thread shutdown *WITH NO WAIT*. 1.84 + // This will have the following effect: 1.85 + // - Exit command will get enqueued, which will be executed later on the thread itself. 1.86 + // - Beyond this point, this DeviceManager object may be deleted by our caller. 1.87 + // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will 1.88 + // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued 1.89 + // after pManager is null. 1.90 + // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last 1.91 + // reference to the thread object. 1.92 + pThread->Shutdown(); 1.93 + pThread.Clear(); 1.94 + 1.95 + DeviceManagerImpl::Shutdown(); 1.96 +} 1.97 + 1.98 +ThreadCommandQueue* DeviceManager::GetThreadQueue() 1.99 +{ 1.100 + return pThread; 1.101 +} 1.102 + 1.103 +ThreadId DeviceManager::GetThreadId() const 1.104 +{ 1.105 + return pThread->GetThreadId(); 1.106 +} 1.107 + 1.108 +bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const 1.109 +{ 1.110 + if ((info->InfoClassType != Device_Manager) && 1.111 + (info->InfoClassType != Device_None)) 1.112 + return false; 1.113 + 1.114 + info->Type = Device_Manager; 1.115 + info->Version = 0; 1.116 + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager"); 1.117 + OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc."); 1.118 + return true; 1.119 +} 1.120 + 1.121 +DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args) 1.122 +{ 1.123 + // TBD: Can this be avoided in the future, once proper device notification is in place? 1.124 + pThread->PushCall((DeviceManagerImpl*)this, 1.125 + &DeviceManager::EnumerateAllFactoryDevices, true); 1.126 + 1.127 + return DeviceManagerImpl::EnumerateDevicesEx(args); 1.128 +} 1.129 + 1.130 +void DeviceManager::displayReconfigurationCallBack (CGDirectDisplayID display, 1.131 + CGDisplayChangeSummaryFlags flags, 1.132 + void *userInfo) 1.133 +{ 1.134 + DeviceManager* manager = reinterpret_cast<DeviceManager*>(userInfo); 1.135 + OVR_UNUSED(manager); 1.136 + 1.137 + if (flags & kCGDisplayAddFlag) 1.138 + { 1.139 + LogText("Display Added, id = %d\n", int(display)); 1.140 + manager->EnumerateDevices<HMDDevice>(); 1.141 + } 1.142 + else if (flags & kCGDisplayRemoveFlag) 1.143 + { 1.144 + LogText("Display Removed, id = %d\n", int(display)); 1.145 + manager->EnumerateDevices<HMDDevice>(); 1.146 + } 1.147 +} 1.148 + 1.149 +//------------------------------------------------------------------------------------- 1.150 +// ***** DeviceManager Thread 1.151 + 1.152 +DeviceManagerThread::DeviceManagerThread() 1.153 + : Thread(ThreadStackSize) 1.154 +{ 1.155 +} 1.156 + 1.157 +DeviceManagerThread::~DeviceManagerThread() 1.158 +{ 1.159 +} 1.160 + 1.161 +int DeviceManagerThread::Run() 1.162 +{ 1.163 + 1.164 + SetThreadName("OVR::DeviceManagerThread"); 1.165 + LogText("OVR::DeviceManagerThread - running (ThreadId=0x%p).\n", GetThreadId()); 1.166 + 1.167 + // Store out the run loop ref. 1.168 + RunLoop = CFRunLoopGetCurrent(); 1.169 + 1.170 + // Create a 'source' to enable us to signal the run loop to process the command queue. 1.171 + CFRunLoopSourceContext sourceContext; 1.172 + memset(&sourceContext, 0, sizeof(sourceContext)); 1.173 + sourceContext.version = 0; 1.174 + sourceContext.info = this; 1.175 + sourceContext.perform = &staticCommandQueueSourceCallback; 1.176 + 1.177 + CommandQueueSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 , &sourceContext); 1.178 + 1.179 + CFRunLoopAddSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode); 1.180 + 1.181 + 1.182 + // Signal to the parent thread that initialization has finished. 1.183 + StartupEvent.SetEvent(); 1.184 + 1.185 + 1.186 + ThreadCommand::PopBuffer command; 1.187 + 1.188 + while(!IsExiting()) 1.189 + { 1.190 + // PopCommand will reset event on empty queue. 1.191 + if (PopCommand(&command)) 1.192 + { 1.193 + command.Execute(); 1.194 + } 1.195 + else 1.196 + { 1.197 + SInt32 exitReason = 0; 1.198 + do { 1.199 + 1.200 + UInt32 waitMs = INT_MAX; 1.201 + 1.202 + // If devices have time-dependent logic registered, get the longest wait 1.203 + // allowed based on current ticks. 1.204 + if (!TicksNotifiers.IsEmpty()) 1.205 + { 1.206 + UInt64 ticksMks = Timer::GetTicks(); 1.207 + UInt32 waitAllowed; 1.208 + 1.209 + for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++) 1.210 + { 1.211 + waitAllowed = (UInt32)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs); 1.212 + if (waitAllowed < waitMs) 1.213 + waitMs = waitAllowed; 1.214 + } 1.215 + } 1.216 + 1.217 + // Enter blocking run loop. We may continue until we timeout in which 1.218 + // case it's time to service the ticks. Or if commands arrive in the command 1.219 + // queue then the source callback will call 'CFRunLoopStop' causing this 1.220 + // to return. 1.221 + CFTimeInterval blockInterval = 0.001 * (double) waitMs; 1.222 + exitReason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, blockInterval, false); 1.223 + 1.224 + if (exitReason == kCFRunLoopRunFinished) 1.225 + { 1.226 + // Maybe this will occur during shutdown? 1.227 + break; 1.228 + } 1.229 + else if (exitReason == kCFRunLoopRunStopped ) 1.230 + { 1.231 + // Commands need processing or we're being shutdown. 1.232 + break; 1.233 + } 1.234 + else if (exitReason == kCFRunLoopRunTimedOut) 1.235 + { 1.236 + // Timed out so that we can service our ticks callbacks. 1.237 + continue; 1.238 + } 1.239 + else if (exitReason == kCFRunLoopRunHandledSource) 1.240 + { 1.241 + // Should never occur since the last param when we call 1.242 + // 'CFRunLoopRunInMode' is false. 1.243 + OVR_ASSERT(false); 1.244 + break; 1.245 + } 1.246 + else 1.247 + { 1.248 + OVR_ASSERT_LOG(false, ("CFRunLoopRunInMode returned unexpected code")); 1.249 + break; 1.250 + } 1.251 + } 1.252 + while(true); 1.253 + } 1.254 + } 1.255 + 1.256 + 1.257 + CFRunLoopRemoveSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode); 1.258 + CFRelease(CommandQueueSource); 1.259 + 1.260 + LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%p).\n", GetThreadId()); 1.261 + 1.262 + return 0; 1.263 +} 1.264 + 1.265 +void DeviceManagerThread::staticCommandQueueSourceCallback(void* pContext) 1.266 +{ 1.267 + DeviceManagerThread* pThread = (DeviceManagerThread*) pContext; 1.268 + pThread->commandQueueSourceCallback(); 1.269 +} 1.270 + 1.271 +void DeviceManagerThread::commandQueueSourceCallback() 1.272 +{ 1.273 + CFRunLoopStop(RunLoop); 1.274 +} 1.275 + 1.276 +bool DeviceManagerThread::AddTicksNotifier(Notifier* notify) 1.277 +{ 1.278 + TicksNotifiers.PushBack(notify); 1.279 + return true; 1.280 +} 1.281 + 1.282 +bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify) 1.283 +{ 1.284 + for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++) 1.285 + { 1.286 + if (TicksNotifiers[i] == notify) 1.287 + { 1.288 + TicksNotifiers.RemoveAt(i); 1.289 + return true; 1.290 + } 1.291 + } 1.292 + return false; 1.293 +} 1.294 + 1.295 +void DeviceManagerThread::Shutdown() 1.296 +{ 1.297 + // Push for thread shutdown *WITH NO WAIT*. 1.298 + // This will have the following effect: 1.299 + // - Exit command will get enqueued, which will be executed later on the thread itself. 1.300 + // - Beyond this point, this DeviceManager object may be deleted by our caller. 1.301 + // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will 1.302 + // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued 1.303 + // after pManager is null. 1.304 + // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last 1.305 + // reference to the thread object. 1.306 + PushExitCommand(false); 1.307 + 1.308 + // make sure CFRunLoopRunInMode is woken up 1.309 + CFRunLoopSourceSignal(CommandQueueSource); 1.310 + CFRunLoopWakeUp(RunLoop); 1.311 +} 1.312 + 1.313 +} // namespace OSX 1.314 + 1.315 + 1.316 +//------------------------------------------------------------------------------------- 1.317 +// ***** Creation 1.318 + 1.319 +// Creates a new DeviceManager and initializes OVR. 1.320 +DeviceManager* DeviceManager::Create() 1.321 +{ 1.322 + 1.323 + if (!System::IsInitialized()) 1.324 + { 1.325 + // Use custom message, since Log is not yet installed. 1.326 + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> 1.327 + LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); ); 1.328 + return 0; 1.329 + } 1.330 + 1.331 + Ptr<OSX::DeviceManager> manager = *new OSX::DeviceManager; 1.332 + 1.333 + if (manager) 1.334 + { 1.335 + if (manager->Initialize(0)) 1.336 + { 1.337 + manager->AddFactory(&LatencyTestDeviceFactory::Instance); 1.338 + manager->AddFactory(&SensorDeviceFactory::Instance); 1.339 + manager->AddFactory(&OSX::HMDDeviceFactory::Instance); 1.340 + 1.341 + manager->AddRef(); 1.342 + } 1.343 + else 1.344 + { 1.345 + manager.Clear(); 1.346 + } 1.347 + } 1.348 + 1.349 + return manager.GetPtr(); 1.350 +} 1.351 + 1.352 +} // namespace OVR