From a9819577d6e510b4ae76a69721373cf29b344bee Mon Sep 17 00:00:00 2001 From: TheOneWithTheBraid Date: Fri, 15 Jul 2022 07:39:28 -0700 Subject: [PATCH] chore: Windows support Signed-off-by: TheOneWithTheBraid --- .gitlab-ci.yml | 3 + .metadata | 15 --- assets/l10n/intl_en.arb | 2 + lib/utils/update_checker_no_store.dart | 116 ++++++++++++++++++ lib/widgets/layouts/loading_view.dart | 18 +-- pubspec.lock | 40 +++++- pubspec.yaml | 22 +++- scripts/build-windows.ps1 | 9 +- scripts/prepare-windows.ps1 | 4 +- windows/CMakeLists.txt | 22 ++-- windows/flutter/CMakeLists.txt | 4 +- .../flutter/generated_plugin_registrant.cc | 3 - windows/flutter/generated_plugins.cmake | 1 - windows/runner/CMakeLists.txt | 18 ++- windows/runner/Runner.rc | 8 +- windows/runner/flutter_window.cpp | 9 +- windows/runner/flutter_window.h | 10 +- windows/runner/main.cpp | 17 ++- windows/runner/run_loop.cpp | 39 ------ windows/runner/run_loop.h | 40 ------ windows/runner/utils.cpp | 42 +++++++ windows/runner/utils.h | 11 ++ windows/runner/win32_window.cpp | 3 +- 23 files changed, 308 insertions(+), 148 deletions(-) create mode 100644 lib/utils/update_checker_no_store.dart delete mode 100644 windows/runner/run_loop.cpp delete mode 100644 windows/runner/run_loop.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 690e368a..7509fec4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -391,9 +391,11 @@ upload-windows: image: alpine:latest script: - apk add --no-cache curl zip + - mv build/windows/runner/Release/fluffychat.msix fluffychat.msix - cd build/windows/runner/Release; zip -r ../../../../package.zip . ; cd - - | curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.zip ${PACKAGE_REGISTRY_URL}/fluffychat-windows.zip + curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file fluffychat.msix ${PACKAGE_REGISTRY_URL}/fluffychat-windows.msix upload-playstore: stage: release @@ -416,6 +418,7 @@ release: --assets-link "{\"name\":\"fluffychat-linux-x86.tar.gz\",\"url\":\"${PACKAGE_REGISTRY_URL}/fluffychat-linux-x86.tar.gz\"}" \ --assets-link "{\"name\":\"fluffychat-linux-arm64.tar.gz\",\"url\":\"${PACKAGE_REGISTRY_URL}/fluffychat-linux-arm64.tar.gz\"}" \ --assets-link "{\"name\":\"fluffychat-windows.zip\",\"url\":\"${PACKAGE_REGISTRY_URL}/fluffychat-windows.zip\"}" \ + --assets-link "{\"name\":\"fluffychat-windows.msix\",\"url\":\"${PACKAGE_REGISTRY_URL}/fluffychat-windows.msix\"}" \ --assets-link "{\"name\":\"fluffychat-web.tar.gz\",\"url\":\"${PACKAGE_REGISTRY_URL}/fluffychat-web.tar.gz\"}" --assets-link "{\"name\":\"FluffyChat-x86_64.AppImage\",\"url\":\"${PACKAGE_REGISTRY_URL}/FluffyChat-x86_64.AppImage\",\"filepath\":\"/FluffyChat-x86_64.AppImage\"}" --assets-link "{\"name\":\"FluffyChat-x86_64.AppImage.zsync\",\"url\":\"${PACKAGE_REGISTRY_URL}/FluffyChat-x86_64.AppImage.zsync\",\"filepath\":\"/FluffyChat-x86_64.AppImage.zsync\"}" diff --git a/.metadata b/.metadata index e7c10010..43b81a94 100644 --- a/.metadata +++ b/.metadata @@ -15,21 +15,6 @@ migration: - platform: root create_revision: 85684f9300908116a78138ea4c6036c35c9a1236 base_revision: 85684f9300908116a78138ea4c6036c35c9a1236 - - platform: android - create_revision: 85684f9300908116a78138ea4c6036c35c9a1236 - base_revision: 85684f9300908116a78138ea4c6036c35c9a1236 - - platform: ios - create_revision: 85684f9300908116a78138ea4c6036c35c9a1236 - base_revision: 85684f9300908116a78138ea4c6036c35c9a1236 - - platform: linux - create_revision: 85684f9300908116a78138ea4c6036c35c9a1236 - base_revision: 85684f9300908116a78138ea4c6036c35c9a1236 - - platform: macos - create_revision: 85684f9300908116a78138ea4c6036c35c9a1236 - base_revision: 85684f9300908116a78138ea4c6036c35c9a1236 - - platform: web - create_revision: 85684f9300908116a78138ea4c6036c35c9a1236 - base_revision: 85684f9300908116a78138ea4c6036c35c9a1236 - platform: windows create_revision: 85684f9300908116a78138ea4c6036c35c9a1236 base_revision: 85684f9300908116a78138ea4c6036c35c9a1236 diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index fc0170ba..46b60ef5 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -19,6 +19,8 @@ "type": "text", "placeholders": {} }, + "updateAvailable": "FluffyChat update available", + "updateNow": "Start update in background", "accept": "Accept", "@accept": { "type": "text", diff --git a/lib/utils/update_checker_no_store.dart b/lib/utils/update_checker_no_store.dart new file mode 100644 index 00000000..b4e73bc8 --- /dev/null +++ b/lib/utils/update_checker_no_store.dart @@ -0,0 +1,116 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:http/http.dart'; +import 'package:matrix/matrix.dart'; +import 'package:path_provider/path_provider.dart'; + +import 'platform_infos.dart'; + +/// helper class checking for updates on platforms without store release +/// +/// Currently, this is only Windows +class UpdateCheckerNoStore { + static const gitLabProjectId = '16112282'; + static const gitLabHost = 'gitlab.com'; + + static Uri get tagsUri => Uri.parse( + 'https://$gitLabHost/projects/$gitLabProjectId/repository/tags'); + + final BuildContext context; + + const UpdateCheckerNoStore(this.context); + + Future checkUpdate() async { + // platform-specific implementations + try { + if (PlatformInfos.isWindows) { + final response = await get(tagsUri); + if (response.statusCode == 200) { + final json = jsonDecode(response.body); + var latestTag = json[0]['name'] as String; + var currentVersion = await PlatformInfos.getVersion(); + + if (latestTag.startsWith('v')) { + latestTag = latestTag.substring(1); + } + if (currentVersion.startsWith('v')) { + currentVersion = currentVersion.substring(1); + } + if (latestTag != currentVersion) { + final metadata = UpdateMetadata(latestTag); + await showUpdateDialog(metadata); + } + return; + } else { + throw response; + } + } else { + return; + } + } catch (e) { + Logs().i('Could not look for updates:', e); + return; + } + } + + Uri downloadUri(UpdateMetadata metadata) { + // platform specific + if (PlatformInfos.isWindows) { + return Uri.parse('https://$gitLabHost/famedly/fluffychat/-' + '/jobs/artifacts/$metadata/raw/' + 'build/windows/runner/Release/fluffychat.msix?job=build_windows'); + } else { + throw UnimplementedError('No download URI available for this platform.'); + } + } + + /// launches an app update + /// + /// Either uses the operating systems package or app management to perform + /// an update or launches a custom installer + Future launchUpdater(UpdateMetadata metadata) async { + // platform specific + try { + if (kIsWeb) return; + if (PlatformInfos.isWindows) { + final dir = await getTemporaryDirectory(); + final response = await get(downloadUri(metadata)); + if (response.statusCode == 200) { + final file = File(dir.path + '/fluffychat.msix'); + await file.writeAsBytes(response.bodyBytes); + Process.start(file.path, [], runInShell: true); + } else { + throw response; + } + } + } catch (e) { + Logs().w('Error launching th update:', e); + } + } + + Future showUpdateDialog(UpdateMetadata metadata) async { + final result = await showOkCancelAlertDialog( + title: L10n.of(context)!.updateAvailable, + message: L10n.of(context)!.updateNow, + context: context, + ); + if (result == OkCancelResult.ok) { + await launchUpdater(metadata); + } + } +} + +class UpdateMetadata { + final String version; + + const UpdateMetadata(this.version); + + @override + String toString() => 'v$version'; +} diff --git a/lib/widgets/layouts/loading_view.dart b/lib/widgets/layouts/loading_view.dart index 7015bcd9..d45fe53f 100644 --- a/lib/widgets/layouts/loading_view.dart +++ b/lib/widgets/layouts/loading_view.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; import 'package:vrouter/vrouter.dart'; +import 'package:fluffychat/utils/update_checker_no_store.dart'; import 'package:fluffychat/widgets/layouts/empty_page.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -12,13 +13,16 @@ class LoadingView extends StatelessWidget { @override Widget build(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback( - (_) => VRouter.of(context).to( - Matrix.of(context).widget.clients.any((client) => - client.onLoginStateChanged.value == LoginState.loggedIn) - ? '/rooms' - : '/home', - queryParameters: VRouter.of(context).queryParameters, - ), + (_) async { + await UpdateCheckerNoStore(context).checkUpdate(); + VRouter.of(context).to( + Matrix.of(context).widget.clients.any((client) => + client.onLoginStateChanged.value == LoginState.loggedIn) + ? '/rooms' + : '/home', + queryParameters: VRouter.of(context).queryParameters, + ); + }, ); return const EmptyPage(loading: true); } diff --git a/pubspec.lock b/pubspec.lock index 771a6d89..780a3369 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -162,6 +162,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + cli_dialog: + dependency: transitive + description: + name: cli_dialog + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0" cli_util: dependency: transitive description: @@ -276,6 +283,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.12.0" + dart_console: + dependency: transitive + description: + name: dart_console + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" dart_style: dependency: transitive description: @@ -684,11 +698,13 @@ packages: source: hosted version: "1.0.2" flutter_secure_storage_windows: - dependency: transitive + dependency: "direct overridden" description: - name: flutter_secure_storage_windows - url: "https://pub.dartlang.org" - source: hosted + path: "." + ref: main + resolved-ref: "3751da0df5eede20d64816ceb79ca78ace7389b3" + url: "https://gitlab.com/TheOneWithTheBraid/flutter_secure_storage_windows.git" + source: git version: "1.1.2" flutter_slidable: dependency: "direct main" @@ -789,6 +805,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.6" + get_it: + dependency: transitive + description: + name: get_it + url: "https://pub.dartlang.org" + source: hosted + version: "7.2.0" glob: dependency: transitive description: @@ -839,7 +862,7 @@ packages: source: hosted version: "2.0.0" http: - dependency: "direct dev" + dependency: "direct main" description: name: http url: "https://pub.dartlang.org" @@ -1090,6 +1113,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + msix: + dependency: "direct dev" + description: + name: msix + url: "https://pub.dartlang.org" + source: hosted + version: "3.6.2" native_imaging: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index cb0eda38..fc63730d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,7 @@ dependencies: geolocator: ^7.6.2 handy_window: ^0.1.6 hive_flutter: ^1.1.0 + http: ^0.13.4 image: ^3.1.1 image_picker: ^0.8.4+8 intl: any @@ -96,10 +97,10 @@ dev_dependencies: flutter_native_splash: ^2.0.3+1 flutter_test: sdk: flutter - http: ^0.13.4 import_sorter: ^4.6.0 integration_test: sdk: flutter + msix: ^3.6.2 flutter_native_splash: color: "#ffffff" @@ -127,6 +128,19 @@ flutter: fonts: - asset: fonts/NotoEmoji/NotoColorEmoji.ttf +msix_config: + display_name: FluffyChat + publisher_display_name: FluffyChat + publisher: CN=FluffyChat, O=Head of bad integration tests, L=Matrix, S=Internet, C=EU + identity_name: chat.fluffy.fluffychat + logo_path: assets\logo.png + capabilities: internetClient, location, microphone, webcam + protocol_activation: https + app_uri_handler_hosts: fluffychat.im, matrix.to + execution_alias: fluffychat + sign_msix: false + install_certificate: false + dependency_overrides: # Necessary for webRTC on web. # Fix for stream fallback for unsupported browsers: @@ -136,6 +150,12 @@ dependency_overrides: url: https://github.com/TheOneWithTheBraid/plus_plugins.git ref: a04401cb48abe92d138c0e9288b360739994a9e9 path: packages/connectivity_plus/connectivity_plus_web + # fake secure storage plugin for Windows + # See: https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/15161 + flutter_secure_storage_windows: + git: + url: https://gitlab.com/TheOneWithTheBraid/flutter_secure_storage_windows.git + ref: main geolocator_android: hosted: name: geolocator_android diff --git a/scripts/build-windows.ps1 b/scripts/build-windows.ps1 index dea9cf8f..418e81a6 100644 --- a/scripts/build-windows.ps1 +++ b/scripts/build-windows.ps1 @@ -2,4 +2,11 @@ flutter doctor flutter config --enable-windows-desktop flutter clean flutter pub get -flutter build windows --release -v \ No newline at end of file + +Write-Output "$WINDOWN_PFX" +Move-Item -Path $WINDOWS_PFX -Destination fluffychat.pem +certutil -decode fluffychat.pem fluffychat.pfx + +flutter build windows --release -v + +flutter pub run msix:create -c fluffychat.pfx -p $WINDOWS_PFX_PASS --sign-msix true --install-certificate false \ No newline at end of file diff --git a/scripts/prepare-windows.ps1 b/scripts/prepare-windows.ps1 index 7b749091..c73dfe71 100644 --- a/scripts/prepare-windows.ps1 +++ b/scripts/prepare-windows.ps1 @@ -1,3 +1,5 @@ choco install flutter -y Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" -refreshenv \ No newline at end of file +refreshenv + +flutter config --no-analytics diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index a18ebc9d..1c2b9945 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -1,13 +1,16 @@ -cmake_minimum_required(VERSION 3.15) +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) project(fluffychat LANGUAGES CXX) +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. set(BINARY_NAME "fluffychat") +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. cmake_policy(SET CMP0063 NEW) -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Configure build options. +# Define build configuration option. get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(IS_MULTICONFIG) set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" @@ -20,7 +23,7 @@ else() "Debug" "Profile" "Release") endif() endif() - +# Define settings for the Profile build mode. 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}") @@ -30,6 +33,10 @@ set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") add_definitions(-DUNICODE -D_UNICODE) # Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. function(APPLY_STANDARD_SETTINGS TARGET) target_compile_features(${TARGET} PUBLIC cxx_std_17) target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") @@ -38,12 +45,11 @@ function(APPLY_STANDARD_SETTINGS TARGET) target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") endfunction() -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") - # Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") add_subdirectory(${FLUTTER_MANAGED_DIR}) -# Application build +# Application build; see runner/CMakeLists.txt. add_subdirectory("runner") # Generated plugin build rules, which manage building the plugins and adding diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 744f08a9..930d2071 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -1,4 +1,5 @@ -cmake_minimum_required(VERSION 3.15) +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") @@ -23,6 +24,7 @@ list(APPEND FLUTTER_LIBRARY_HEADERS "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) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index ca808ea9..a621db73 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -21,8 +20,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("DesktopDropPlugin")); DesktopLifecyclePluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("DesktopLifecyclePlugin")); - FlutterSecureStorageWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); FlutterWebRTCPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterWebRTCPlugin")); RecordWindowsPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 317c9a92..b956de8e 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -6,7 +6,6 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus_windows desktop_drop desktop_lifecycle - flutter_secure_storage_windows flutter_webrtc record_windows url_launcher_windows diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt index 977e38b5..b9e550fb 100644 --- a/windows/runner/CMakeLists.txt +++ b/windows/runner/CMakeLists.txt @@ -1,18 +1,32 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.14) project(runner LANGUAGES CXX) +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. add_executable(${BINARY_NAME} WIN32 "flutter_window.cpp" "main.cpp" - "run_loop.cpp" "utils.cpp" "win32_window.cpp" "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" "Runner.rc" "runner.exe.manifest" ) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. apply_standard_settings(${BINARY_NAME}) + +# Disable Windows macros that collide with C++ standard library functions. target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc index 7951cb50..52aa4877 100644 --- a/windows/runner/Runner.rc +++ b/windows/runner/Runner.rc @@ -90,12 +90,12 @@ BEGIN BLOCK "040904e4" BEGIN VALUE "CompanyName", "chat.fluffy" "\0" - VALUE "FileDescription", "A new Flutter project." "\0" + VALUE "FileDescription", "fluffychat" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "FluffyChat" "\0" - VALUE "LegalCopyright", "Copyright (C) 2020 chat.fluffy. All rights reserved." "\0" + VALUE "InternalName", "fluffychat" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 chat.fluffy. All rights reserved." "\0" VALUE "OriginalFilename", "fluffychat.exe" "\0" - VALUE "ProductName", "FluffyChat" "\0" + VALUE "ProductName", "fluffychat" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" END END diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp index c4227230..b43b9095 100644 --- a/windows/runner/flutter_window.cpp +++ b/windows/runner/flutter_window.cpp @@ -4,9 +4,8 @@ #include "flutter/generated_plugin_registrant.h" -FlutterWindow::FlutterWindow(RunLoop* run_loop, - const flutter::DartProject& project) - : run_loop_(run_loop), project_(project) {} +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} FlutterWindow::~FlutterWindow() {} @@ -26,14 +25,12 @@ bool FlutterWindow::OnCreate() { 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_->engine()); flutter_controller_ = nullptr; } @@ -44,7 +41,7 @@ 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. + // Give Flutter, including plugins, an opportunity to handle window messages. if (flutter_controller_) { std::optional result = flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h index b663ddd5..6da0652f 100644 --- a/windows/runner/flutter_window.h +++ b/windows/runner/flutter_window.h @@ -6,16 +6,13 @@ #include -#include "run_loop.h" #include "win32_window.h" // A window that does nothing but host a Flutter view. class FlutterWindow : public Win32Window { public: - // Creates a new FlutterWindow driven by the |run_loop|, hosting a - // Flutter view running |project|. - explicit FlutterWindow(RunLoop* run_loop, - const flutter::DartProject& project); + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); virtual ~FlutterWindow(); protected: @@ -26,9 +23,6 @@ class FlutterWindow : public Win32Window { LPARAM const lparam) noexcept override; private: - // The run loop driving events for this window. - RunLoop* run_loop_; - // The project to run. flutter::DartProject project_; diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp index 553f9c0f..3c2f3ab5 100644 --- a/windows/runner/main.cpp +++ b/windows/runner/main.cpp @@ -3,7 +3,6 @@ #include #include "flutter_window.h" -#include "run_loop.h" #include "utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, @@ -18,10 +17,14 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, // plugins. ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - RunLoop run_loop; - flutter::DartProject project(L"data"); - FlutterWindow window(&run_loop, project); + + 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"FluffyChat", origin, size)) { @@ -29,7 +32,11 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, } window.SetQuitOnClose(true); - run_loop.Run(); + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } ::CoUninitialize(); return EXIT_SUCCESS; diff --git a/windows/runner/run_loop.cpp b/windows/runner/run_loop.cpp deleted file mode 100644 index f23c5f21..00000000 --- a/windows/runner/run_loop.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "run_loop.h" - -#include - -#include - -RunLoop::RunLoop() {} - -RunLoop::~RunLoop() {} - -void RunLoop::Run() { - MSG msg; - while (GetMessage(&msg, nullptr, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } -} - -void RunLoop::RegisterFlutterInstance( - flutter::FlutterEngine* flutter_instance) { - flutter_instances_.insert(flutter_instance); -} - -void RunLoop::UnregisterFlutterInstance( - flutter::FlutterEngine* flutter_instance) { - flutter_instances_.erase(flutter_instance); -} - -RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { - TimePoint next_event_time = TimePoint::max(); - 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); - } - } - return next_event_time; -} diff --git a/windows/runner/run_loop.h b/windows/runner/run_loop.h deleted file mode 100644 index 000d3624..00000000 --- a/windows/runner/run_loop.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef RUNNER_RUN_LOOP_H_ -#define RUNNER_RUN_LOOP_H_ - -#include - -#include -#include - -// A runloop that will service events for Flutter instances as well -// as native messages. -class RunLoop { - public: - RunLoop(); - ~RunLoop(); - - // Prevent copying - RunLoop(RunLoop const&) = delete; - RunLoop& operator=(RunLoop const&) = delete; - - // Runs the run loop until the application quits. - void Run(); - - // Registers the given Flutter instance for event servicing. - void RegisterFlutterInstance( - flutter::FlutterEngine* flutter_instance); - - // Unregisters the given Flutter instance from event servicing. - void UnregisterFlutterInstance( - flutter::FlutterEngine* flutter_instance); - - private: - using TimePoint = std::chrono::steady_clock::time_point; - - // Processes all currently pending messages for registered Flutter instances. - TimePoint ProcessFlutterMessages(); - - std::set flutter_instances_; -}; - -#endif // RUNNER_RUN_LOOP_H_ diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp index 37501e5d..f5bf9fa0 100644 --- a/windows/runner/utils.cpp +++ b/windows/runner/utils.cpp @@ -20,3 +20,45 @@ void CreateAndAttachConsole() { 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); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return 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 index d792603b..3879d547 100644 --- a/windows/runner/utils.h +++ b/windows/runner/utils.h @@ -1,8 +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 index efc3eb9f..c10f08dc 100644 --- a/windows/runner/win32_window.cpp +++ b/windows/runner/win32_window.cpp @@ -173,7 +173,7 @@ Win32Window::MessageHandler(HWND hwnd, return 0; } - case WM_SIZE: + case WM_SIZE: { RECT rect = GetClientArea(); if (child_content_ != nullptr) { // Size and position the child window. @@ -181,6 +181,7 @@ Win32Window::MessageHandler(HWND hwnd, rect.bottom - rect.top, TRUE); } return 0; + } case WM_ACTIVATE: if (child_content_ != nullptr) {