From 5dd5dbb3d8420a060cda3bcb6fe3311d0f3c3904 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 17 Sep 2016 02:00:22 +1000 Subject: [PATCH 1/6] Add a systemPreferences API method to fetch the system ColorizationColor This is useful for automatical app theming * Fetches the color from the registry * Returns an empty string if the fetch fails (a falsey value) --- .../api/atom_api_system_preferences.cc | 14 +-- .../browser/api/atom_api_system_preferences.h | 30 ++++++ .../api/atom_api_system_preferences_win.cc | 93 +++++++++++++++++++ filenames.gypi | 1 + 4 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 atom/browser/api/atom_api_system_preferences_win.cc diff --git a/atom/browser/api/atom_api_system_preferences.cc b/atom/browser/api/atom_api_system_preferences.cc index ba6c9720da6e..f0b966e86a87 100644 --- a/atom/browser/api/atom_api_system_preferences.cc +++ b/atom/browser/api/atom_api_system_preferences.cc @@ -9,27 +9,20 @@ #include "atom/common/node_includes.h" #include "native_mate/dictionary.h" -#if defined(OS_WIN) -#include "ui/base/win/shell.h" -#endif - namespace atom { namespace api { SystemPreferences::SystemPreferences(v8::Isolate* isolate) { Init(isolate); + #if defined(OS_WIN) + InitializeWindow(); + #endif } SystemPreferences::~SystemPreferences() { } -#if defined(OS_WIN) -bool SystemPreferences::IsAeroGlassEnabled() { - return ui::win::IsAeroGlassEnabled(); -} -#endif - #if !defined(OS_MACOSX) bool SystemPreferences::IsDarkMode() { return false; @@ -49,6 +42,7 @@ void SystemPreferences::BuildPrototype( mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) #if defined(OS_WIN) .SetMethod("isAeroGlassEnabled", &SystemPreferences::IsAeroGlassEnabled) + .SetMethod("getAccentColor", &SystemPreferences::GetAccentColor) #elif defined(OS_MACOSX) .SetMethod("postNotification", &SystemPreferences::PostNotification) diff --git a/atom/browser/api/atom_api_system_preferences.h b/atom/browser/api/atom_api_system_preferences.h index f855ab0ebb3d..e257dc518a31 100644 --- a/atom/browser/api/atom_api_system_preferences.h +++ b/atom/browser/api/atom_api_system_preferences.h @@ -29,6 +29,17 @@ class SystemPreferences : public mate::EventEmitter { #if defined(OS_WIN) bool IsAeroGlassEnabled(); + + typedef HRESULT (STDAPICALLTYPE *DwmGetColorizationColor)(DWORD *, BOOL *); + DwmGetColorizationColor dwmGetColorizationColor = + (DwmGetColorizationColor) GetProcAddress(LoadLibraryW(L"dwmapi.dll"), + "DwmGetColorizationColor"); + + std::string GetAccentColor(); + + void InitializeWindow(); + + #elif defined(OS_MACOSX) using NotificationCallback = base::Callback< void(const std::string&, const base::DictionaryValue&)>; @@ -64,6 +75,25 @@ class SystemPreferences : public mate::EventEmitter { #endif private: +#if defined(OS_WIN) + // Static callback invoked when a message comes in to our messaging window. + static LRESULT CALLBACK + WndProcStatic(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + + LRESULT CALLBACK + WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + + // The window class of |window_|. + ATOM atom_; + + // The handle of the module that contains the window procedure of |window_|. + HMODULE instance_; + + // The window used for processing events. + HWND window_; + + std::string current_color_; +#endif DISALLOW_COPY_AND_ASSIGN(SystemPreferences); }; diff --git a/atom/browser/api/atom_api_system_preferences_win.cc b/atom/browser/api/atom_api_system_preferences_win.cc new file mode 100644 index 000000000000..0c8e96a8a9c3 --- /dev/null +++ b/atom/browser/api/atom_api_system_preferences_win.cc @@ -0,0 +1,93 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_system_preferences.h" + +#include "base/win/wrapped_window_proc.h" +#include "ui/base/win/shell.h" +#include "ui/gfx/win/hwnd_util.h" + +namespace atom { + +namespace { + +const wchar_t kSystemPreferencestWindowClass[] = + L"Electron_SystemPreferencesHostWindow"; + +} // namespace + +namespace api { + +bool SystemPreferences::IsAeroGlassEnabled() { + return ui::win::IsAeroGlassEnabled(); +} + +std::string hexColorDWORDToRGBA(DWORD color) { + std::ostringstream stream; + stream << std::hex << color; + std::string hexColor = stream.str(); + return hexColor.substr(2) + hexColor.substr(0, 2); +} + +std::string SystemPreferences::GetAccentColor() { + DWORD color = 0; + BOOL opaque = FALSE; + + if (FAILED(dwmGetColorizationColor(&color, &opaque))) { + return ""; + } + + return hexColorDWORDToRGBA(color); +} + +void SystemPreferences::InitializeWindow() { + WNDCLASSEX window_class; + base::win::InitializeWindowClass( + kSystemPreferencestWindowClass, + &base::win::WrappedWindowProc, + 0, 0, 0, NULL, NULL, NULL, NULL, NULL, + &window_class); + instance_ = window_class.hInstance; + atom_ = RegisterClassEx(&window_class); + + // Create an offscreen window for receiving broadcast messages for the system + // colorization color. Create a hidden WS_POPUP window instead of an + // HWND_MESSAGE window, because only top-level windows such as popups can + // receive broadcast messages like "WM_DWMCOLORIZATIONCOLORCHANGED". + window_ = CreateWindow(MAKEINTATOM(atom_), + 0, WS_POPUP, 0, 0, 0, 0, 0, 0, instance_, 0); + gfx::CheckWindowCreated(window_); + gfx::SetWindowUserData(window_, this); +} + +LRESULT CALLBACK SystemPreferences::WndProcStatic(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + SystemPreferences* msg_wnd = reinterpret_cast( + GetWindowLongPtr(hwnd, GWLP_USERDATA)); + if (msg_wnd) + return msg_wnd->WndProc(hwnd, message, wparam, lparam); + else + return ::DefWindowProc(hwnd, message, wparam, lparam); +} + +LRESULT CALLBACK SystemPreferences::WndProc(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + if (message == WM_DWMCOLORIZATIONCOLORCHANGED) { + DWORD new_color = (DWORD) wparam; + std::string new_color_string = hexColorDWORDToRGBA(new_color); + if (new_color_string != current_color_) { + Emit("accent-color-changed", hexColorDWORDToRGBA(new_color)); + current_color_ = new_color_string; + } + } + return ::DefWindowProc(hwnd, message, wparam, lparam); +} + +} // namespace api + +} // namespace atom diff --git a/filenames.gypi b/filenames.gypi index 1f4c9e440f2c..88a8dc0510e1 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -127,6 +127,7 @@ 'atom/browser/api/atom_api_system_preferences.cc', 'atom/browser/api/atom_api_system_preferences.h', 'atom/browser/api/atom_api_system_preferences_mac.mm', + 'atom/browser/api/atom_api_system_preferences_win.cc', 'atom/browser/api/atom_api_tray.cc', 'atom/browser/api/atom_api_tray.h', 'atom/browser/api/atom_api_web_contents.cc', From c0f8b4895df90c30c8c6f787159ffb014e4597bb Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 17 Sep 2016 02:08:41 +1000 Subject: [PATCH 2/6] Update docs --- docs/api/system-preferences.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/api/system-preferences.md b/docs/api/system-preferences.md index 3224ba33b3ce..6788d07017b4 100644 --- a/docs/api/system-preferences.md +++ b/docs/api/system-preferences.md @@ -7,6 +7,19 @@ const {systemPreferences} = require('electron') console.log(systemPreferences.isDarkMode()) ``` +## Events + +The `systemPreferences` object emits the following events: + +### Event: 'accent-color-changed' _Windows_ + +Returns: + +* `event` Event +* `newColor` String - The new RGBA color the user assigned to be there system +accent color. + + ## Methods ### `systemPreferences.isDarkMode()` _macOS_ @@ -124,3 +137,16 @@ if (browserOptions.transparent) { ``` [dwm-composition]:https://msdn.microsoft.com/en-us/library/windows/desktop/aa969540.aspx + +### `SystemPreferences.getAccentColor()` _Windows_ + +Returns the users current system wide color preference in the form of an RGBA +hexadecimal string. + +```js +const color = app.getSystemAccentColor() // `"aabbccdd"` +const red = color.substr(0, 2) // "aa" +const green = color.substr(2, 2) // "bb" +const blue = color.substr(4, 2) // "cc" +const alpha = color.substr(6, 2) // "dd" +``` From 14545105491f4c78792e86c4b9bed4b1ca334004 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 17 Sep 2016 02:09:28 +1000 Subject: [PATCH 3/6] Alphabetical method ordering --- atom/browser/api/atom_api_system_preferences.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atom/browser/api/atom_api_system_preferences.cc b/atom/browser/api/atom_api_system_preferences.cc index f0b966e86a87..6d3897b197be 100644 --- a/atom/browser/api/atom_api_system_preferences.cc +++ b/atom/browser/api/atom_api_system_preferences.cc @@ -41,8 +41,8 @@ void SystemPreferences::BuildPrototype( prototype->SetClassName(mate::StringToV8(isolate, "SystemPreferences")); mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) #if defined(OS_WIN) - .SetMethod("isAeroGlassEnabled", &SystemPreferences::IsAeroGlassEnabled) .SetMethod("getAccentColor", &SystemPreferences::GetAccentColor) + .SetMethod("isAeroGlassEnabled", &SystemPreferences::IsAeroGlassEnabled) #elif defined(OS_MACOSX) .SetMethod("postNotification", &SystemPreferences::PostNotification) From 9f946527929d65607c0d42d33733d370fee27f83 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 17 Sep 2016 02:13:09 +1000 Subject: [PATCH 4/6] Add basic spec for getAccentColor --- spec/api-system-preferences-spec.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/spec/api-system-preferences-spec.js b/spec/api-system-preferences-spec.js index be7009ae6bf1..80f33bddbbed 100644 --- a/spec/api-system-preferences-spec.js +++ b/spec/api-system-preferences-spec.js @@ -3,11 +3,23 @@ const {remote} = require('electron') const {systemPreferences} = remote describe('systemPreferences module', function () { - if (process.platform !== 'darwin') { - return - } + describe('systemPreferences.getAccentColor', function () { + if (process.platform !== 'win32') { + return + } + + it('should return a non-empty string', function () { + let accentColor = systemPreferences.getAccentColor(); + assert.notEqual(accentColor, null); + assert(accentColor.length > 0); + }) + }) describe('systemPreferences.getUserDefault(key, type)', function () { + if (process.platform !== 'darwin') { + return + } + it('returns values for known user defaults', function () { let locale = systemPreferences.getUserDefault('AppleLocale', 'string') assert.notEqual(locale, null) From ba98109d33b41496c722b22030488000f3e00398 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 17 Sep 2016 02:27:46 +1000 Subject: [PATCH 5/6] Remove semicolons from spec --- spec/api-system-preferences-spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/api-system-preferences-spec.js b/spec/api-system-preferences-spec.js index 80f33bddbbed..16992c31e112 100644 --- a/spec/api-system-preferences-spec.js +++ b/spec/api-system-preferences-spec.js @@ -9,9 +9,9 @@ describe('systemPreferences module', function () { } it('should return a non-empty string', function () { - let accentColor = systemPreferences.getAccentColor(); - assert.notEqual(accentColor, null); - assert(accentColor.length > 0); + let accentColor = systemPreferences.getAccentColor() + assert.notEqual(accentColor, null) + assert(accentColor.length > 0) }) }) From 456f102cdc159f2b29837dbbf9124feef8c23b21 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 20 Sep 2016 02:42:41 +1000 Subject: [PATCH 6/6] Fix k typo --- atom/browser/api/atom_api_system_preferences_win.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/atom/browser/api/atom_api_system_preferences_win.cc b/atom/browser/api/atom_api_system_preferences_win.cc index 0c8e96a8a9c3..454c8ae24f7f 100644 --- a/atom/browser/api/atom_api_system_preferences_win.cc +++ b/atom/browser/api/atom_api_system_preferences_win.cc @@ -12,7 +12,7 @@ namespace atom { namespace { -const wchar_t kSystemPreferencestWindowClass[] = +const wchar_t kSystemPreferencesWindowClass[] = L"Electron_SystemPreferencesHostWindow"; } // namespace @@ -44,7 +44,7 @@ std::string SystemPreferences::GetAccentColor() { void SystemPreferences::InitializeWindow() { WNDCLASSEX window_class; base::win::InitializeWindowClass( - kSystemPreferencestWindowClass, + kSystemPreferencesWindowClass, &base::win::WrappedWindowProc, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, &window_class);