system_message_window_win.cc 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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. #include "media/device_monitors/system_message_window_win.h"
  5. #include <dbt.h>
  6. #include <stddef.h>
  7. #include <memory>
  8. #include "base/logging.h"
  9. #include "base/no_destructor.h"
  10. #include "base/system/system_monitor.h"
  11. #include "base/win/wrapped_window_proc.h"
  12. #include "media/audio/win/core_audio_util_win.h"
  13. namespace media {
  14. namespace {
  15. const wchar_t kWindowClassName[] = L"Chrome_SystemMessageWindow";
  16. // A static map from a device category guid to base::SystemMonitor::DeviceType.
  17. struct DeviceCategoryToType {
  18. const GUID device_category;
  19. const base::SystemMonitor::DeviceType device_type;
  20. };
  21. const std::vector<DeviceCategoryToType>& GetDeviceCategoryToType() {
  22. static const base::NoDestructor<std::vector<DeviceCategoryToType>>
  23. device_category_to_type(
  24. {{KSCATEGORY_AUDIO, base::SystemMonitor::DEVTYPE_AUDIO},
  25. {KSCATEGORY_VIDEO, base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE}});
  26. return *device_category_to_type;
  27. }
  28. } // namespace
  29. // Manages the device notification handles for SystemMessageWindowWin.
  30. class SystemMessageWindowWin::DeviceNotifications {
  31. public:
  32. DeviceNotifications() = delete;
  33. explicit DeviceNotifications(HWND hwnd)
  34. : notifications_(std::size(GetDeviceCategoryToType())) {
  35. Register(hwnd);
  36. }
  37. DeviceNotifications(const DeviceNotifications&) = delete;
  38. DeviceNotifications& operator=(const DeviceNotifications&) = delete;
  39. ~DeviceNotifications() { Unregister(); }
  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 (size_t i = 0; i < GetDeviceCategoryToType().size(); ++i) {
  49. // If CoreAudio is supported, AudioDeviceListenerWin will
  50. // take care of monitoring audio devices.
  51. if (core_audio_support &&
  52. KSCATEGORY_AUDIO == GetDeviceCategoryToType()[i].device_category) {
  53. continue;
  54. }
  55. filter.dbcc_classguid = GetDeviceCategoryToType()[i].device_category;
  56. DCHECK_EQ(notifications_[i], static_cast<HDEVNOTIFY>(NULL));
  57. notifications_[i] = RegisterDeviceNotification(
  58. hwnd, &filter, DEVICE_NOTIFY_WINDOW_HANDLE);
  59. DPLOG_IF(ERROR, !notifications_[i])
  60. << "RegisterDeviceNotification failed";
  61. }
  62. }
  63. void Unregister() {
  64. for (size_t i = 0; i < notifications_.size(); ++i) {
  65. if (notifications_[i]) {
  66. UnregisterDeviceNotification(notifications_[i]);
  67. notifications_[i] = NULL;
  68. }
  69. }
  70. }
  71. private:
  72. std::vector<HDEVNOTIFY> notifications_;
  73. };
  74. SystemMessageWindowWin::SystemMessageWindowWin() {
  75. WNDCLASSEX window_class;
  76. base::win::InitializeWindowClass(
  77. kWindowClassName,
  78. &base::win::WrappedWindowProc<SystemMessageWindowWin::WndProcThunk>, 0, 0,
  79. 0, NULL, NULL, NULL, NULL, NULL, &window_class);
  80. instance_ = window_class.hInstance;
  81. ATOM clazz = RegisterClassEx(&window_class);
  82. DCHECK(clazz);
  83. window_ =
  84. CreateWindow(kWindowClassName, 0, 0, 0, 0, 0, 0, 0, 0, instance_, 0);
  85. SetWindowLongPtr(window_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
  86. device_notifications_ = std::make_unique<DeviceNotifications>(window_);
  87. }
  88. SystemMessageWindowWin::~SystemMessageWindowWin() {
  89. if (window_) {
  90. DestroyWindow(window_);
  91. UnregisterClass(kWindowClassName, instance_);
  92. }
  93. }
  94. LRESULT SystemMessageWindowWin::OnDeviceChange(UINT event_type, LPARAM data) {
  95. base::SystemMonitor* monitor = base::SystemMonitor::Get();
  96. base::SystemMonitor::DeviceType device_type =
  97. base::SystemMonitor::DEVTYPE_UNKNOWN;
  98. switch (event_type) {
  99. case DBT_DEVNODES_CHANGED:
  100. // For this notification, we're happy with the default DEVTYPE_UNKNOWN.
  101. break;
  102. case DBT_DEVICEREMOVECOMPLETE:
  103. case DBT_DEVICEARRIVAL: {
  104. // This notification has more details about the specific device that
  105. // was added or removed. See if this is a category we're interested
  106. // in monitoring and if so report the specific device type. If we don't
  107. // find the category in our map, ignore the notification and do not
  108. // notify the system monitor.
  109. DEV_BROADCAST_DEVICEINTERFACE* device_interface =
  110. reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
  111. if (device_interface->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
  112. return TRUE;
  113. for (const auto& map_entry : GetDeviceCategoryToType()) {
  114. if (map_entry.device_category == device_interface->dbcc_classguid) {
  115. device_type = map_entry.device_type;
  116. break;
  117. }
  118. }
  119. // Devices that we do not have a DEVTYPE_ for, get detected via
  120. // DBT_DEVNODES_CHANGED, so we avoid sending additional notifications
  121. // for those here.
  122. if (device_type == base::SystemMonitor::DEVTYPE_UNKNOWN)
  123. return TRUE;
  124. break;
  125. }
  126. default:
  127. return TRUE;
  128. }
  129. monitor->ProcessDevicesChanged(device_type);
  130. return TRUE;
  131. }
  132. LRESULT CALLBACK SystemMessageWindowWin::WndProc(HWND hwnd,
  133. UINT message,
  134. WPARAM wparam,
  135. LPARAM lparam) {
  136. switch (message) {
  137. case WM_DEVICECHANGE:
  138. return OnDeviceChange(static_cast<UINT>(wparam), lparam);
  139. default:
  140. break;
  141. }
  142. return ::DefWindowProc(hwnd, message, wparam, lparam);
  143. }
  144. } // namespace media