00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "OVR_Win32_DeviceStatus.h"
00017
00018 #include "OVR_Win32_HIDDevice.h"
00019
00020 #include "Kernel/OVR_Log.h"
00021
00022 #include <dbt.h>
00023
00024 namespace OVR { namespace Win32 {
00025
00026 static TCHAR windowClassName[] = TEXT("LibOVR_DeviceStatus_WindowClass");
00027
00028
00029 DeviceStatus::DeviceStatus(Notifier* const pClient)
00030 : pNotificationClient(pClient), LastTimerId(0)
00031 {
00032 }
00033
00034 bool DeviceStatus::Initialize()
00035 {
00036
00037 WNDCLASS wndClass;
00038 wndClass.style = CS_HREDRAW | CS_VREDRAW;
00039 wndClass.lpfnWndProc = WindowsMessageCallback;
00040 wndClass.cbClsExtra = 0;
00041 wndClass.cbWndExtra = 0;
00042 wndClass.hInstance = 0;
00043 wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
00044 wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
00045 wndClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
00046 wndClass.lpszMenuName = NULL;
00047 wndClass.lpszClassName = windowClassName;
00048
00049 if (!RegisterClass(&wndClass))
00050 {
00051 OVR_ASSERT_LOG(false, ("Failed to register window class."));
00052 return false;
00053 }
00054
00055
00056
00057
00058 hMessageWindow = CreateWindow( windowClassName,
00059 windowClassName,
00060 WS_OVERLAPPEDWINDOW,
00061 CW_USEDEFAULT,
00062 CW_USEDEFAULT,
00063 CW_USEDEFAULT,
00064 CW_USEDEFAULT,
00065 HWND_MESSAGE,
00066 NULL,
00067 0,
00068 this);
00069
00070
00071 if (hMessageWindow == NULL)
00072 {
00073 OVR_ASSERT_LOG(false, ("Failed to create window."));
00074 return false;
00075 }
00076
00077
00078 ::SetWindowPos(hMessageWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
00079 UpdateWindow(hMessageWindow);
00080
00081
00082
00083 HIDDeviceManager* hidDeviceManager = new HIDDeviceManager(NULL);
00084 HidGuid = hidDeviceManager->GetHIDGuid();
00085 hidDeviceManager->Release();
00086
00087 DEV_BROADCAST_DEVICEINTERFACE notificationFilter;
00088
00089 ZeroMemory(¬ificationFilter, sizeof(notificationFilter));
00090 notificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
00091 notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
00092
00093
00094
00095
00096 hDeviceNotify = RegisterDeviceNotification(
00097 hMessageWindow,
00098 ¬ificationFilter,
00099 DEVICE_NOTIFY_ALL_INTERFACE_CLASSES|DEVICE_NOTIFY_WINDOW_HANDLE);
00100
00101 if (hDeviceNotify == NULL)
00102 {
00103 OVR_ASSERT_LOG(false, ("Failed to register for device notifications."));
00104 return false;
00105 }
00106
00107 return true;
00108 }
00109
00110 void DeviceStatus::ShutDown()
00111 {
00112 OVR_ASSERT(hMessageWindow);
00113
00114 if (!UnregisterDeviceNotification(hDeviceNotify))
00115 {
00116 OVR_ASSERT_LOG(false, ("Failed to unregister device notification."));
00117 }
00118
00119 PostMessage(hMessageWindow, WM_CLOSE, 0, 0);
00120
00121 while (hMessageWindow != NULL)
00122 {
00123 ProcessMessages();
00124 Sleep(1);
00125 }
00126
00127 if (!UnregisterClass(windowClassName, NULL))
00128 {
00129 OVR_ASSERT_LOG(false, ("Failed to unregister window class."));
00130 }
00131 }
00132
00133 DeviceStatus::~DeviceStatus()
00134 {
00135 OVR_ASSERT_LOG(hMessageWindow == NULL, ("Need to call 'ShutDown' from DeviceManagerThread."));
00136 }
00137
00138 void DeviceStatus::ProcessMessages()
00139 {
00140 OVR_ASSERT_LOG(hMessageWindow != NULL, ("Need to call 'Initialize' before first use."));
00141
00142 MSG msg;
00143
00144
00145
00146 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
00147 {
00148 TranslateMessage(&msg);
00149 DispatchMessage(&msg);
00150 }
00151 }
00152
00153 bool DeviceStatus::MessageCallback(WORD messageType, const String& devicePath)
00154 {
00155 bool rv = true;
00156 if (messageType == DBT_DEVICEARRIVAL)
00157 {
00158 rv = pNotificationClient->OnMessage(Notifier::DeviceAdded, devicePath);
00159 }
00160 else if (messageType == DBT_DEVICEREMOVECOMPLETE)
00161 {
00162 pNotificationClient->OnMessage(Notifier::DeviceRemoved, devicePath);
00163 }
00164 else
00165 {
00166 OVR_ASSERT(0);
00167 }
00168 return rv;
00169 }
00170
00171 void DeviceStatus::CleanupRecoveryTimer(UPInt index)
00172 {
00173 ::KillTimer(hMessageWindow, RecoveryTimers[index].TimerId);
00174 RecoveryTimers.RemoveAt(index);
00175 }
00176
00177 DeviceStatus::RecoveryTimerDesc*
00178 DeviceStatus::FindRecoveryTimer(UINT_PTR timerId, UPInt* pindex)
00179 {
00180 for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i)
00181 {
00182 RecoveryTimerDesc* pdesc = &RecoveryTimers[i];
00183 if (pdesc->TimerId == timerId)
00184 {
00185 *pindex = i;
00186 return pdesc;
00187 }
00188 }
00189 return NULL;
00190 }
00191
00192 void DeviceStatus::FindAndCleanupRecoveryTimer(const String& devicePath)
00193 {
00194 for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i)
00195 {
00196 RecoveryTimerDesc* pdesc = &RecoveryTimers[i];
00197 if (pdesc->DevicePath.CompareNoCase(devicePath))
00198 {
00199 CleanupRecoveryTimer(i);
00200 break;
00201 }
00202 }
00203 }
00204
00205 LRESULT CALLBACK DeviceStatus::WindowsMessageCallback( HWND hwnd,
00206 UINT message,
00207 WPARAM wParam,
00208 LPARAM lParam)
00209 {
00210 switch (message)
00211 {
00212 case WM_CREATE:
00213 {
00214
00215 LPCREATESTRUCT create_struct = reinterpret_cast<LPCREATESTRUCT>(lParam);
00216 void *lpCreateParam = create_struct->lpCreateParams;
00217 DeviceStatus *pDeviceStatus = reinterpret_cast<DeviceStatus*>(lpCreateParam);
00218
00219 SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pDeviceStatus));
00220 }
00221 return 0;
00222
00223 case WM_DEVICECHANGE:
00224 {
00225 WORD loword = LOWORD(wParam);
00226
00227 if (loword != DBT_DEVICEARRIVAL &&
00228 loword != DBT_DEVICEREMOVECOMPLETE)
00229 {
00230
00231
00232 return TRUE;
00233 }
00234
00235 DEV_BROADCAST_DEVICEINTERFACE* hdr;
00236 hdr = (DEV_BROADCAST_DEVICEINTERFACE*) lParam;
00237
00238 if (hdr->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
00239 {
00240
00241 return TRUE;
00242 }
00243
00244 LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA);
00245 OVR_ASSERT(userData != NULL);
00246
00247
00248 DeviceStatus* pDeviceStatus = (DeviceStatus*) userData;
00249 String devicePath(hdr->dbcc_name);
00250
00251
00252 if (pDeviceStatus->HidGuid == hdr->dbcc_classguid)
00253 {
00254
00255
00256 pDeviceStatus->FindAndCleanupRecoveryTimer(devicePath);
00257
00258 if (!pDeviceStatus->MessageCallback(loword, devicePath))
00259 {
00260
00261 if (loword == DBT_DEVICEARRIVAL)
00262 {
00263
00264
00265
00266
00267 OVR_DEBUG_LOG(("Adding failed, recovering through a timer..."));
00268 UINT_PTR tid = ::SetTimer(hwnd, ++pDeviceStatus->LastTimerId,
00269 USBRecoveryTimeInterval, NULL);
00270 RecoveryTimerDesc rtDesc;
00271 rtDesc.TimerId = tid;
00272 rtDesc.DevicePath = devicePath;
00273 rtDesc.NumAttempts= 0;
00274 pDeviceStatus->RecoveryTimers.PushBack(rtDesc);
00275
00276 if (pDeviceStatus->LastTimerId + 1 == 0)
00277 pDeviceStatus->LastTimerId = 0;
00278 }
00279 }
00280 }
00281
00282
00283
00284 else if (strstr(devicePath.ToCStr(), "#OVR00"))
00285 {
00286 pDeviceStatus->MessageCallback(loword, devicePath);
00287 }
00288 }
00289 return TRUE;
00290
00291 case WM_TIMER:
00292 {
00293 if (wParam != 0)
00294 {
00295 LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA);
00296 OVR_ASSERT(userData != NULL);
00297
00298
00299 DeviceStatus* pDeviceStatus = (DeviceStatus*) userData;
00300
00301
00302 UPInt rtIndex;
00303 RecoveryTimerDesc* prtDesc = pDeviceStatus->FindRecoveryTimer(wParam, &rtIndex);
00304 if (prtDesc)
00305 {
00306 if (pDeviceStatus->MessageCallback(DBT_DEVICEARRIVAL, prtDesc->DevicePath))
00307 {
00308 OVR_DEBUG_LOG(("Recovered, adding is successful, cleaning up the timer..."));
00309
00310 pDeviceStatus->CleanupRecoveryTimer(rtIndex);
00311 }
00312 else
00313 {
00314 if (++prtDesc->NumAttempts >= MaxUSBRecoveryAttempts)
00315 {
00316 OVR_DEBUG_LOG(("Failed to recover USB after %d attempts, path = '%s', aborting...",
00317 prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr()));
00318 pDeviceStatus->CleanupRecoveryTimer(rtIndex);
00319 }
00320 else
00321 {
00322 OVR_DEBUG_LOG(("Failed to recover USB, %d attempts, path = '%s'",
00323 prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr()));
00324 }
00325 }
00326 }
00327 }
00328 }
00329 return 0;
00330
00331 case WM_CLOSE:
00332 {
00333 LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA);
00334 OVR_ASSERT(userData != NULL);
00335 DeviceStatus* pDeviceStatus = (DeviceStatus*) userData;
00336 pDeviceStatus->hMessageWindow = NULL;
00337
00338 DestroyWindow(hwnd);
00339 }
00340 return 0;
00341
00342 case WM_DESTROY:
00343 PostQuitMessage(0);
00344 return 0;
00345 }
00346
00347 return DefWindowProc(hwnd, message, wParam, lParam);
00348 }
00349
00350 }}