electron_api_system_preferences_win.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Copyright (c) 2014 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include <iomanip>
  5. #include <string_view>
  6. #include <dwmapi.h>
  7. #include <windows.devices.enumeration.h>
  8. #include <wrl/client.h>
  9. #include "shell/browser/api/electron_api_system_preferences.h"
  10. #include "base/containers/fixed_flat_map.h"
  11. #include "base/win/core_winrt_util.h"
  12. #include "base/win/windows_types.h"
  13. #include "base/win/wrapped_window_proc.h"
  14. #include "shell/common/color_util.h"
  15. #include "shell/common/process_util.h"
  16. #include "ui/gfx/color_utils.h"
  17. #include "ui/gfx/win/hwnd_util.h"
  18. namespace electron {
  19. namespace {
  20. const wchar_t kSystemPreferencesWindowClass[] =
  21. L"Electron_SystemPreferencesHostWindow";
  22. using ABI::Windows::Devices::Enumeration::DeviceAccessStatus;
  23. using ABI::Windows::Devices::Enumeration::DeviceClass;
  24. using ABI::Windows::Devices::Enumeration::IDeviceAccessInformation;
  25. using ABI::Windows::Devices::Enumeration::IDeviceAccessInformationStatics;
  26. using Microsoft::WRL::ComPtr;
  27. DeviceAccessStatus GetDeviceAccessStatus(DeviceClass device_class) {
  28. ComPtr<IDeviceAccessInformationStatics> dev_access_info_statics;
  29. HRESULT hr = base::win::GetActivationFactory<
  30. IDeviceAccessInformationStatics,
  31. RuntimeClass_Windows_Devices_Enumeration_DeviceAccessInformation>(
  32. &dev_access_info_statics);
  33. if (FAILED(hr)) {
  34. VLOG(1) << "IDeviceAccessInformationStatics failed: " << hr;
  35. return DeviceAccessStatus::DeviceAccessStatus_Allowed;
  36. }
  37. ComPtr<IDeviceAccessInformation> dev_access_info;
  38. hr = dev_access_info_statics->CreateFromDeviceClass(device_class,
  39. &dev_access_info);
  40. if (FAILED(hr)) {
  41. VLOG(1) << "IDeviceAccessInformation failed: " << hr;
  42. return DeviceAccessStatus::DeviceAccessStatus_Allowed;
  43. }
  44. auto status = DeviceAccessStatus::DeviceAccessStatus_Unspecified;
  45. dev_access_info->get_CurrentStatus(&status);
  46. return status;
  47. }
  48. std::string ConvertDeviceAccessStatus(DeviceAccessStatus value) {
  49. switch (value) {
  50. case DeviceAccessStatus::DeviceAccessStatus_Unspecified:
  51. return "not-determined";
  52. case DeviceAccessStatus::DeviceAccessStatus_Allowed:
  53. return "granted";
  54. case DeviceAccessStatus::DeviceAccessStatus_DeniedBySystem:
  55. return "restricted";
  56. case DeviceAccessStatus::DeviceAccessStatus_DeniedByUser:
  57. return "denied";
  58. default:
  59. return "unknown";
  60. }
  61. }
  62. } // namespace
  63. namespace api {
  64. std::string hexColorDWORDToRGBA(DWORD color) {
  65. DWORD rgba = color << 8 | color >> 24;
  66. std::ostringstream stream;
  67. stream << std::hex << std::setw(8) << std::setfill('0') << rgba;
  68. return stream.str();
  69. }
  70. std::string SystemPreferences::GetAccentColor() {
  71. DWORD color = 0;
  72. BOOL opaque = FALSE;
  73. if (FAILED(DwmGetColorizationColor(&color, &opaque))) {
  74. return "";
  75. }
  76. return hexColorDWORDToRGBA(color);
  77. }
  78. std::string SystemPreferences::GetColor(gin_helper::ErrorThrower thrower,
  79. const std::string& color) {
  80. static constexpr auto Lookup = base::MakeFixedFlatMap<std::string_view, int>({
  81. {"3d-dark-shadow", COLOR_3DDKSHADOW},
  82. {"3d-face", COLOR_3DFACE},
  83. {"3d-highlight", COLOR_3DHIGHLIGHT},
  84. {"3d-light", COLOR_3DLIGHT},
  85. {"3d-shadow", COLOR_3DSHADOW},
  86. {"active-border", COLOR_ACTIVEBORDER},
  87. {"active-caption", COLOR_ACTIVECAPTION},
  88. {"active-caption-gradient", COLOR_GRADIENTACTIVECAPTION},
  89. {"app-workspace", COLOR_APPWORKSPACE},
  90. {"button-text", COLOR_BTNTEXT},
  91. {"caption-text", COLOR_CAPTIONTEXT},
  92. {"desktop", COLOR_DESKTOP},
  93. {"disabled-text", COLOR_GRAYTEXT},
  94. {"highlight", COLOR_HIGHLIGHT},
  95. {"highlight-text", COLOR_HIGHLIGHTTEXT},
  96. {"hotlight", COLOR_HOTLIGHT},
  97. {"inactive-border", COLOR_INACTIVEBORDER},
  98. {"inactive-caption", COLOR_INACTIVECAPTION},
  99. {"inactive-caption-gradient", COLOR_GRADIENTINACTIVECAPTION},
  100. {"inactive-caption-text", COLOR_INACTIVECAPTIONTEXT},
  101. {"info-background", COLOR_INFOBK},
  102. {"info-text", COLOR_INFOTEXT},
  103. {"menu", COLOR_MENU},
  104. {"menu-highlight", COLOR_MENUHILIGHT},
  105. {"menu-text", COLOR_MENUTEXT},
  106. {"menubar", COLOR_MENUBAR},
  107. {"scrollbar", COLOR_SCROLLBAR},
  108. {"window", COLOR_WINDOW},
  109. {"window-frame", COLOR_WINDOWFRAME},
  110. {"window-text", COLOR_WINDOWTEXT},
  111. });
  112. if (const auto* iter = Lookup.find(color); iter != Lookup.end())
  113. return ToRGBAHex(color_utils::GetSysSkColor(iter->second));
  114. thrower.ThrowError("Unknown color: " + color);
  115. return "";
  116. }
  117. std::string SystemPreferences::GetMediaAccessStatus(
  118. gin_helper::ErrorThrower thrower,
  119. const std::string& media_type) {
  120. if (media_type == "camera") {
  121. return ConvertDeviceAccessStatus(
  122. GetDeviceAccessStatus(DeviceClass::DeviceClass_VideoCapture));
  123. } else if (media_type == "microphone") {
  124. return ConvertDeviceAccessStatus(
  125. GetDeviceAccessStatus(DeviceClass::DeviceClass_AudioCapture));
  126. } else if (media_type == "screen") {
  127. return ConvertDeviceAccessStatus(
  128. DeviceAccessStatus::DeviceAccessStatus_Allowed);
  129. } else {
  130. thrower.ThrowError("Invalid media type");
  131. return {};
  132. }
  133. }
  134. void SystemPreferences::InitializeWindow() {
  135. if (electron::IsUtilityProcess())
  136. return;
  137. // Wait until app is ready before creating sys color listener
  138. // Creating this listener before the app is ready causes global shortcuts
  139. // to not fire
  140. if (Browser::Get()->is_ready())
  141. color_change_listener_ =
  142. std::make_unique<gfx::ScopedSysColorChangeListener>(this);
  143. else
  144. Browser::Get()->AddObserver(this);
  145. WNDCLASSEX window_class;
  146. base::win::InitializeWindowClass(
  147. kSystemPreferencesWindowClass,
  148. &base::win::WrappedWindowProc<SystemPreferences::WndProcStatic>, 0, 0, 0,
  149. nullptr, nullptr, nullptr, nullptr, nullptr, &window_class);
  150. instance_ = window_class.hInstance;
  151. atom_ = RegisterClassEx(&window_class);
  152. // Create an offscreen window for receiving broadcast messages for the system
  153. // colorization color. Create a hidden WS_POPUP window instead of an
  154. // HWND_MESSAGE window, because only top-level windows such as popups can
  155. // receive broadcast messages like "WM_DWMCOLORIZATIONCOLORCHANGED".
  156. window_ = CreateWindow(MAKEINTATOM(atom_), 0, WS_POPUP, 0, 0, 0, 0, 0, 0,
  157. instance_, 0);
  158. gfx::CheckWindowCreated(window_, ::GetLastError());
  159. gfx::SetWindowUserData(window_, this);
  160. }
  161. LRESULT CALLBACK SystemPreferences::WndProcStatic(HWND hwnd,
  162. UINT message,
  163. WPARAM wparam,
  164. LPARAM lparam) {
  165. auto* msg_wnd = reinterpret_cast<SystemPreferences*>(
  166. GetWindowLongPtr(hwnd, GWLP_USERDATA));
  167. if (msg_wnd)
  168. return msg_wnd->WndProc(hwnd, message, wparam, lparam);
  169. else
  170. return ::DefWindowProc(hwnd, message, wparam, lparam);
  171. }
  172. LRESULT CALLBACK SystemPreferences::WndProc(HWND hwnd,
  173. UINT message,
  174. WPARAM wparam,
  175. LPARAM lparam) {
  176. if (message == WM_DWMCOLORIZATIONCOLORCHANGED) {
  177. DWORD new_color = (DWORD)wparam;
  178. std::string new_color_string = hexColorDWORDToRGBA(new_color);
  179. if (new_color_string != current_color_) {
  180. Emit("accent-color-changed", hexColorDWORDToRGBA(new_color));
  181. current_color_ = new_color_string;
  182. }
  183. }
  184. return ::DefWindowProc(hwnd, message, wparam, lparam);
  185. }
  186. void SystemPreferences::OnSysColorChange() {
  187. Emit("color-changed");
  188. }
  189. void SystemPreferences::OnFinishLaunching(base::Value::Dict launch_info) {
  190. color_change_listener_ =
  191. std::make_unique<gfx::ScopedSysColorChangeListener>(this);
  192. }
  193. } // namespace api
  194. } // namespace electron