From 66a2efba4f5941f8988ae157ff2d185af26225be Mon Sep 17 00:00:00 2001 From: KR Tirtho Date: Sun, 9 Jan 2022 18:46:43 +0600 Subject: [PATCH] Windows initial Support added (player not working) --- windows/.gitignore | 17 ++ windows/CMakeLists.txt | 95 +++++++ windows/flutter/CMakeLists.txt | 103 ++++++++ .../flutter/generated_plugin_registrant.cc | 20 ++ windows/flutter/generated_plugin_registrant.h | 15 ++ windows/flutter/generated_plugins.cmake | 18 ++ windows/runner/CMakeLists.txt | 17 ++ windows/runner/Runner.rc | 121 +++++++++ windows/runner/flutter_window.cpp | 61 +++++ windows/runner/flutter_window.h | 33 +++ windows/runner/main.cpp | 46 ++++ windows/runner/resource.h | 16 ++ windows/runner/resources/icon.ico | Bin 0 -> 62794 bytes windows/runner/runner.exe.manifest | 20 ++ windows/runner/utils.cpp | 64 +++++ windows/runner/utils.h | 19 ++ windows/runner/win32_window.cpp | 245 ++++++++++++++++++ windows/runner/win32_window.h | 98 +++++++ 18 files changed, 1008 insertions(+) create mode 100644 windows/.gitignore create mode 100644 windows/CMakeLists.txt create mode 100644 windows/flutter/CMakeLists.txt create mode 100644 windows/flutter/generated_plugin_registrant.cc create mode 100644 windows/flutter/generated_plugin_registrant.h create mode 100644 windows/flutter/generated_plugins.cmake create mode 100644 windows/runner/CMakeLists.txt create mode 100644 windows/runner/Runner.rc create mode 100644 windows/runner/flutter_window.cpp create mode 100644 windows/runner/flutter_window.h create mode 100644 windows/runner/main.cpp create mode 100644 windows/runner/resource.h create mode 100644 windows/runner/resources/icon.ico create mode 100644 windows/runner/runner.exe.manifest create mode 100644 windows/runner/utils.cpp create mode 100644 windows/runner/utils.h create mode 100644 windows/runner/win32_window.cpp create mode 100644 windows/runner/win32_window.h diff --git a/windows/.gitignore b/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 00000000..6e1e3cb3 --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.14) +project(spotube LANGUAGES CXX) + +set(BINARY_NAME "spotube") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..b2e4bd8d --- /dev/null +++ b/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..f4a18032 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,20 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + BitsdojoWindowPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("BitsdojoWindowPlugin")); + HotkeyManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("HotkeyManagerPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); +} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..81650d05 --- /dev/null +++ b/windows/flutter/generated_plugins.cmake @@ -0,0 +1,18 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + bitsdojo_window_windows + hotkey_manager + url_launcher_windows +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..de2d8916 --- /dev/null +++ b/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc new file mode 100644 index 00000000..715cf84e --- /dev/null +++ b/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "A new Flutter project." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "spotube" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "spotube.exe" "\0" + VALUE "ProductName", "spotube" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..b43b9095 --- /dev/null +++ b/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp new file mode 100644 index 00000000..1ad6625c --- /dev/null +++ b/windows/runner/main.cpp @@ -0,0 +1,46 @@ +#include +auto bdw = bitsdojo_window_configure(BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP); + +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"spotube", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/windows/runner/resource.h b/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/windows/runner/resources/icon.ico b/windows/runner/resources/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ada0d7b957e139ff35ee77eb881e255758a31ddf GIT binary patch literal 62794 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ⅇz#?9zpi7ad;cg*f?SOWfvSYJzR4wm1SUf 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 D*GAFm literal 0 HcmV?d00001 diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..c977c4a4 --- /dev/null +++ b/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp new file mode 100644 index 00000000..d19bdbbc --- /dev/null +++ b/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/windows/runner/utils.h b/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp new file mode 100644 index 00000000..c10f08dc --- /dev/null +++ b/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// 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); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + 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 = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, 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, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(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 { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + 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; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h new file mode 100644 index 00000000..17ba4311 --- /dev/null +++ b/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_