Home | History | Annotate | Download | only in browser
      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