// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "flutter/shell/platform/windows/win32_window.h" namespace flutter { Win32Window::Win32Window() { // Assume Windows 10 1703 or greater for DPI handling. When running on a // older release of Windows where this context doesn't exist, DPI calls will // fail and Flutter rendering will be impacted until this is fixed. // To handle downlevel correctly, dpi_helper must use the most recent DPI // context available should be used: Windows 1703: Per-Monitor V2, 8.1: // Per-Monitor V1, Windows 7: System See // https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows // for more information. // TODO the calling applicaiton should participate in setting the DPI. // Currently dpi_helper is asserting per-monitor V2. There are two problems // with this: 1) it is advised that the awareness mode is set using manifest, // not programatically. 2) The calling executable should be responsible for // setting an appropriate scaling mode, not a library. This will be // particularly important once there is a means of hosting Flutter content in // an existing app. BOOL result = dpi_helper_->SetProcessDpiAwarenessContext( DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); if (result != TRUE) { OutputDebugString(L"Failed to set PMV2"); } } Win32Window::~Win32Window() { Destroy(); } void Win32Window::Initialize(const char* title, const unsigned int x, const unsigned int y, const unsigned int width, const unsigned int height) { Destroy(); std::wstring converted_title = NarrowToWide(title); WNDCLASS window_class = ResgisterWindowClass(converted_title); CreateWindow(window_class.lpszClassName, converted_title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, width, height, nullptr, nullptr, window_class.hInstance, this); } std::wstring Win32Window::NarrowToWide(const char* source) { size_t length = strlen(source); size_t outlen = 0; std::wstring wideTitle(length, L'#'); mbstowcs_s(&outlen, &wideTitle[0], length + 1, source, length); return wideTitle; } WNDCLASS Win32Window::ResgisterWindowClass(std::wstring& title) { window_class_name_ = title; WNDCLASS window_class{}; window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); window_class.lpszClassName = title.c_str(); window_class.style = CS_HREDRAW | CS_VREDRAW; window_class.cbClsExtra = 0; window_class.cbWndExtra = 0; window_class.hInstance = GetModuleHandle(nullptr); window_class.hIcon = nullptr; 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); // Since the application is running in Per-monitor V2 mode, turn on // automatic titlebar scaling BOOL result = that->dpi_helper_->EnableNonClientDpiScaling(window); if (result != TRUE) { OutputDebugString(L"Failed to enable non-client area autoscaling"); } that->current_dpi_ = that->dpi_helper_->GetDpiForWindow(window); 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 { int xPos = 0, yPos = 0; UINT width = 0, height = 0; auto window = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); if (window != nullptr) { switch (message) { case WM_DPICHANGED: return HandleDpiChange(window_handle_, wparam, lparam); break; case WM_DESTROY: window->OnClose(); return 0; break; case WM_SIZE: width = LOWORD(lparam); height = HIWORD(lparam); current_width_ = width; current_height_ = height; window->HandleResize(width, height); break; case WM_MOUSEMOVE: xPos = GET_X_LPARAM(lparam); yPos = GET_Y_LPARAM(lparam); window->OnPointerMove(static_cast(xPos), static_cast(yPos)); break; case WM_LBUTTONDOWN: xPos = GET_X_LPARAM(lparam); yPos = GET_Y_LPARAM(lparam); window->OnPointerDown(static_cast(xPos), static_cast(yPos)); break; case WM_LBUTTONUP: xPos = GET_X_LPARAM(lparam); yPos = GET_Y_LPARAM(lparam); window->OnPointerUp(static_cast(xPos), static_cast(yPos)); break; case WM_MOUSEWHEEL: window->OnScroll( 0.0, -(static_cast(HIWORD(wparam)) / (double)WHEEL_DELTA)); break; case WM_CHAR: case WM_SYSCHAR: case WM_UNICHAR: if (wparam != VK_BACK) { window->OnChar(static_cast(wparam)); } break; case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: unsigned char scancode = ((unsigned char*)&lparam)[2]; unsigned int virtualKey = MapVirtualKey(scancode, MAPVK_VSC_TO_VK); const int key = virtualKey; const int action = message == WM_KEYDOWN ? WM_KEYDOWN : WM_KEYUP; window->OnKey(key, scancode, action, 0); break; } return DefWindowProc(hwnd, message, wparam, lparam); } return DefWindowProc(window_handle_, message, wparam, lparam); } UINT Win32Window::GetCurrentDPI() { return current_dpi_; } UINT Win32Window::GetCurrentWidth() { return current_width_; } UINT Win32Window::GetCurrentHeight() { return current_height_; } HWND Win32Window::GetWindowHandle() { return window_handle_; } void Win32Window::Destroy() { if (window_handle_) { DestroyWindow(window_handle_); window_handle_ = nullptr; } UnregisterClass(window_class_name_.c_str(), nullptr); } // DPI Change handler. on WM_DPICHANGE resize the window LRESULT Win32Window::HandleDpiChange(HWND hwnd, WPARAM wparam, LPARAM lparam) { if (hwnd != nullptr) { auto window = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); UINT uDpi = HIWORD(wparam); current_dpi_ = uDpi; window->OnDpiScale(uDpi); // Resize the window auto lprcNewScale = reinterpret_cast(lparam); LONG newWidth = lprcNewScale->right - lprcNewScale->left; LONG newHeight = lprcNewScale->bottom - lprcNewScale->top; SetWindowPos(hwnd, nullptr, lprcNewScale->left, lprcNewScale->top, newWidth, newHeight, SWP_NOZORDER | SWP_NOACTIVATE); } return 0; } void Win32Window::HandleResize(UINT width, UINT height) { current_width_ = width; current_height_ = height; OnResize(width, height); } Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { return reinterpret_cast( GetWindowLongPtr(window, GWLP_USERDATA)); } } // namespace flutter