chore: Windows support

Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
This commit is contained in:
TheOneWithTheBraid 2022-07-15 07:39:28 -07:00
parent aaf6610ff4
commit a9819577d6
23 changed files with 308 additions and 148 deletions

View File

@ -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\"}"

View File

@ -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

View File

@ -19,6 +19,8 @@
"type": "text",
"placeholders": {}
},
"updateAvailable": "FluffyChat update available",
"updateNow": "Start update in background",
"accept": "Accept",
"@accept": {
"type": "text",

View File

@ -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<void> 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<void> 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<void> 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';
}

View File

@ -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);
}

View File

@ -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:

View File

@ -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

View File

@ -2,4 +2,11 @@ flutter doctor
flutter config --enable-windows-desktop
flutter clean
flutter pub get
flutter build windows --release -v
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

View File

@ -1,3 +1,5 @@
choco install flutter -y
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
refreshenv
refreshenv
flutter config --no-analytics

View File

@ -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 "$<$<CONFIG:Debug>:_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

View File

@ -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)

View File

@ -9,7 +9,6 @@
#include <connectivity_plus_windows/connectivity_plus_windows_plugin.h>
#include <desktop_drop/desktop_drop_plugin.h>
#include <desktop_lifecycle/desktop_lifecycle_plugin.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
#include <record_windows/record_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h>
@ -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(

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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<LRESULT> result =
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,

View File

@ -6,16 +6,13 @@
#include <memory>
#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_;

View File

@ -3,7 +3,6 @@
#include <windows.h>
#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<std::string> 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;

View File

@ -1,39 +0,0 @@
#include "run_loop.h"
#include <windows.h>
#include <algorithm>
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;
}

View File

@ -1,40 +0,0 @@
#ifndef RUNNER_RUN_LOOP_H_
#define RUNNER_RUN_LOOP_H_
#include <flutter/flutter_engine.h>
#include <chrono>
#include <set>
// 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::FlutterEngine*> flutter_instances_;
};
#endif // RUNNER_RUN_LOOP_H_

View File

@ -20,3 +20,45 @@ void CreateAndAttachConsole() {
FlutterDesktopResyncOutputStreams();
}
}
std::vector<std::string> 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::string>();
}
std::vector<std::string> 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;
}

View File

@ -1,8 +1,19 @@
#ifndef RUNNER_UTILS_H_
#define RUNNER_UTILS_H_
#include <string>
#include <vector>
// 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<std::string>,
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
std::vector<std::string> GetCommandLineArguments();
#endif // RUNNER_UTILS_H_

View File

@ -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) {