| // 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<CREATESTRUCT*>(lparam); |
| SetWindowLongPtr(window, GWLP_USERDATA, |
| reinterpret_cast<LONG_PTR>(cs->lpCreateParams)); |
| |
| auto that = static_cast<Win32Window*>(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<Win32Window*>(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<double>(xPos), |
| static_cast<double>(yPos)); |
| break; |
| case WM_LBUTTONDOWN: |
| xPos = GET_X_LPARAM(lparam); |
| yPos = GET_Y_LPARAM(lparam); |
| window->OnPointerDown(static_cast<double>(xPos), |
| static_cast<double>(yPos)); |
| break; |
| case WM_LBUTTONUP: |
| xPos = GET_X_LPARAM(lparam); |
| yPos = GET_Y_LPARAM(lparam); |
| window->OnPointerUp(static_cast<double>(xPos), |
| static_cast<double>(yPos)); |
| break; |
| case WM_MOUSEWHEEL: |
| window->OnScroll( |
| 0.0, -(static_cast<short>(HIWORD(wparam)) / (double)WHEEL_DELTA)); |
| break; |
| case WM_CHAR: |
| case WM_SYSCHAR: |
| case WM_UNICHAR: |
| if (wparam != VK_BACK) { |
| window->OnChar(static_cast<unsigned int>(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<Win32Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); |
| |
| UINT uDpi = HIWORD(wparam); |
| current_dpi_ = uDpi; |
| window->OnDpiScale(uDpi); |
| |
| // Resize the window |
| auto lprcNewScale = reinterpret_cast<RECT*>(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<Win32Window*>( |
| GetWindowLongPtr(window, GWLP_USERDATA)); |
| } |
| |
| } // namespace flutter |