// Copyright 2019 The Flutter team. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "win32_window.h" #include "resource.h" #include "shellscalingapi.h" namespace { // the Windows DPI system is based on this // constant for machines running at 100% scaling. constexpr int kBaseDpi = 96; constexpr LPCWSTR kClassName = L"CLASSNAME"; // Scale helper to convert logical scaler values to physical using passed in // scale factor int Scale(int source, double scale_factor) { return static_cast(source * scale_factor); } } // namespace Win32Window::Win32Window() {} Win32Window::~Win32Window() { Destroy(); } bool Win32Window::CreateAndShow(const std::wstring &title, const Point &origin, const Size &size) { Destroy(); WNDCLASS window_class = RegisterWindowClass(); HMONITOR defaut_monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY); UINT dpi_x = 0, dpi_y = 0; GetDpiForMonitor(defaut_monitor, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y); double scale_factor = static_cast(dpi_x) / kBaseDpi; HWND window = CreateWindow( window_class.lpszClassName, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), Scale(size.width, scale_factor), Scale(size.height, scale_factor), nullptr, nullptr, window_class.hInstance, this); return window != nullptr; } WNDCLASS Win32Window::RegisterWindowClass() { WNDCLASS window_class{}; window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); window_class.lpszClassName = kClassName; window_class.style = CS_HREDRAW | CS_VREDRAW; window_class.cbClsExtra = 0; window_class.cbWndExtra = 0; window_class.hInstance = GetModuleHandle(nullptr); window_class.hIcon = LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); window_class.hbrBackground = 0; window_class.lpszMenuName = nullptr; window_class.lpfnWndProc = WndProc; RegisterClass(&window_class); return window_class; } LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept { if (message == WM_NCCREATE) { auto cs = reinterpret_cast(lparam); SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast(cs->lpCreateParams)); auto that = static_cast(cs->lpCreateParams); that->window_handle_ = window; } else if (Win32Window *that = GetThisFromHandle(window)) { return that->MessageHandler(window, message, wparam, lparam); } return DefWindowProc(window, message, wparam, lparam); } LRESULT Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept { auto window = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); if (window == nullptr) { return 0; } switch (message) { case WM_DESTROY: window_handle_ = nullptr; Destroy(); return 0; case WM_SIZE: RECT rect; GetClientRect(hwnd, &rect); if (child_content_ != nullptr) { // Size and position the child window. MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); } return 0; case WM_ACTIVATE: if (child_content_ != nullptr) { SetFocus(child_content_); } return 0; // Messages that are directly forwarded to embedding. case WM_FONTCHANGE: SendMessage(child_content_, WM_FONTCHANGE, NULL, NULL); return 0; } return DefWindowProc(window_handle_, message, wparam, lparam); } void Win32Window::Destroy() { if (window_handle_) { DestroyWindow(window_handle_); window_handle_ = nullptr; } UnregisterClass(kClassName, nullptr); } Win32Window *Win32Window::GetThisFromHandle(HWND const window) noexcept { return reinterpret_cast( GetWindowLongPtr(window, GWLP_USERDATA)); } void Win32Window::SetChildContent(HWND content) { child_content_ = content; auto res = SetParent(content, window_handle_); RECT frame; GetClientRect(window_handle_, &frame); MoveWindow(content, frame.left, frame.top, frame.right - frame.left, frame.bottom - frame.top, true); SetFocus(child_content_); } HWND Win32Window::GetHandle() { return window_handle_; }