OVR_OSX_DeviceManager.cpp
Go to the documentation of this file.
00001 /************************************************************************************
00002 
00003 Filename    :   OVR_OSX_DeviceManager.cpp
00004 Content     :   OSX specific DeviceManager implementation.
00005 Created     :   March 14, 2013
00006 Authors     :   Lee Cooper
00007 
00008 Copyright   :   Copyright 2013 Oculus VR, Inc. All Rights reserved.
00009 
00010 Use of this software is subject to the terms of the Oculus license
00011 agreement provided at the time of installation or download, or which
00012 otherwise accompanies this software in either electronic or hard copy form.
00013 
00014 *************************************************************************************/
00015 
00016 #include "OVR_OSX_DeviceManager.h"
00017 
00018 // Sensor & HMD Factories
00019 #include "OVR_LatencyTestImpl.h"
00020 #include "OVR_SensorImpl.h"
00021 #include "OVR_OSX_HMDDevice.h"
00022 #include "OVR_OSX_HIDDevice.h"
00023 
00024 #include "Kernel/OVR_Timer.h"
00025 #include "Kernel/OVR_Std.h"
00026 #include "Kernel/OVR_Log.h"
00027 
00028 #include <IOKit/hid/IOHIDManager.h>
00029 #include <IOKit/hid/IOHIDKeys.h>
00030 
00031 
00032 namespace OVR { namespace OSX {
00033 
00034 //-------------------------------------------------------------------------------------
00035 // **** OSX::DeviceManager
00036 
00037 DeviceManager::DeviceManager()
00038 {
00039 }
00040 
00041 DeviceManager::~DeviceManager()
00042 {
00043     OVR_DEBUG_LOG(("OSX::DeviceManager::~DeviceManager was called"));
00044 }
00045 
00046 bool DeviceManager::Initialize(DeviceBase*)
00047 {
00048     if (!DeviceManagerImpl::Initialize(0))
00049         return false;
00050     
00051     // Start the background thread.
00052     pThread = *new DeviceManagerThread();
00053     if (!pThread || !pThread->Start())
00054         return false;
00055 
00056     // Wait for the thread to be fully up and running.
00057     pThread->StartupEvent.Wait();
00058 
00059     // Do this now that we know the thread's run loop.
00060     HidDeviceManager = *HIDDeviceManager::CreateInternal(this);
00061 
00062     CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, this);
00063          
00064     pCreateDesc->pDevice = this;
00065     LogText("OVR::DeviceManager - initialized.\n");
00066 
00067     return true;
00068 }
00069 
00070 void DeviceManager::Shutdown()
00071 {
00072     LogText("OVR::DeviceManager - shutting down.\n");
00073 
00074     CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, this);
00075     
00076     // Set Manager shutdown marker variable; this prevents
00077     // any existing DeviceHandle objects from accessing device.
00078     pCreateDesc->pLock->pManager = 0;
00079 
00080     // Push for thread shutdown *WITH NO WAIT*.
00081     // This will have the following effect:
00082     //  - Exit command will get enqueued, which will be executed later on the thread itself.
00083     //  - Beyond this point, this DeviceManager object may be deleted by our caller.
00084     //  - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
00085     //    fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
00086     //    after pManager is null.
00087     //  - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
00088     //    reference to the thread object.
00089     pThread->Shutdown();
00090     pThread.Clear();
00091 
00092     DeviceManagerImpl::Shutdown();
00093 }
00094 
00095 ThreadCommandQueue* DeviceManager::GetThreadQueue()
00096 {
00097     return pThread;
00098 }
00099 
00100 ThreadId DeviceManager::GetThreadId() const
00101 {
00102     return pThread->GetThreadId();
00103 }
00104 
00105 bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const
00106 {
00107     if ((info->InfoClassType != Device_Manager) &&
00108         (info->InfoClassType != Device_None))
00109         return false;
00110     
00111     info->Type    = Device_Manager;
00112     info->Version = 0;
00113     OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager");
00114     OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc.");        
00115     return true;
00116 }
00117 
00118 DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
00119 {
00120     // TBD: Can this be avoided in the future, once proper device notification is in place?
00121     pThread->PushCall((DeviceManagerImpl*)this,
00122                       &DeviceManager::EnumerateAllFactoryDevices, true);
00123 
00124     return DeviceManagerImpl::EnumerateDevicesEx(args);
00125 }
00126 
00127 void DeviceManager::displayReconfigurationCallBack (CGDirectDisplayID display,
00128                                                     CGDisplayChangeSummaryFlags flags,
00129                                                     void *userInfo)
00130 {
00131     DeviceManager* manager = reinterpret_cast<DeviceManager*>(userInfo);
00132     OVR_UNUSED(manager);
00133     
00134     if (flags & kCGDisplayAddFlag)
00135     {
00136         LogText("Display Added, id = %d\n", int(display));
00137         manager->EnumerateDevices<HMDDevice>();
00138     }
00139     else if (flags & kCGDisplayRemoveFlag)
00140     {
00141         LogText("Display Removed, id = %d\n", int(display));
00142         manager->EnumerateDevices<HMDDevice>();
00143     }
00144 }
00145 
00146 //-------------------------------------------------------------------------------------
00147 // ***** DeviceManager Thread 
00148 
00149 DeviceManagerThread::DeviceManagerThread()
00150     : Thread(ThreadStackSize)
00151 {    
00152 }
00153 
00154 DeviceManagerThread::~DeviceManagerThread()
00155 {
00156 }
00157 
00158 int DeviceManagerThread::Run()
00159 {
00160 
00161     SetThreadName("OVR::DeviceManagerThread");
00162     LogText("OVR::DeviceManagerThread - running (ThreadId=0x%p).\n", GetThreadId());
00163 
00164     // Store out the run loop ref.
00165     RunLoop = CFRunLoopGetCurrent();
00166 
00167     // Create a 'source' to enable us to signal the run loop to process the command queue.
00168     CFRunLoopSourceContext sourceContext;
00169     memset(&sourceContext, 0, sizeof(sourceContext));
00170     sourceContext.version = 0;
00171     sourceContext.info = this;
00172     sourceContext.perform = &staticCommandQueueSourceCallback;
00173 
00174     CommandQueueSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 , &sourceContext);
00175 
00176     CFRunLoopAddSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode);
00177 
00178 
00179     // Signal to the parent thread that initialization has finished.
00180     StartupEvent.SetEvent();
00181 
00182 
00183     ThreadCommand::PopBuffer command;
00184    
00185     while(!IsExiting())
00186     {
00187         // PopCommand will reset event on empty queue.
00188         if (PopCommand(&command))
00189         {
00190             command.Execute();
00191         }
00192         else
00193         {
00194             SInt32 exitReason = 0;
00195             do {
00196 
00197                 UInt32 waitMs = INT_MAX;
00198 
00199                 // If devices have time-dependent logic registered, get the longest wait
00200                 // allowed based on current ticks.
00201                 if (!TicksNotifiers.IsEmpty())
00202                 {
00203                     UInt64 ticksMks = Timer::GetTicks();
00204                     UInt32  waitAllowed;
00205 
00206                     for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++)
00207                     {
00208                         waitAllowed = (UInt32)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs);
00209                         if (waitAllowed < waitMs)
00210                             waitMs = waitAllowed;
00211                     }
00212                 }
00213                 
00214                 // Enter blocking run loop. We may continue until we timeout in which
00215                 // case it's time to service the ticks. Or if commands arrive in the command
00216                 // queue then the source callback will call 'CFRunLoopStop' causing this
00217                 // to return.
00218                 CFTimeInterval blockInterval = 0.001 * (double) waitMs;
00219                 exitReason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, blockInterval, false);
00220 
00221                 if (exitReason == kCFRunLoopRunFinished)
00222                 {
00223                     // Maybe this will occur during shutdown?
00224                     break;
00225                 }
00226                 else if (exitReason == kCFRunLoopRunStopped )
00227                 {
00228                     // Commands need processing or we're being shutdown.
00229                     break;
00230                 }
00231                 else if (exitReason == kCFRunLoopRunTimedOut)
00232                 {
00233                     // Timed out so that we can service our ticks callbacks.
00234                     continue;
00235                 }
00236                 else if (exitReason == kCFRunLoopRunHandledSource)
00237                 {
00238                     // Should never occur since the last param when we call
00239                     // 'CFRunLoopRunInMode' is false.
00240                     OVR_ASSERT(false);
00241                     break;
00242                 }
00243                 else
00244                 {
00245                     OVR_ASSERT_LOG(false, ("CFRunLoopRunInMode returned unexpected code"));
00246                     break;
00247                 }
00248             }
00249             while(true);                    
00250         }
00251     }
00252 
00253                                    
00254     CFRunLoopRemoveSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode);
00255     CFRelease(CommandQueueSource);
00256     
00257     LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%p).\n", GetThreadId());
00258 
00259     return 0;
00260 }
00261     
00262 void DeviceManagerThread::staticCommandQueueSourceCallback(void* pContext)
00263 {
00264     DeviceManagerThread* pThread = (DeviceManagerThread*) pContext;
00265     pThread->commandQueueSourceCallback();
00266 }
00267     
00268 void DeviceManagerThread::commandQueueSourceCallback()
00269 {    
00270     CFRunLoopStop(RunLoop);
00271 }
00272 
00273 bool DeviceManagerThread::AddTicksNotifier(Notifier* notify)
00274 {
00275      TicksNotifiers.PushBack(notify);
00276      return true;
00277 }
00278 
00279 bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify)
00280 {
00281     for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++)
00282     {
00283         if (TicksNotifiers[i] == notify)
00284         {
00285             TicksNotifiers.RemoveAt(i);
00286             return true;
00287         }
00288     }
00289     return false;
00290 }
00291 
00292 void DeviceManagerThread::Shutdown()
00293 {
00294     // Push for thread shutdown *WITH NO WAIT*.
00295     // This will have the following effect:
00296     //  - Exit command will get enqueued, which will be executed later on the thread itself.
00297     //  - Beyond this point, this DeviceManager object may be deleted by our caller.
00298     //  - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
00299     //    fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
00300     //    after pManager is null.
00301     //  - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
00302     //    reference to the thread object.
00303     PushExitCommand(false);
00304 
00305     // make sure CFRunLoopRunInMode is woken up
00306     CFRunLoopSourceSignal(CommandQueueSource);
00307     CFRunLoopWakeUp(RunLoop);
00308 }
00309     
00310 } // namespace OSX
00311 
00312 
00313 //-------------------------------------------------------------------------------------
00314 // ***** Creation
00315 
00316 // Creates a new DeviceManager and initializes OVR.
00317 DeviceManager* DeviceManager::Create()
00318 {
00319 
00320     if (!System::IsInitialized())
00321     {
00322         // Use custom message, since Log is not yet installed.
00323         OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
00324             LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); );
00325         return 0;
00326     }
00327 
00328     Ptr<OSX::DeviceManager> manager = *new OSX::DeviceManager;
00329 
00330     if (manager)
00331     {
00332         if (manager->Initialize(0))
00333         {
00334             manager->AddFactory(&LatencyTestDeviceFactory::Instance);
00335             manager->AddFactory(&SensorDeviceFactory::Instance);
00336             manager->AddFactory(&OSX::HMDDeviceFactory::Instance);
00337 
00338             manager->AddRef();
00339         }
00340         else
00341         {
00342             manager.Clear();
00343         }
00344     }    
00345 
00346     return manager.GetPtr();
00347 }
00348 
00349 } // namespace OVR


oculus_sdk
Author(s):
autogenerated on Mon Oct 6 2014 03:01:19