oculus1
view libovr/Src/OVR_DeviceImpl.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_DeviceImpl.h
4 Content : Partial back-end independent implementation of Device interfaces
5 Created : October 10, 2012
6 Authors : Michael Antonov
8 Copyright : Copyright 2012 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_DeviceImpl.h"
17 #include "Kernel/OVR_Atomic.h"
18 #include "Kernel/OVR_Log.h"
19 #include "Kernel/OVR_System.h"
21 #include "OVR_DeviceImpl.h"
22 #include "OVR_SensorImpl.h"
23 #include "OVR_Profile.h"
25 namespace OVR {
28 //-------------------------------------------------------------------------------------
29 // ***** SharedLock
31 // This is a general purpose globally shared Lock implementation that should probably be
32 // moved to Kernel.
33 // May in theory busy spin-wait if we hit contention on first lock creation,
34 // but this shouldn't matter in practice since Lock* should be cached.
37 enum { LockInitMarker = 0xFFFFFFFF };
39 Lock* SharedLock::GetLockAddRef()
40 {
41 int oldUseCount;
43 do {
44 oldUseCount = UseCount;
45 if (oldUseCount == LockInitMarker)
46 continue;
48 if (oldUseCount == 0)
49 {
50 // Initialize marker
51 if (AtomicOps<int>::CompareAndSet_Sync(&UseCount, 0, LockInitMarker))
52 {
53 Construct<Lock>(Buffer);
54 do { }
55 while (!AtomicOps<int>::CompareAndSet_Sync(&UseCount, LockInitMarker, 1));
56 return toLock();
57 }
58 continue;
59 }
61 } while (!AtomicOps<int>::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount + 1));
63 return toLock();
64 }
66 void SharedLock::ReleaseLock(Lock* plock)
67 {
68 OVR_UNUSED(plock);
69 OVR_ASSERT(plock == toLock());
71 int oldUseCount;
73 do {
74 oldUseCount = UseCount;
75 OVR_ASSERT(oldUseCount != LockInitMarker);
77 if (oldUseCount == 1)
78 {
79 // Initialize marker
80 if (AtomicOps<int>::CompareAndSet_Sync(&UseCount, 1, LockInitMarker))
81 {
82 Destruct<Lock>(toLock());
84 do { }
85 while (!AtomicOps<int>::CompareAndSet_Sync(&UseCount, LockInitMarker, 0));
87 return;
88 }
89 continue;
90 }
92 } while (!AtomicOps<int>::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount - 1));
93 }
97 //-------------------------------------------------------------------------------------
98 // ***** MessageHandler
100 // Threading notes:
101 // The OnMessage() handler and SetMessageHandler are currently synchronized
102 // through a separately stored shared Lock object to avoid calling the handler
103 // from background thread while it's being removed.
105 static SharedLock MessageHandlerSharedLock;
108 class MessageHandlerImpl
109 {
110 public:
111 MessageHandlerImpl()
112 : pLock(MessageHandlerSharedLock.GetLockAddRef())
113 {
114 }
115 ~MessageHandlerImpl()
116 {
117 MessageHandlerSharedLock.ReleaseLock(pLock);
118 pLock = 0;
119 }
121 static MessageHandlerImpl* FromHandler(MessageHandler* handler)
122 { return (MessageHandlerImpl*)&handler->Internal; }
123 static const MessageHandlerImpl* FromHandler(const MessageHandler* handler)
124 { return (const MessageHandlerImpl*)&handler->Internal; }
126 // This lock is held while calling a handler and when we are applied/
127 // removed from a device.
128 Lock* pLock;
129 // List of device we are applied to.
130 List<MessageHandlerRef> UseList;
131 };
134 MessageHandlerRef::MessageHandlerRef(DeviceBase* device)
135 : pLock(MessageHandlerSharedLock.GetLockAddRef()), pDevice(device), pHandler(0)
136 {
137 }
139 MessageHandlerRef::~MessageHandlerRef()
140 {
141 {
142 Lock::Locker lockScope(pLock);
143 if (pHandler)
144 {
145 pHandler = 0;
146 RemoveNode();
147 }
148 }
149 MessageHandlerSharedLock.ReleaseLock(pLock);
150 pLock = 0;
151 }
153 void MessageHandlerRef::SetHandler(MessageHandler* handler)
154 {
155 OVR_ASSERT(!handler ||
156 MessageHandlerImpl::FromHandler(handler)->pLock == pLock);
157 Lock::Locker lockScope(pLock);
158 SetHandler_NTS(handler);
159 }
161 void MessageHandlerRef::SetHandler_NTS(MessageHandler* handler)
162 {
163 if (pHandler != handler)
164 {
165 if (pHandler)
166 RemoveNode();
167 pHandler = handler;
169 if (handler)
170 {
171 MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(handler);
172 handlerImpl->UseList.PushBack(this);
173 }
174 // TBD: Call notifier on device?
175 }
176 }
179 MessageHandler::MessageHandler()
180 {
181 OVR_COMPILER_ASSERT(sizeof(Internal) > sizeof(MessageHandlerImpl));
182 Construct<MessageHandlerImpl>(Internal);
183 }
185 MessageHandler::~MessageHandler()
186 {
187 MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
188 {
189 Lock::Locker lockedScope(handlerImpl->pLock);
190 OVR_ASSERT_LOG(handlerImpl->UseList.IsEmpty(),
191 ("~MessageHandler %p - Handler still active; call RemoveHandlerFromDevices", this));
192 }
194 Destruct<MessageHandlerImpl>(handlerImpl);
195 }
197 bool MessageHandler::IsHandlerInstalled() const
198 {
199 const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
200 Lock::Locker lockedScope(handlerImpl->pLock);
201 return handlerImpl->UseList.IsEmpty() != true;
202 }
205 void MessageHandler::RemoveHandlerFromDevices()
206 {
207 MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
208 Lock::Locker lockedScope(handlerImpl->pLock);
210 while(!handlerImpl->UseList.IsEmpty())
211 {
212 MessageHandlerRef* use = handlerImpl->UseList.GetFirst();
213 use->SetHandler_NTS(0);
214 }
215 }
217 Lock* MessageHandler::GetHandlerLock() const
218 {
219 const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
220 return handlerImpl->pLock;
221 }
224 //-------------------------------------------------------------------------------------
225 // ***** DeviceBase
228 // Delegate relevant implementation to DeviceRectord to avoid re-implementation in
229 // every derived Device.
230 void DeviceBase::AddRef()
231 {
232 getDeviceCommon()->DeviceAddRef();
233 }
234 void DeviceBase::Release()
235 {
236 getDeviceCommon()->DeviceRelease();
237 }
238 DeviceBase* DeviceBase::GetParent() const
239 {
240 return getDeviceCommon()->pParent.GetPtr();
241 }
242 DeviceManager* DeviceBase::GetManager() const
243 {
244 return getDeviceCommon()->pCreateDesc->GetManagerImpl();
245 }
247 void DeviceBase::SetMessageHandler(MessageHandler* handler)
248 {
249 getDeviceCommon()->HandlerRef.SetHandler(handler);
250 }
251 MessageHandler* DeviceBase::GetMessageHandler() const
252 {
253 return getDeviceCommon()->HandlerRef.GetHandler();
254 }
256 DeviceType DeviceBase::GetType() const
257 {
258 return getDeviceCommon()->pCreateDesc->Type;
259 }
261 bool DeviceBase::GetDeviceInfo(DeviceInfo* info) const
262 {
263 return getDeviceCommon()->pCreateDesc->GetDeviceInfo(info);
264 //info->Name[0] = 0;
265 //return false;
266 }
268 // returns the MessageHandler's lock
269 Lock* DeviceBase::GetHandlerLock() const
270 {
271 return getDeviceCommon()->HandlerRef.GetLock();
272 }
274 // Derive DeviceManagerCreateDesc to provide abstract function implementation.
275 class DeviceManagerCreateDesc : public DeviceCreateDesc
276 {
277 public:
278 DeviceManagerCreateDesc(DeviceFactory* factory)
279 : DeviceCreateDesc(factory, Device_Manager) { }
281 // We don't need there on Manager since it isn't assigned to DeviceHandle.
282 virtual DeviceCreateDesc* Clone() const { return 0; }
283 virtual MatchResult MatchDevice(const DeviceCreateDesc&,
284 DeviceCreateDesc**) const { return Match_None; }
285 virtual DeviceBase* NewDeviceInstance() { return 0; }
286 virtual bool GetDeviceInfo(DeviceInfo*) const { return false; }
287 };
289 //-------------------------------------------------------------------------------------
290 // ***** DeviceManagerImpl
292 DeviceManagerImpl::DeviceManagerImpl()
293 : DeviceImpl<OVR::DeviceManager>(CreateManagerDesc(), 0)
294 //,DeviceCreateDescList(pCreateDesc ? pCreateDesc->pLock : 0)
295 {
296 if (pCreateDesc)
297 {
298 pCreateDesc->pLock->pManager = this;
299 }
300 }
302 DeviceManagerImpl::~DeviceManagerImpl()
303 {
304 // Shutdown must've been called.
305 OVR_ASSERT(!pCreateDesc->pDevice);
307 // Remove all factories
308 while(!Factories.IsEmpty())
309 {
310 DeviceFactory* factory = Factories.GetFirst();
311 factory->RemovedFromManager();
312 factory->RemoveNode();
313 }
314 }
316 DeviceCreateDesc* DeviceManagerImpl::CreateManagerDesc()
317 {
318 DeviceCreateDesc* managerDesc = new DeviceManagerCreateDesc(0);
319 if (managerDesc)
320 {
321 managerDesc->pLock = *new DeviceManagerLock;
322 }
323 return managerDesc;
324 }
326 bool DeviceManagerImpl::Initialize(DeviceBase* parent)
327 {
328 OVR_UNUSED(parent);
329 if (!pCreateDesc || !pCreateDesc->pLock)
330 return false;
332 pProfileManager = *ProfileManager::Create();
334 return true;
335 }
337 void DeviceManagerImpl::Shutdown()
338 {
339 // Remove all device descriptors from list while the lock is held.
340 // Some descriptors may survive longer due to handles.
341 while(!Devices.IsEmpty())
342 {
343 DeviceCreateDesc* devDesc = Devices.GetFirst();
344 OVR_ASSERT(!devDesc->pDevice); // Manager shouldn't be dying while Device exists.
345 devDesc->Enumerated = false;
346 devDesc->RemoveNode();
347 devDesc->pNext = devDesc->pPrev = 0;
349 if (devDesc->HandleCount == 0)
350 {
351 delete devDesc;
352 }
353 }
354 Devices.Clear();
356 // These must've been cleared by caller.
357 OVR_ASSERT(pCreateDesc->pDevice == 0);
358 OVR_ASSERT(pCreateDesc->pLock->pManager == 0);
360 pProfileManager.Clear();
361 }
364 // Callbacks for DeviceCreation/Release
365 DeviceBase* DeviceManagerImpl::CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent)
366 {
367 // Calls to DeviceManagerImpl::CreateDevice are enqueued with wait while holding pManager,
368 // so 'this' must remain valid.
369 OVR_ASSERT(createDesc->pLock->pManager);
371 Lock::Locker devicesLock(GetLock());
373 // If device already exists, just AddRef to it.
374 if (createDesc->pDevice)
375 {
376 createDesc->pDevice->AddRef();
377 return createDesc->pDevice;
378 }
380 if (!parent)
381 parent = this;
383 DeviceBase* device = createDesc->NewDeviceInstance();
385 if (device)
386 {
387 if (device->getDeviceCommon()->Initialize(parent))
388 {
389 createDesc->pDevice = device;
390 }
391 else
392 {
393 // Don't go through Release() to avoid PushCall behaviour,
394 // as it is not needed here.
395 delete device;
396 device = 0;
397 }
398 }
400 return device;
401 }
403 Void DeviceManagerImpl::ReleaseDevice_MgrThread(DeviceBase* device)
404 {
405 // descKeepAlive will keep ManagerLock object alive as well,
406 // allowing us to exit gracefully.
407 Ptr<DeviceCreateDesc> descKeepAlive;
408 Lock::Locker devicesLock(GetLock());
409 DeviceCommon* devCommon = device->getDeviceCommon();
411 while(1)
412 {
413 UInt32 refCount = devCommon->RefCount;
415 if (refCount > 1)
416 {
417 if (devCommon->RefCount.CompareAndSet_NoSync(refCount, refCount-1))
418 {
419 // We decreented from initial count higher then 1;
420 // nothing else to do.
421 return 0;
422 }
423 }
424 else if (devCommon->RefCount.CompareAndSet_NoSync(1, 0))
425 {
426 // { 1 -> 0 } decrement succeded. Destroy this device.
427 break;
428 }
429 }
431 // At this point, may be releasing the device manager itself.
432 // This does not matter, however, since shutdown logic is the same
433 // in both cases. DeviceManager::Shutdown with begin shutdown process for
434 // the internal manager thread, which will eventually destroy itself.
435 // TBD: Clean thread shutdown.
436 descKeepAlive = devCommon->pCreateDesc;
437 descKeepAlive->pDevice = 0;
438 devCommon->Shutdown();
439 delete device;
440 return 0;
441 }
445 Void DeviceManagerImpl::EnumerateAllFactoryDevices()
446 {
447 // 1. Mark matching devices as NOT enumerated.
448 // 2. Call factory to enumerate all HW devices, adding any device that
449 // was not matched.
450 // 3. Remove non-matching devices.
452 Lock::Locker deviceLock(GetLock());
454 DeviceCreateDesc* devDesc, *nextdevDesc;
456 // 1.
457 for(devDesc = Devices.GetFirst();
458 !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
459 {
460 //if (devDesc->pFactory == factory)
461 devDesc->Enumerated = false;
462 }
464 // 2.
465 DeviceFactory* factory = Factories.GetFirst();
466 while(!Factories.IsNull(factory))
467 {
468 EnumerateFactoryDevices(factory);
469 factory = factory->pNext;
470 }
473 // 3.
474 for(devDesc = Devices.GetFirst();
475 !Devices.IsNull(devDesc); devDesc = nextdevDesc)
476 {
477 // In case 'devDesc' gets removed.
478 nextdevDesc = devDesc->pNext;
480 // Note, device might be not enumerated since it is opened and
481 // in use! Do NOT notify 'device removed' in this case (!AB)
482 if (!devDesc->Enumerated)
483 {
484 // This deletes the devDesc for HandleCount == 0 due to Release in DeviceHandle.
485 CallOnDeviceRemoved(devDesc);
487 /*
488 if (devDesc->HandleCount == 0)
489 {
490 // Device must be dead if it ever existed, since it AddRefs to us.
491 // ~DeviceCreateDesc removes its node from list.
492 OVR_ASSERT(!devDesc->pDevice);
493 delete devDesc;
494 }
495 */
496 }
497 }
499 return 0;
500 }
502 Ptr<DeviceCreateDesc> DeviceManagerImpl::AddDevice_NeedsLock(
503 const DeviceCreateDesc& createDesc)
504 {
505 // If found, mark as enumerated and we are done.
506 DeviceCreateDesc* descCandidate = 0;
508 for(DeviceCreateDesc* devDesc = Devices.GetFirst();
509 !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
510 {
511 DeviceCreateDesc::MatchResult mr = devDesc->MatchDevice(createDesc, &descCandidate);
512 if (mr == DeviceCreateDesc::Match_Found)
513 {
514 devDesc->Enumerated = true;
515 if (!devDesc->pDevice)
516 CallOnDeviceAdded(devDesc);
517 return devDesc;
518 }
519 }
521 // Update candidate (this may involve writing fields to HMDDevice createDesc).
522 if (descCandidate)
523 {
524 bool newDevice = false;
525 if (descCandidate->UpdateMatchedCandidate(createDesc, &newDevice))
526 {
527 descCandidate->Enumerated = true;
528 if (!descCandidate->pDevice || newDevice)
529 CallOnDeviceAdded(descCandidate);
530 return descCandidate;
531 }
532 }
534 // If not found, add new device.
535 // - This stores a new descriptor with
536 // {pDevice = 0, HandleCount = 1, Enumerated = true}
537 DeviceCreateDesc* desc = createDesc.Clone();
538 desc->pLock = pCreateDesc->pLock;
539 Devices.PushBack(desc);
540 desc->Enumerated = true;
542 CallOnDeviceAdded(desc);
544 return desc;
545 }
547 Ptr<DeviceCreateDesc> DeviceManagerImpl::FindDevice(
548 const String& path,
549 DeviceType deviceType)
550 {
551 Lock::Locker deviceLock(GetLock());
552 DeviceCreateDesc* devDesc;
554 for (devDesc = Devices.GetFirst();
555 !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
556 {
557 if ((deviceType == Device_None || deviceType == devDesc->Type) &&
558 devDesc->MatchDevice(path))
559 return devDesc;
560 }
561 return NULL;
562 }
564 Ptr<DeviceCreateDesc> DeviceManagerImpl::FindHIDDevice(const HIDDeviceDesc& hidDevDesc)
565 {
566 Lock::Locker deviceLock(GetLock());
567 DeviceCreateDesc* devDesc;
569 for (devDesc = Devices.GetFirst();
570 !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
571 {
572 if (devDesc->MatchHIDDevice(hidDevDesc))
573 return devDesc;
574 }
575 return NULL;
576 }
578 void DeviceManagerImpl::DetectHIDDevice(const HIDDeviceDesc& hidDevDesc)
579 {
580 Lock::Locker deviceLock(GetLock());
581 DeviceFactory* factory = Factories.GetFirst();
582 while(!Factories.IsNull(factory))
583 {
584 if (factory->DetectHIDDevice(this, hidDevDesc))
585 break;
586 factory = factory->pNext;
587 }
589 }
591 // Enumerates devices for a particular factory.
592 Void DeviceManagerImpl::EnumerateFactoryDevices(DeviceFactory* factory)
593 {
595 class FactoryEnumerateVisitor : public DeviceFactory::EnumerateVisitor
596 {
597 DeviceManagerImpl* pManager;
598 DeviceFactory* pFactory;
599 public:
600 FactoryEnumerateVisitor(DeviceManagerImpl* manager, DeviceFactory* factory)
601 : pManager(manager), pFactory(factory) { }
603 virtual void Visit(const DeviceCreateDesc& createDesc)
604 {
605 pManager->AddDevice_NeedsLock(createDesc);
606 }
607 };
609 FactoryEnumerateVisitor newDeviceVisitor(this, factory);
610 factory->EnumerateDevices(newDeviceVisitor);
613 return 0;
614 }
617 DeviceEnumerator<> DeviceManagerImpl::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
618 {
619 Lock::Locker deviceLock(GetLock());
621 if (Devices.IsEmpty())
622 return DeviceEnumerator<>();
624 DeviceCreateDesc* firstDeviceDesc = Devices.GetFirst();
625 DeviceEnumerator<> e = enumeratorFromHandle(DeviceHandle(firstDeviceDesc), args);
627 if (!args.MatchRule(firstDeviceDesc->Type, firstDeviceDesc->Enumerated))
628 {
629 e.Next();
630 }
632 return e;
633 }
635 //-------------------------------------------------------------------------------------
636 // ***** DeviceCommon
638 void DeviceCommon::DeviceAddRef()
639 {
640 RefCount++;
641 }
643 void DeviceCommon::DeviceRelease()
644 {
645 while(1)
646 {
647 UInt32 refCount = RefCount;
648 OVR_ASSERT(refCount > 0);
650 if (refCount == 1)
651 {
652 DeviceManagerImpl* manager = pCreateDesc->GetManagerImpl();
653 ThreadCommandQueue* queue = manager->GetThreadQueue();
655 // Enqueue ReleaseDevice for {1 -> 0} transition with no wait.
656 // We pass our reference ownership into the queue to destroy.
657 // It's in theory possible for another thread to re-steal our device reference,
658 // but that is checked for atomically in DeviceManagerImpl::ReleaseDevice.
659 if (!queue->PushCall(manager, &DeviceManagerImpl::ReleaseDevice_MgrThread,
660 pCreateDesc->pDevice))
661 {
662 // PushCall shouldn't fail because background thread runs while manager is
663 // alive and we are holding Manager alive through pParent chain.
664 OVR_ASSERT(false);
665 }
667 // Warning! At his point everything, including manager, may be dead.
668 break;
669 }
670 else if (RefCount.CompareAndSet_NoSync(refCount, refCount-1))
671 {
672 break;
673 }
674 }
675 }
679 //-------------------------------------------------------------------------------------
680 // ***** DeviceCreateDesc
683 void DeviceCreateDesc::AddRef()
684 {
685 // Technically, HandleCount { 0 -> 1 } transition can only happen during Lock,
686 // but we leave this to caller to worry about (happens during enumeration).
687 HandleCount++;
688 }
690 void DeviceCreateDesc::Release()
691 {
692 while(1)
693 {
694 UInt32 handleCount = HandleCount;
695 // HandleCount must obviously be >= 1, since we are releasing it.
696 OVR_ASSERT(handleCount > 0);
698 // {1 -> 0} transition may cause us to be destroyed, so require a lock.
699 if (handleCount == 1)
700 {
701 Ptr<DeviceManagerLock> lockKeepAlive;
702 Lock::Locker deviceLockScope(GetLock());
704 if (!HandleCount.CompareAndSet_NoSync(handleCount, 0))
705 continue;
707 OVR_ASSERT(pDevice == 0);
709 // Destroy *this if the manager was destroyed already, or Enumerated
710 // is false (device no longer available).
711 if (!GetManagerImpl() || !Enumerated)
712 {
713 lockKeepAlive = pLock;
715 // Remove from manager list (only matters for !Enumerated).
716 if (pNext)
717 {
718 RemoveNode();
719 pNext = pPrev = 0;
720 }
722 delete this;
723 }
725 // Available DeviceCreateDesc may survive with { HandleCount == 0 },
726 // in case it might be enumerated again later.
727 break;
728 }
729 else if (HandleCount.CompareAndSet_NoSync(handleCount, handleCount-1))
730 {
731 break;
732 }
733 }
734 }
736 HMDDevice* HMDDevice::Disconnect(SensorDevice* psensor)
737 {
738 if (!psensor)
739 return NULL;
741 OVR::DeviceManager* manager = GetManager();
742 if (manager)
743 {
744 //DeviceManagerImpl* mgrImpl = static_cast<DeviceManagerImpl*>(manager);
745 Ptr<DeviceCreateDesc> desc = getDeviceCommon()->pCreateDesc;
746 if (desc)
747 {
748 class Visitor : public DeviceFactory::EnumerateVisitor
749 {
750 Ptr<DeviceCreateDesc> Desc;
751 public:
752 Visitor(DeviceCreateDesc* desc) : Desc(desc) {}
753 virtual void Visit(const DeviceCreateDesc& createDesc)
754 {
755 Lock::Locker lock(Desc->GetLock());
756 Desc->UpdateMatchedCandidate(createDesc);
757 }
758 } visitor(desc);
759 //SensorDeviceImpl* sImpl = static_cast<SensorDeviceImpl*>(psensor);
761 SensorDisplayInfoImpl displayInfo;
763 if (psensor->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
764 {
765 displayInfo.Unpack();
767 // If we got display info, try to match / create HMDDevice as well
768 // so that sensor settings give preference.
769 if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt)
770 {
771 SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, visitor);
772 }
773 }
774 }
775 }
776 return this;
777 }
779 bool HMDDevice::IsDisconnected() const
780 {
781 OVR::HMDInfo info;
782 GetDeviceInfo(&info);
783 // if strlen(info.DisplayDeviceName) == 0 then
784 // this HMD is 'fake' (created using sensor).
785 return (strlen(info.DisplayDeviceName) == 0);
786 }
789 } // namespace OVR