• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
DeviceNotifications(HWND hwnd)32   explicit DeviceNotifications(HWND hwnd) : notifications_() {
33     Register(hwnd);
34   }
35 
~DeviceNotifications()36   ~DeviceNotifications() {
37     Unregister();
38   }
39 
Register(HWND hwnd)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 
Unregister()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 
SystemMessageWindowWin()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 
~SystemMessageWindowWin()98 SystemMessageWindowWin::~SystemMessageWindowWin() {
99   if (window_) {
100     DestroyWindow(window_);
101     UnregisterClass(kWindowClassName, instance_);
102   }
103 }
104 
OnDeviceChange(UINT event_type,LPARAM data)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 
WndProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)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