1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/system_message_window_win.h" 6 7 #include <dbt.h> 8 9 #include "base/logging.h" 10 #include "base/system_monitor/system_monitor.h" 11 #include "base/win/wrapped_window_proc.h" 12 #include "media/audio/win/core_audio_util_win.h" 13 14 namespace content { 15 16 namespace { 17 const wchar_t kWindowClassName[] = L"Chrome_SystemMessageWindow"; 18 19 // A static map from a device category guid to base::SystemMonitor::DeviceType. 20 struct { 21 const GUID device_category; 22 const base::SystemMonitor::DeviceType device_type; 23 } const kDeviceCategoryMap[] = { 24 { KSCATEGORY_AUDIO, base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE }, 25 { KSCATEGORY_VIDEO, base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE }, 26 }; 27 } // namespace 28 29 // Manages the device notification handles for SystemMessageWindowWin. 30 class SystemMessageWindowWin::DeviceNotifications { 31 public: 32 explicit DeviceNotifications(HWND hwnd) : notifications_() { 33 Register(hwnd); 34 } 35 36 ~DeviceNotifications() { 37 Unregister(); 38 } 39 40 void Register(HWND hwnd) { 41 // Request to receive device notifications. All applications receive basic 42 // notifications via WM_DEVICECHANGE but in order to receive detailed device 43 // arrival and removal messages, we need to register. 44 DEV_BROADCAST_DEVICEINTERFACE filter = {0}; 45 filter.dbcc_size = sizeof(filter); 46 filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 47 bool core_audio_support = media::CoreAudioUtil::IsSupported(); 48 for (int i = 0; i < arraysize(kDeviceCategoryMap); ++i) { 49 // If CoreAudio is supported, AudioDeviceListenerWin will 50 // take care of monitoring audio devices. 51 if (core_audio_support && 52 KSCATEGORY_AUDIO == kDeviceCategoryMap[i].device_category) { 53 continue; 54 } 55 56 filter.dbcc_classguid = kDeviceCategoryMap[i].device_category; 57 DCHECK_EQ(notifications_[i], static_cast<HDEVNOTIFY>(NULL)); 58 notifications_[i] = RegisterDeviceNotification( 59 hwnd, &filter, DEVICE_NOTIFY_WINDOW_HANDLE); 60 DPLOG_IF(ERROR, !notifications_[i]) 61 << "RegisterDeviceNotification failed"; 62 } 63 } 64 65 void Unregister() { 66 for (int i = 0; i < arraysize(notifications_); ++i) { 67 if (notifications_[i]) { 68 UnregisterDeviceNotification(notifications_[i]); 69 notifications_[i] = NULL; 70 } 71 } 72 } 73 74 private: 75 HDEVNOTIFY notifications_[arraysize(kDeviceCategoryMap)]; 76 77 DISALLOW_IMPLICIT_CONSTRUCTORS(DeviceNotifications); 78 }; 79 80 81 SystemMessageWindowWin::SystemMessageWindowWin() { 82 WNDCLASSEX window_class; 83 base::win::InitializeWindowClass( 84 kWindowClassName, 85 &base::win::WrappedWindowProc<SystemMessageWindowWin::WndProcThunk>, 86 0, 0, 0, NULL, NULL, NULL, NULL, NULL, 87 &window_class); 88 instance_ = window_class.hInstance; 89 ATOM clazz = RegisterClassEx(&window_class); 90 DCHECK(clazz); 91 92 window_ = CreateWindow(kWindowClassName, 93 0, 0, 0, 0, 0, 0, 0, 0, instance_, 0); 94 SetWindowLongPtr(window_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this)); 95 device_notifications_.reset(new DeviceNotifications(window_)); 96 } 97 98 SystemMessageWindowWin::~SystemMessageWindowWin() { 99 if (window_) { 100 DestroyWindow(window_); 101 UnregisterClass(kWindowClassName, instance_); 102 } 103 } 104 105 LRESULT SystemMessageWindowWin::OnDeviceChange(UINT event_type, LPARAM data) { 106 base::SystemMonitor* monitor = base::SystemMonitor::Get(); 107 base::SystemMonitor::DeviceType device_type = 108 base::SystemMonitor::DEVTYPE_UNKNOWN; 109 switch (event_type) { 110 case DBT_DEVNODES_CHANGED: 111 // For this notification, we're happy with the default DEVTYPE_UNKNOWN. 112 break; 113 114 case DBT_DEVICEREMOVECOMPLETE: 115 case DBT_DEVICEARRIVAL: { 116 // This notification has more details about the specific device that 117 // was added or removed. See if this is a category we're interested 118 // in monitoring and if so report the specific device type. If we don't 119 // find the category in our map, ignore the notification and do not 120 // notify the system monitor. 121 DEV_BROADCAST_DEVICEINTERFACE* device_interface = 122 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data); 123 if (device_interface->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE) 124 return TRUE; 125 for (int i = 0; i < arraysize(kDeviceCategoryMap); ++i) { 126 if (kDeviceCategoryMap[i].device_category == 127 device_interface->dbcc_classguid) { 128 device_type = kDeviceCategoryMap[i].device_type; 129 break; 130 } 131 } 132 133 // Devices that we do not have a DEVTYPE_ for, get detected via 134 // DBT_DEVNODES_CHANGED, so we avoid sending additional notifications 135 // for those here. 136 if (device_type == base::SystemMonitor::DEVTYPE_UNKNOWN) 137 return TRUE; 138 break; 139 } 140 141 default: 142 return TRUE; 143 } 144 145 monitor->ProcessDevicesChanged(device_type); 146 147 return TRUE; 148 } 149 150 LRESULT CALLBACK SystemMessageWindowWin::WndProc(HWND hwnd, UINT message, 151 WPARAM wparam, LPARAM lparam) { 152 switch (message) { 153 case WM_DEVICECHANGE: 154 return OnDeviceChange(static_cast<UINT>(wparam), lparam); 155 default: 156 break; 157 } 158 159 return ::DefWindowProc(hwnd, message, wparam, lparam); 160 } 161 162 } // namespace content 163