oculus1
diff libovr/Src/win32/OVR_Win32_DeviceStatus.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/win32/OVR_Win32_DeviceStatus.cpp Sat Sep 14 16:14:59 2013 +0300 1.3 @@ -0,0 +1,350 @@ 1.4 +/************************************************************************************ 1.5 + 1.6 +Filename : OVR_Win32_DeviceStatus.cpp 1.7 +Content : Win32 implementation of DeviceStatus. 1.8 +Created : January 24, 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_Win32_DeviceStatus.h" 1.20 + 1.21 +#include "OVR_Win32_HIDDevice.h" 1.22 + 1.23 +#include "Kernel/OVR_Log.h" 1.24 + 1.25 +#include <dbt.h> 1.26 + 1.27 +namespace OVR { namespace Win32 { 1.28 + 1.29 +static TCHAR windowClassName[] = TEXT("LibOVR_DeviceStatus_WindowClass"); 1.30 + 1.31 +//------------------------------------------------------------------------------------- 1.32 +DeviceStatus::DeviceStatus(Notifier* const pClient) 1.33 + : pNotificationClient(pClient), LastTimerId(0) 1.34 +{ 1.35 +} 1.36 + 1.37 +bool DeviceStatus::Initialize() 1.38 +{ 1.39 + 1.40 + WNDCLASS wndClass; 1.41 + wndClass.style = CS_HREDRAW | CS_VREDRAW; 1.42 + wndClass.lpfnWndProc = WindowsMessageCallback; 1.43 + wndClass.cbClsExtra = 0; 1.44 + wndClass.cbWndExtra = 0; 1.45 + wndClass.hInstance = 0; 1.46 + wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); 1.47 + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); 1.48 + wndClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); 1.49 + wndClass.lpszMenuName = NULL; 1.50 + wndClass.lpszClassName = windowClassName; 1.51 + 1.52 + if (!RegisterClass(&wndClass)) 1.53 + { 1.54 + OVR_ASSERT_LOG(false, ("Failed to register window class.")); 1.55 + return false; 1.56 + } 1.57 + 1.58 + // We're going to create a 'message-only' window. This will be hidden, can't be enumerated etc. 1.59 + // To do this we supply 'HWND_MESSAGE' as the hWndParent. 1.60 + // http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only 1.61 + hMessageWindow = CreateWindow( windowClassName, 1.62 + windowClassName, 1.63 + WS_OVERLAPPEDWINDOW, 1.64 + CW_USEDEFAULT, 1.65 + CW_USEDEFAULT, 1.66 + CW_USEDEFAULT, 1.67 + CW_USEDEFAULT, 1.68 + HWND_MESSAGE, 1.69 + NULL, 1.70 + 0, 1.71 + this); // Pass this object via the CREATESTRUCT mechanism 1.72 + // so that we can attach it to the window user data. 1.73 + 1.74 + if (hMessageWindow == NULL) 1.75 + { 1.76 + OVR_ASSERT_LOG(false, ("Failed to create window.")); 1.77 + return false; 1.78 + } 1.79 + 1.80 + // According to MS, topmost windows receive WM_DEVICECHANGE faster. 1.81 + ::SetWindowPos(hMessageWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); 1.82 + UpdateWindow(hMessageWindow); 1.83 + 1.84 + 1.85 + // Register notification for additional HID messages. 1.86 + HIDDeviceManager* hidDeviceManager = new HIDDeviceManager(NULL); 1.87 + HidGuid = hidDeviceManager->GetHIDGuid(); 1.88 + hidDeviceManager->Release(); 1.89 + 1.90 + DEV_BROADCAST_DEVICEINTERFACE notificationFilter; 1.91 + 1.92 + ZeroMemory(¬ificationFilter, sizeof(notificationFilter)); 1.93 + notificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); 1.94 + notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 1.95 + //notificationFilter.dbcc_classguid = hidguid; 1.96 + 1.97 + // We need DEVICE_NOTIFY_ALL_INTERFACE_CLASSES to detect 1.98 + // HDMI plug/unplug events. 1.99 + hDeviceNotify = RegisterDeviceNotification( 1.100 + hMessageWindow, 1.101 + ¬ificationFilter, 1.102 + DEVICE_NOTIFY_ALL_INTERFACE_CLASSES|DEVICE_NOTIFY_WINDOW_HANDLE); 1.103 + 1.104 + if (hDeviceNotify == NULL) 1.105 + { 1.106 + OVR_ASSERT_LOG(false, ("Failed to register for device notifications.")); 1.107 + return false; 1.108 + } 1.109 + 1.110 + return true; 1.111 +} 1.112 + 1.113 +void DeviceStatus::ShutDown() 1.114 +{ 1.115 + OVR_ASSERT(hMessageWindow); 1.116 + 1.117 + if (!UnregisterDeviceNotification(hDeviceNotify)) 1.118 + { 1.119 + OVR_ASSERT_LOG(false, ("Failed to unregister device notification.")); 1.120 + } 1.121 + 1.122 + PostMessage(hMessageWindow, WM_CLOSE, 0, 0); 1.123 + 1.124 + while (hMessageWindow != NULL) 1.125 + { 1.126 + ProcessMessages(); 1.127 + Sleep(1); 1.128 + } 1.129 + 1.130 + if (!UnregisterClass(windowClassName, NULL)) 1.131 + { 1.132 + OVR_ASSERT_LOG(false, ("Failed to unregister window class.")); 1.133 + } 1.134 +} 1.135 + 1.136 +DeviceStatus::~DeviceStatus() 1.137 +{ 1.138 + OVR_ASSERT_LOG(hMessageWindow == NULL, ("Need to call 'ShutDown' from DeviceManagerThread.")); 1.139 +} 1.140 + 1.141 +void DeviceStatus::ProcessMessages() 1.142 +{ 1.143 + OVR_ASSERT_LOG(hMessageWindow != NULL, ("Need to call 'Initialize' before first use.")); 1.144 + 1.145 + MSG msg; 1.146 + 1.147 + // Note WM_DEVICECHANGED messages are dispatched but not retrieved by PeekMessage. 1.148 + // I think this is because they are pending, non-queued messages. 1.149 + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 1.150 + { 1.151 + TranslateMessage(&msg); 1.152 + DispatchMessage(&msg); 1.153 + } 1.154 +} 1.155 + 1.156 +bool DeviceStatus::MessageCallback(WORD messageType, const String& devicePath) 1.157 +{ 1.158 + bool rv = true; 1.159 + if (messageType == DBT_DEVICEARRIVAL) 1.160 + { 1.161 + rv = pNotificationClient->OnMessage(Notifier::DeviceAdded, devicePath); 1.162 + } 1.163 + else if (messageType == DBT_DEVICEREMOVECOMPLETE) 1.164 + { 1.165 + pNotificationClient->OnMessage(Notifier::DeviceRemoved, devicePath); 1.166 + } 1.167 + else 1.168 + { 1.169 + OVR_ASSERT(0); 1.170 + } 1.171 + return rv; 1.172 +} 1.173 + 1.174 +void DeviceStatus::CleanupRecoveryTimer(UPInt index) 1.175 +{ 1.176 + ::KillTimer(hMessageWindow, RecoveryTimers[index].TimerId); 1.177 + RecoveryTimers.RemoveAt(index); 1.178 +} 1.179 + 1.180 +DeviceStatus::RecoveryTimerDesc* 1.181 +DeviceStatus::FindRecoveryTimer(UINT_PTR timerId, UPInt* pindex) 1.182 +{ 1.183 + for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i) 1.184 + { 1.185 + RecoveryTimerDesc* pdesc = &RecoveryTimers[i]; 1.186 + if (pdesc->TimerId == timerId) 1.187 + { 1.188 + *pindex = i; 1.189 + return pdesc; 1.190 + } 1.191 + } 1.192 + return NULL; 1.193 +} 1.194 + 1.195 +void DeviceStatus::FindAndCleanupRecoveryTimer(const String& devicePath) 1.196 +{ 1.197 + for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i) 1.198 + { 1.199 + RecoveryTimerDesc* pdesc = &RecoveryTimers[i]; 1.200 + if (pdesc->DevicePath.CompareNoCase(devicePath)) 1.201 + { 1.202 + CleanupRecoveryTimer(i); 1.203 + break; 1.204 + } 1.205 + } 1.206 +} 1.207 + 1.208 +LRESULT CALLBACK DeviceStatus::WindowsMessageCallback( HWND hwnd, 1.209 + UINT message, 1.210 + WPARAM wParam, 1.211 + LPARAM lParam) 1.212 +{ 1.213 + switch (message) 1.214 + { 1.215 + case WM_CREATE: 1.216 + { 1.217 + // Setup window user data with device status object pointer. 1.218 + LPCREATESTRUCT create_struct = reinterpret_cast<LPCREATESTRUCT>(lParam); 1.219 + void *lpCreateParam = create_struct->lpCreateParams; 1.220 + DeviceStatus *pDeviceStatus = reinterpret_cast<DeviceStatus*>(lpCreateParam); 1.221 + 1.222 + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pDeviceStatus)); 1.223 + } 1.224 + return 0; // Return 0 for successfully handled WM_CREATE. 1.225 + 1.226 + case WM_DEVICECHANGE: 1.227 + { 1.228 + WORD loword = LOWORD(wParam); 1.229 + 1.230 + if (loword != DBT_DEVICEARRIVAL && 1.231 + loword != DBT_DEVICEREMOVECOMPLETE) 1.232 + { 1.233 + // Ignore messages other than device arrive and remove complete 1.234 + // (we're not handling intermediate ones). 1.235 + return TRUE; // Grant WM_DEVICECHANGE request. 1.236 + } 1.237 + 1.238 + DEV_BROADCAST_DEVICEINTERFACE* hdr; 1.239 + hdr = (DEV_BROADCAST_DEVICEINTERFACE*) lParam; 1.240 + 1.241 + if (hdr->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE) 1.242 + { 1.243 + // Ignore non interface device messages. 1.244 + return TRUE; // Grant WM_DEVICECHANGE request. 1.245 + } 1.246 + 1.247 + LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA); 1.248 + OVR_ASSERT(userData != NULL); 1.249 + 1.250 + // Call callback on device messages object with the device path. 1.251 + DeviceStatus* pDeviceStatus = (DeviceStatus*) userData; 1.252 + String devicePath(hdr->dbcc_name); 1.253 + 1.254 + // check if HID device caused the event... 1.255 + if (pDeviceStatus->HidGuid == hdr->dbcc_classguid) 1.256 + { 1.257 + // check if recovery timer is already running; stop it and 1.258 + // remove it, if so. 1.259 + pDeviceStatus->FindAndCleanupRecoveryTimer(devicePath); 1.260 + 1.261 + if (!pDeviceStatus->MessageCallback(loword, devicePath)) 1.262 + { 1.263 + // hmmm.... unsuccessful 1.264 + if (loword == DBT_DEVICEARRIVAL) 1.265 + { 1.266 + // Windows sometimes may return errors ERROR_SHARING_VIOLATION and 1.267 + // ERROR_FILE_NOT_FOUND when trying to open an USB device via 1.268 + // CreateFile. Need to start a recovery timer that will try to 1.269 + // re-open the device again. 1.270 + OVR_DEBUG_LOG(("Adding failed, recovering through a timer...")); 1.271 + UINT_PTR tid = ::SetTimer(hwnd, ++pDeviceStatus->LastTimerId, 1.272 + USBRecoveryTimeInterval, NULL); 1.273 + RecoveryTimerDesc rtDesc; 1.274 + rtDesc.TimerId = tid; 1.275 + rtDesc.DevicePath = devicePath; 1.276 + rtDesc.NumAttempts= 0; 1.277 + pDeviceStatus->RecoveryTimers.PushBack(rtDesc); 1.278 + // wrap around the timer counter, avoid timerId == 0... 1.279 + if (pDeviceStatus->LastTimerId + 1 == 0) 1.280 + pDeviceStatus->LastTimerId = 0; 1.281 + } 1.282 + } 1.283 + } 1.284 + // Check if Oculus HDMI device was plugged/unplugged, preliminary 1.285 + // filtering. (is there any way to get GUID? !AB) 1.286 + //else if (strstr(devicePath.ToCStr(), "DISPLAY#")) 1.287 + else if (strstr(devicePath.ToCStr(), "#OVR00")) 1.288 + { 1.289 + pDeviceStatus->MessageCallback(loword, devicePath); 1.290 + } 1.291 + } 1.292 + return TRUE; // Grant WM_DEVICECHANGE request. 1.293 + 1.294 + case WM_TIMER: 1.295 + { 1.296 + if (wParam != 0) 1.297 + { 1.298 + LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA); 1.299 + OVR_ASSERT(userData != NULL); 1.300 + 1.301 + // Call callback on device messages object with the device path. 1.302 + DeviceStatus* pDeviceStatus = (DeviceStatus*) userData; 1.303 + 1.304 + // Check if we have recovery timer running (actually, we must be!) 1.305 + UPInt rtIndex; 1.306 + RecoveryTimerDesc* prtDesc = pDeviceStatus->FindRecoveryTimer(wParam, &rtIndex); 1.307 + if (prtDesc) 1.308 + { 1.309 + if (pDeviceStatus->MessageCallback(DBT_DEVICEARRIVAL, prtDesc->DevicePath)) 1.310 + { 1.311 + OVR_DEBUG_LOG(("Recovered, adding is successful, cleaning up the timer...")); 1.312 + // now it is successful, kill the timer and cleanup 1.313 + pDeviceStatus->CleanupRecoveryTimer(rtIndex); 1.314 + } 1.315 + else 1.316 + { 1.317 + if (++prtDesc->NumAttempts >= MaxUSBRecoveryAttempts) 1.318 + { 1.319 + OVR_DEBUG_LOG(("Failed to recover USB after %d attempts, path = '%s', aborting...", 1.320 + prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr())); 1.321 + pDeviceStatus->CleanupRecoveryTimer(rtIndex); 1.322 + } 1.323 + else 1.324 + { 1.325 + OVR_DEBUG_LOG(("Failed to recover USB, %d attempts, path = '%s'", 1.326 + prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr())); 1.327 + } 1.328 + } 1.329 + } 1.330 + } 1.331 + } 1.332 + return 0; 1.333 + 1.334 + case WM_CLOSE: 1.335 + { 1.336 + LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA); 1.337 + OVR_ASSERT(userData != NULL); 1.338 + DeviceStatus* pDeviceStatus = (DeviceStatus*) userData; 1.339 + pDeviceStatus->hMessageWindow = NULL; 1.340 + 1.341 + DestroyWindow(hwnd); 1.342 + } 1.343 + return 0; // We processed the WM_CLOSE message. 1.344 + 1.345 + case WM_DESTROY: 1.346 + PostQuitMessage(0); 1.347 + return 0; // We processed the WM_DESTROY message. 1.348 + } 1.349 + 1.350 + return DefWindowProc(hwnd, message, wParam, lParam); 1.351 +} 1.352 + 1.353 +}} // namespace OVR::Win32