diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5c0f033..07860ce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,6 +4,12 @@ image: variables: GIT_SUBMODULE_STRATEGY: recursive +.shared_windows_runners: + tags: + - shared-windows + - windows + - windows-1809 + stages: - coverage - publish @@ -44,6 +50,28 @@ build_web: paths: - build/web/ +build_windows: + extends: + - .shared_windows_runners + stage: publish + script: + # Install chocolately + - Set-ExecutionPolicy Bypass -Scope Process + - Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) + - choco install git -y + - choco install visualstudio2019community -y --package-parameters "--add Microsoft.VisualStudio.Product.BuildTools --includeRecommended --includeOptional --passive --locale en-US" + - cd ..; git clone https://github.com/flutter/flutter.git -b dev; $env:path += ";C:\GitLab-Runner\builds\ChristianPauly\flutter\bin"; cd fluffychat-flutter + - flutter doctor + - flutter config --enable-windows-desktop + - flutter build windows + needs: [] + artifacts: + paths: + - build/windows/runner/Release/ + name: "Binairies" + only: + - main + - tags build_android_debug: stage: coverage diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..449a9f9 --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/windows/flutter/.template_version b/windows/flutter/.template_version deleted file mode 100644 index 7ed6ff8..0000000 --- a/windows/flutter/.template_version +++ /dev/null @@ -1 +0,0 @@ -5 diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 98bb564..c7a8c76 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -34,8 +34,8 @@ add_dependencies(flutter flutter_assemble) # === Wrapper === list(APPEND CPP_WRAPPER_SOURCES_CORE - "engine_method_result.cc" - "standard_codec.cc" + "core_implementations.cc" + "standard_codec.cc" ) list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") list(APPEND CPP_WRAPPER_SOURCES_PLUGIN @@ -80,11 +80,13 @@ add_dependencies(flutter_wrapper_app flutter_assemble) # _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} - ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + ${PHONY_OUTPUT} COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt index 83e5aca..977e38b 100644 --- a/windows/runner/CMakeLists.txt +++ b/windows/runner/CMakeLists.txt @@ -7,12 +7,12 @@ add_executable(${BINARY_NAME} WIN32 "run_loop.cpp" "utils.cpp" "win32_window.cpp" - "window_configuration.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 index 7d86499..ed2b95f 100644 --- a/windows/runner/Runner.rc +++ b/windows/runner/Runner.rc @@ -54,6 +54,57 @@ END // remains consistent on all systems. IDI_APP_ICON ICON "resources\\app_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", "chat.fluffy" "\0" + VALUE "FileDescription", "A new Flutter project." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "Fluffychat" "\0" + VALUE "LegalCopyright", "Copyright (C) 2020 chat.fluffy. All rights reserved." "\0" + VALUE "OriginalFilename", "fluffychat.exe" "\0" + VALUE "ProductName", "Fluffychat" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp index fe980cf..c422723 100644 --- a/windows/runner/flutter_window.cpp +++ b/windows/runner/flutter_window.cpp @@ -1,5 +1,7 @@ #include "flutter_window.h" +#include + #include "flutter/generated_plugin_registrant.h" FlutterWindow::FlutterWindow(RunLoop* run_loop, @@ -8,22 +10,55 @@ FlutterWindow::FlutterWindow(RunLoop* run_loop, FlutterWindow::~FlutterWindow() {} -void FlutterWindow::OnCreate() { - Win32Window::OnCreate(); +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } - // The size here is arbitrary since SetChildContent will resize it. - flutter_controller_ = - std::make_unique(100, 100, project_); - RegisterPlugins(flutter_controller_.get()); - run_loop_->RegisterFlutterInstance(flutter_controller_.get()); + 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()); + run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; } void FlutterWindow::OnDestroy() { if (flutter_controller_) { - run_loop_->UnregisterFlutterInstance(flutter_controller_.get()); + run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); 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 opporutunity 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 index 4f41e16..b663ddd 100644 --- a/windows/runner/flutter_window.h +++ b/windows/runner/flutter_window.h @@ -1,14 +1,14 @@ -#ifndef FLUTTER_WINDOW_H_ -#define FLUTTER_WINDOW_H_ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ #include #include +#include + #include "run_loop.h" #include "win32_window.h" -#include - // A window that does nothing but host a Flutter view. class FlutterWindow : public Win32Window { public: @@ -20,8 +20,10 @@ class FlutterWindow : public Win32Window { protected: // Win32Window: - void OnCreate() override; + bool OnCreate() override; void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; private: // The run loop driving events for this window. @@ -34,4 +36,4 @@ class FlutterWindow : public Win32Window { std::unique_ptr flutter_controller_; }; -#endif // FLUTTER_WINDOW_H_ +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp index 11b48e9..68fd370 100644 --- a/windows/runner/main.cpp +++ b/windows/runner/main.cpp @@ -5,7 +5,6 @@ #include "flutter_window.h" #include "run_loop.h" #include "utils.h" -#include "window_configuration.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, _In_ wchar_t *command_line, _In_ int show_command) { @@ -23,9 +22,9 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, flutter::DartProject project(L"data"); FlutterWindow window(&run_loop, project); - Win32Window::Point origin(kFlutterWindowOriginX, kFlutterWindowOriginY); - Win32Window::Size size(kFlutterWindowWidth, kFlutterWindowHeight); - if (!window.CreateAndShow(kFlutterWindowTitle, origin, size)) { + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"Fluffychat", origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true); diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index c04e20c..f53c6c0 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ diff --git a/windows/runner/run_loop.cpp b/windows/runner/run_loop.cpp index f91d6d4..2d6636a 100644 --- a/windows/runner/run_loop.cpp +++ b/windows/runner/run_loop.cpp @@ -1,9 +1,6 @@ #include "run_loop.h" -#include -// Don't stomp std::min/std::max -#undef max -#undef min +#include #include @@ -47,20 +44,19 @@ void RunLoop::Run() { } void RunLoop::RegisterFlutterInstance( - flutter::FlutterViewController* flutter_instance) { + flutter::FlutterEngine* flutter_instance) { flutter_instances_.insert(flutter_instance); } void RunLoop::UnregisterFlutterInstance( - flutter::FlutterViewController* flutter_instance) { + flutter::FlutterEngine* flutter_instance) { flutter_instances_.erase(flutter_instance); } RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { TimePoint next_event_time = TimePoint::max(); - for (auto flutter_controller : flutter_instances_) { - std::chrono::nanoseconds wait_duration = - flutter_controller->ProcessMessages(); + for (auto instance : flutter_instances_) { + std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); if (wait_duration != std::chrono::nanoseconds::max()) { next_event_time = std::min(next_event_time, TimePoint::clock::now() + wait_duration); diff --git a/windows/runner/run_loop.h b/windows/runner/run_loop.h index 442a58e..000d362 100644 --- a/windows/runner/run_loop.h +++ b/windows/runner/run_loop.h @@ -1,7 +1,7 @@ -#ifndef RUN_LOOP_H_ -#define RUN_LOOP_H_ +#ifndef RUNNER_RUN_LOOP_H_ +#define RUNNER_RUN_LOOP_H_ -#include +#include #include #include @@ -22,11 +22,11 @@ class RunLoop { // Registers the given Flutter instance for event servicing. void RegisterFlutterInstance( - flutter::FlutterViewController* flutter_instance); + flutter::FlutterEngine* flutter_instance); // Unregisters the given Flutter instance from event servicing. void UnregisterFlutterInstance( - flutter::FlutterViewController* flutter_instance); + flutter::FlutterEngine* flutter_instance); private: using TimePoint = std::chrono::steady_clock::time_point; @@ -34,7 +34,7 @@ class RunLoop { // Processes all currently pending messages for registered Flutter instances. TimePoint ProcessFlutterMessages(); - std::set flutter_instances_; + std::set flutter_instances_; }; -#endif // RUN_LOOP_H_ +#endif // RUNNER_RUN_LOOP_H_ diff --git a/windows/runner/utils.h b/windows/runner/utils.h index d247a66..d792603 100644 --- a/windows/runner/utils.h +++ b/windows/runner/utils.h @@ -1,8 +1,8 @@ -#ifndef CONSOLE_UTILS_H_ -#define CONSOLE_UTILS_H_ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ // Creates a console for the process, and redirects stdout and stderr to // it for both the runner and the Flutter library. void CreateAndAttachConsole(); -#endif // CONSOLE_UTILS_H_ +#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp index 677a9a6..efc3eb9 100644 --- a/windows/runner/win32_window.cpp +++ b/windows/runner/win32_window.cpp @@ -122,9 +122,11 @@ bool Win32Window::CreateAndShow(const std::wstring& title, Scale(size.width, scale_factor), Scale(size.height, scale_factor), nullptr, nullptr, GetModuleHandle(nullptr), this); - OnCreate(); + if (!window) { + return false; + } - return window != nullptr; + return OnCreate(); } // static @@ -152,13 +154,6 @@ 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; @@ -179,8 +174,7 @@ Win32Window::MessageHandler(HWND hwnd, return 0; } case WM_SIZE: - RECT rect; - GetClientRect(hwnd, &rect); + RECT rect = GetClientArea(); if (child_content_ != nullptr) { // Size and position the child window. MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, @@ -193,11 +187,6 @@ Win32Window::MessageHandler(HWND hwnd, 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); @@ -223,8 +212,7 @@ Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { void Win32Window::SetChildContent(HWND content) { child_content_ = content; SetParent(content, window_handle_); - RECT frame; - GetClientRect(window_handle_, &frame); + RECT frame = GetClientArea(); MoveWindow(content, frame.left, frame.top, frame.right - frame.left, frame.bottom - frame.top, true); @@ -232,6 +220,12 @@ void Win32Window::SetChildContent(HWND content) { SetFocus(child_content_); } +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + HWND Win32Window::GetHandle() { return window_handle_; } @@ -240,8 +234,9 @@ void Win32Window::SetQuitOnClose(bool quit_on_close) { quit_on_close_ = quit_on_close; } -void Win32Window::OnCreate() { +bool Win32Window::OnCreate() { // No-op; provided for subclasses. + return true; } void Win32Window::OnDestroy() { diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h index 5cbb5d5..17ba431 100644 --- a/windows/runner/win32_window.h +++ b/windows/runner/win32_window.h @@ -1,8 +1,7 @@ -#ifndef WIN32_WINDOW_H_ -#define WIN32_WINDOW_H_ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ -#include -#include +#include #include #include @@ -52,6 +51,9 @@ class Win32Window { // 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 @@ -62,8 +64,8 @@ class Win32Window { LPARAM const lparam) noexcept; // Called when CreateAndShow is called, allowing subclass window-related - // setup. - virtual void OnCreate(); + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); // Called when Destroy is called. virtual void OnDestroy(); @@ -93,4 +95,4 @@ class Win32Window { HWND child_content_ = nullptr; }; -#endif // WIN32_WINDOW_H_ +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/windows/runner/window_configuration.cpp b/windows/runner/window_configuration.cpp deleted file mode 100644 index 154f85b..0000000 --- a/windows/runner/window_configuration.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "window_configuration.h" - -const wchar_t* kFlutterWindowTitle = L"fluffychat"; -const unsigned int kFlutterWindowOriginX = 10; -const unsigned int kFlutterWindowOriginY = 10; -const unsigned int kFlutterWindowWidth = 1280; -const unsigned int kFlutterWindowHeight = 720; diff --git a/windows/runner/window_configuration.h b/windows/runner/window_configuration.h deleted file mode 100644 index ea5cead..0000000 --- a/windows/runner/window_configuration.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef WINDOW_CONFIGURATION_ -#define WINDOW_CONFIGURATION_ - -// This is a temporary approach to isolate changes that people are likely to -// make to main.cpp, where the APIs are still in flux. This will reduce the -// need to resolve conflicts or re-create changes slightly differently every -// time the Windows Flutter API surface changes. -// -// Longer term there should be simpler configuration options for common -// customizations like this, without requiring native code changes. - -extern const wchar_t* kFlutterWindowTitle; -extern const unsigned int kFlutterWindowOriginX; -extern const unsigned int kFlutterWindowOriginY; -extern const unsigned int kFlutterWindowWidth; -extern const unsigned int kFlutterWindowHeight; - -#endif // WINDOW_CONFIGURATION_