oculus1

view libovr/Src/osx/OVR_OSX_DeviceManager.cpp @ 17:cfe4979ab3eb

ops, minor error in the last commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 21 Sep 2013 07:09:48 +0300
parents
children
line source
1 /************************************************************************************
3 Filename : OVR_OSX_DeviceManager.cpp
4 Content : OSX specific DeviceManager implementation.
5 Created : March 14, 2013
6 Authors : Lee Cooper
8 Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
10 Use of this software is subject to the terms of the Oculus license
11 agreement provided at the time of installation or download, or which
12 otherwise accompanies this software in either electronic or hard copy form.
14 *************************************************************************************/
16 #include "OVR_OSX_DeviceManager.h"
18 // Sensor & HMD Factories
19 #include "OVR_LatencyTestImpl.h"
20 #include "OVR_SensorImpl.h"
21 #include "OVR_OSX_HMDDevice.h"
22 #include "OVR_OSX_HIDDevice.h"
24 #include "Kernel/OVR_Timer.h"
25 #include "Kernel/OVR_Std.h"
26 #include "Kernel/OVR_Log.h"
28 #include <IOKit/hid/IOHIDManager.h>
29 #include <IOKit/hid/IOHIDKeys.h>
32 namespace OVR { namespace OSX {
34 //-------------------------------------------------------------------------------------
35 // **** OSX::DeviceManager
37 DeviceManager::DeviceManager()
38 {
39 }
41 DeviceManager::~DeviceManager()
42 {
43 OVR_DEBUG_LOG(("OSX::DeviceManager::~DeviceManager was called"));
44 }
46 bool DeviceManager::Initialize(DeviceBase*)
47 {
48 if (!DeviceManagerImpl::Initialize(0))
49 return false;
51 // Start the background thread.
52 pThread = *new DeviceManagerThread();
53 if (!pThread || !pThread->Start())
54 return false;
56 // Wait for the thread to be fully up and running.
57 pThread->StartupEvent.Wait();
59 // Do this now that we know the thread's run loop.
60 HidDeviceManager = *HIDDeviceManager::CreateInternal(this);
62 CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, this);
64 pCreateDesc->pDevice = this;
65 LogText("OVR::DeviceManager - initialized.\n");
67 return true;
68 }
70 void DeviceManager::Shutdown()
71 {
72 LogText("OVR::DeviceManager - shutting down.\n");
74 CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, this);
76 // Set Manager shutdown marker variable; this prevents
77 // any existing DeviceHandle objects from accessing device.
78 pCreateDesc->pLock->pManager = 0;
80 // Push for thread shutdown *WITH NO WAIT*.
81 // This will have the following effect:
82 // - Exit command will get enqueued, which will be executed later on the thread itself.
83 // - Beyond this point, this DeviceManager object may be deleted by our caller.
84 // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
85 // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
86 // after pManager is null.
87 // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
88 // reference to the thread object.
89 pThread->Shutdown();
90 pThread.Clear();
92 DeviceManagerImpl::Shutdown();
93 }
95 ThreadCommandQueue* DeviceManager::GetThreadQueue()
96 {
97 return pThread;
98 }
100 ThreadId DeviceManager::GetThreadId() const
101 {
102 return pThread->GetThreadId();
103 }
105 bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const
106 {
107 if ((info->InfoClassType != Device_Manager) &&
108 (info->InfoClassType != Device_None))
109 return false;
111 info->Type = Device_Manager;
112 info->Version = 0;
113 OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager");
114 OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc.");
115 return true;
116 }
118 DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
119 {
120 // TBD: Can this be avoided in the future, once proper device notification is in place?
121 pThread->PushCall((DeviceManagerImpl*)this,
122 &DeviceManager::EnumerateAllFactoryDevices, true);
124 return DeviceManagerImpl::EnumerateDevicesEx(args);
125 }
127 void DeviceManager::displayReconfigurationCallBack (CGDirectDisplayID display,
128 CGDisplayChangeSummaryFlags flags,
129 void *userInfo)
130 {
131 DeviceManager* manager = reinterpret_cast<DeviceManager*>(userInfo);
132 OVR_UNUSED(manager);
134 if (flags & kCGDisplayAddFlag)
135 {
136 LogText("Display Added, id = %d\n", int(display));
137 manager->EnumerateDevices<HMDDevice>();
138 }
139 else if (flags & kCGDisplayRemoveFlag)
140 {
141 LogText("Display Removed, id = %d\n", int(display));
142 manager->EnumerateDevices<HMDDevice>();
143 }
144 }
146 //-------------------------------------------------------------------------------------
147 // ***** DeviceManager Thread
149 DeviceManagerThread::DeviceManagerThread()
150 : Thread(ThreadStackSize)
151 {
152 }
154 DeviceManagerThread::~DeviceManagerThread()
155 {
156 }
158 int DeviceManagerThread::Run()
159 {
161 SetThreadName("OVR::DeviceManagerThread");
162 LogText("OVR::DeviceManagerThread - running (ThreadId=0x%p).\n", GetThreadId());
164 // Store out the run loop ref.
165 RunLoop = CFRunLoopGetCurrent();
167 // Create a 'source' to enable us to signal the run loop to process the command queue.
168 CFRunLoopSourceContext sourceContext;
169 memset(&sourceContext, 0, sizeof(sourceContext));
170 sourceContext.version = 0;
171 sourceContext.info = this;
172 sourceContext.perform = &staticCommandQueueSourceCallback;
174 CommandQueueSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 , &sourceContext);
176 CFRunLoopAddSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode);
179 // Signal to the parent thread that initialization has finished.
180 StartupEvent.SetEvent();
183 ThreadCommand::PopBuffer command;
185 while(!IsExiting())
186 {
187 // PopCommand will reset event on empty queue.
188 if (PopCommand(&command))
189 {
190 command.Execute();
191 }
192 else
193 {
194 SInt32 exitReason = 0;
195 do {
197 UInt32 waitMs = INT_MAX;
199 // If devices have time-dependent logic registered, get the longest wait
200 // allowed based on current ticks.
201 if (!TicksNotifiers.IsEmpty())
202 {
203 UInt64 ticksMks = Timer::GetTicks();
204 UInt32 waitAllowed;
206 for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++)
207 {
208 waitAllowed = (UInt32)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs);
209 if (waitAllowed < waitMs)
210 waitMs = waitAllowed;
211 }
212 }
214 // Enter blocking run loop. We may continue until we timeout in which
215 // case it's time to service the ticks. Or if commands arrive in the command
216 // queue then the source callback will call 'CFRunLoopStop' causing this
217 // to return.
218 CFTimeInterval blockInterval = 0.001 * (double) waitMs;
219 exitReason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, blockInterval, false);
221 if (exitReason == kCFRunLoopRunFinished)
222 {
223 // Maybe this will occur during shutdown?
224 break;
225 }
226 else if (exitReason == kCFRunLoopRunStopped )
227 {
228 // Commands need processing or we're being shutdown.
229 break;
230 }
231 else if (exitReason == kCFRunLoopRunTimedOut)
232 {
233 // Timed out so that we can service our ticks callbacks.
234 continue;
235 }
236 else if (exitReason == kCFRunLoopRunHandledSource)
237 {
238 // Should never occur since the last param when we call
239 // 'CFRunLoopRunInMode' is false.
240 OVR_ASSERT(false);
241 break;
242 }
243 else
244 {
245 OVR_ASSERT_LOG(false, ("CFRunLoopRunInMode returned unexpected code"));
246 break;
247 }
248 }
249 while(true);
250 }
251 }
254 CFRunLoopRemoveSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode);
255 CFRelease(CommandQueueSource);
257 LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%p).\n", GetThreadId());
259 return 0;
260 }
262 void DeviceManagerThread::staticCommandQueueSourceCallback(void* pContext)
263 {
264 DeviceManagerThread* pThread = (DeviceManagerThread*) pContext;
265 pThread->commandQueueSourceCallback();
266 }
268 void DeviceManagerThread::commandQueueSourceCallback()
269 {
270 CFRunLoopStop(RunLoop);
271 }
273 bool DeviceManagerThread::AddTicksNotifier(Notifier* notify)
274 {
275 TicksNotifiers.PushBack(notify);
276 return true;
277 }
279 bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify)
280 {
281 for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++)
282 {
283 if (TicksNotifiers[i] == notify)
284 {
285 TicksNotifiers.RemoveAt(i);
286 return true;
287 }
288 }
289 return false;
290 }
292 void DeviceManagerThread::Shutdown()
293 {
294 // Push for thread shutdown *WITH NO WAIT*.
295 // This will have the following effect:
296 // - Exit command will get enqueued, which will be executed later on the thread itself.
297 // - Beyond this point, this DeviceManager object may be deleted by our caller.
298 // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
299 // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
300 // after pManager is null.
301 // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
302 // reference to the thread object.
303 PushExitCommand(false);
305 // make sure CFRunLoopRunInMode is woken up
306 CFRunLoopSourceSignal(CommandQueueSource);
307 CFRunLoopWakeUp(RunLoop);
308 }
310 } // namespace OSX
313 //-------------------------------------------------------------------------------------
314 // ***** Creation
316 // Creates a new DeviceManager and initializes OVR.
317 DeviceManager* DeviceManager::Create()
318 {
320 if (!System::IsInitialized())
321 {
322 // Use custom message, since Log is not yet installed.
323 OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
324 LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); );
325 return 0;
326 }
328 Ptr<OSX::DeviceManager> manager = *new OSX::DeviceManager;
330 if (manager)
331 {
332 if (manager->Initialize(0))
333 {
334 manager->AddFactory(&LatencyTestDeviceFactory::Instance);
335 manager->AddFactory(&SensorDeviceFactory::Instance);
336 manager->AddFactory(&OSX::HMDDeviceFactory::Instance);
338 manager->AddRef();
339 }
340 else
341 {
342 manager.Clear();
343 }
344 }
346 return manager.GetPtr();
347 }
349 } // namespace OVR