languages

A collection of programs made with different programming languages.
git clone git://evanalba.com/languages
Log | Files | Refs

win32_window.cpp (8534B)


      1 #include "win32_window.h"
      2 
      3 #include <dwmapi.h>
      4 #include <flutter_windows.h>
      5 
      6 #include "resource.h"
      7 
      8 namespace {
      9 
     10 /// Window attribute that enables dark mode window decorations.
     11 ///
     12 /// Redefined in case the developer's machine has a Windows SDK older than
     13 /// version 10.0.22000.0.
     14 /// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
     15 #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
     16 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
     17 #endif
     18 
     19 constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
     20 
     21 /// Registry key for app theme preference.
     22 ///
     23 /// A value of 0 indicates apps should use dark mode. A non-zero or missing
     24 /// value indicates apps should use light mode.
     25 constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
     26   L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
     27 constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
     28 
     29 // The number of Win32Window objects that currently exist.
     30 static int g_active_window_count = 0;
     31 
     32 using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
     33 
     34 // Scale helper to convert logical scaler values to physical using passed in
     35 // scale factor
     36 int Scale(int source, double scale_factor) {
     37   return static_cast<int>(source * scale_factor);
     38 }
     39 
     40 // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
     41 // This API is only needed for PerMonitor V1 awareness mode.
     42 void EnableFullDpiSupportIfAvailable(HWND hwnd) {
     43   HMODULE user32_module = LoadLibraryA("User32.dll");
     44   if (!user32_module) {
     45     return;
     46   }
     47   auto enable_non_client_dpi_scaling =
     48       reinterpret_cast<EnableNonClientDpiScaling*>(
     49           GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
     50   if (enable_non_client_dpi_scaling != nullptr) {
     51     enable_non_client_dpi_scaling(hwnd);
     52   }
     53   FreeLibrary(user32_module);
     54 }
     55 
     56 }  // namespace
     57 
     58 // Manages the Win32Window's window class registration.
     59 class WindowClassRegistrar {
     60  public:
     61   ~WindowClassRegistrar() = default;
     62 
     63   // Returns the singleton registrar instance.
     64   static WindowClassRegistrar* GetInstance() {
     65     if (!instance_) {
     66       instance_ = new WindowClassRegistrar();
     67     }
     68     return instance_;
     69   }
     70 
     71   // Returns the name of the window class, registering the class if it hasn't
     72   // previously been registered.
     73   const wchar_t* GetWindowClass();
     74 
     75   // Unregisters the window class. Should only be called if there are no
     76   // instances of the window.
     77   void UnregisterWindowClass();
     78 
     79  private:
     80   WindowClassRegistrar() = default;
     81 
     82   static WindowClassRegistrar* instance_;
     83 
     84   bool class_registered_ = false;
     85 };
     86 
     87 WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
     88 
     89 const wchar_t* WindowClassRegistrar::GetWindowClass() {
     90   if (!class_registered_) {
     91     WNDCLASS window_class{};
     92     window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
     93     window_class.lpszClassName = kWindowClassName;
     94     window_class.style = CS_HREDRAW | CS_VREDRAW;
     95     window_class.cbClsExtra = 0;
     96     window_class.cbWndExtra = 0;
     97     window_class.hInstance = GetModuleHandle(nullptr);
     98     window_class.hIcon =
     99         LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
    100     window_class.hbrBackground = 0;
    101     window_class.lpszMenuName = nullptr;
    102     window_class.lpfnWndProc = Win32Window::WndProc;
    103     RegisterClass(&window_class);
    104     class_registered_ = true;
    105   }
    106   return kWindowClassName;
    107 }
    108 
    109 void WindowClassRegistrar::UnregisterWindowClass() {
    110   UnregisterClass(kWindowClassName, nullptr);
    111   class_registered_ = false;
    112 }
    113 
    114 Win32Window::Win32Window() {
    115   ++g_active_window_count;
    116 }
    117 
    118 Win32Window::~Win32Window() {
    119   --g_active_window_count;
    120   Destroy();
    121 }
    122 
    123 bool Win32Window::Create(const std::wstring& title,
    124                          const Point& origin,
    125                          const Size& size) {
    126   Destroy();
    127 
    128   const wchar_t* window_class =
    129       WindowClassRegistrar::GetInstance()->GetWindowClass();
    130 
    131   const POINT target_point = {static_cast<LONG>(origin.x),
    132                               static_cast<LONG>(origin.y)};
    133   HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
    134   UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
    135   double scale_factor = dpi / 96.0;
    136 
    137   HWND window = CreateWindow(
    138       window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
    139       Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
    140       Scale(size.width, scale_factor), Scale(size.height, scale_factor),
    141       nullptr, nullptr, GetModuleHandle(nullptr), this);
    142 
    143   if (!window) {
    144     return false;
    145   }
    146 
    147   UpdateTheme(window);
    148 
    149   return OnCreate();
    150 }
    151 
    152 bool Win32Window::Show() {
    153   return ShowWindow(window_handle_, SW_SHOWNORMAL);
    154 }
    155 
    156 // static
    157 LRESULT CALLBACK Win32Window::WndProc(HWND const window,
    158                                       UINT const message,
    159                                       WPARAM const wparam,
    160                                       LPARAM const lparam) noexcept {
    161   if (message == WM_NCCREATE) {
    162     auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
    163     SetWindowLongPtr(window, GWLP_USERDATA,
    164                      reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
    165 
    166     auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
    167     EnableFullDpiSupportIfAvailable(window);
    168     that->window_handle_ = window;
    169   } else if (Win32Window* that = GetThisFromHandle(window)) {
    170     return that->MessageHandler(window, message, wparam, lparam);
    171   }
    172 
    173   return DefWindowProc(window, message, wparam, lparam);
    174 }
    175 
    176 LRESULT
    177 Win32Window::MessageHandler(HWND hwnd,
    178                             UINT const message,
    179                             WPARAM const wparam,
    180                             LPARAM const lparam) noexcept {
    181   switch (message) {
    182     case WM_DESTROY:
    183       window_handle_ = nullptr;
    184       Destroy();
    185       if (quit_on_close_) {
    186         PostQuitMessage(0);
    187       }
    188       return 0;
    189 
    190     case WM_DPICHANGED: {
    191       auto newRectSize = reinterpret_cast<RECT*>(lparam);
    192       LONG newWidth = newRectSize->right - newRectSize->left;
    193       LONG newHeight = newRectSize->bottom - newRectSize->top;
    194 
    195       SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
    196                    newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
    197 
    198       return 0;
    199     }
    200     case WM_SIZE: {
    201       RECT rect = GetClientArea();
    202       if (child_content_ != nullptr) {
    203         // Size and position the child window.
    204         MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
    205                    rect.bottom - rect.top, TRUE);
    206       }
    207       return 0;
    208     }
    209 
    210     case WM_ACTIVATE:
    211       if (child_content_ != nullptr) {
    212         SetFocus(child_content_);
    213       }
    214       return 0;
    215 
    216     case WM_DWMCOLORIZATIONCOLORCHANGED:
    217       UpdateTheme(hwnd);
    218       return 0;
    219   }
    220 
    221   return DefWindowProc(window_handle_, message, wparam, lparam);
    222 }
    223 
    224 void Win32Window::Destroy() {
    225   OnDestroy();
    226 
    227   if (window_handle_) {
    228     DestroyWindow(window_handle_);
    229     window_handle_ = nullptr;
    230   }
    231   if (g_active_window_count == 0) {
    232     WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
    233   }
    234 }
    235 
    236 Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
    237   return reinterpret_cast<Win32Window*>(
    238       GetWindowLongPtr(window, GWLP_USERDATA));
    239 }
    240 
    241 void Win32Window::SetChildContent(HWND content) {
    242   child_content_ = content;
    243   SetParent(content, window_handle_);
    244   RECT frame = GetClientArea();
    245 
    246   MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
    247              frame.bottom - frame.top, true);
    248 
    249   SetFocus(child_content_);
    250 }
    251 
    252 RECT Win32Window::GetClientArea() {
    253   RECT frame;
    254   GetClientRect(window_handle_, &frame);
    255   return frame;
    256 }
    257 
    258 HWND Win32Window::GetHandle() {
    259   return window_handle_;
    260 }
    261 
    262 void Win32Window::SetQuitOnClose(bool quit_on_close) {
    263   quit_on_close_ = quit_on_close;
    264 }
    265 
    266 bool Win32Window::OnCreate() {
    267   // No-op; provided for subclasses.
    268   return true;
    269 }
    270 
    271 void Win32Window::OnDestroy() {
    272   // No-op; provided for subclasses.
    273 }
    274 
    275 void Win32Window::UpdateTheme(HWND const window) {
    276   DWORD light_mode;
    277   DWORD light_mode_size = sizeof(light_mode);
    278   LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
    279                                kGetPreferredBrightnessRegValue,
    280                                RRF_RT_REG_DWORD, nullptr, &light_mode,
    281                                &light_mode_size);
    282 
    283   if (result == ERROR_SUCCESS) {
    284     BOOL enable_dark_mode = light_mode == 0;
    285     DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
    286                           &enable_dark_mode, sizeof(enable_dark_mode));
    287   }
    288 }