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