feat: Implement basic windows linux support

This commit is contained in:
Christian Pauly 2020-09-27 11:18:38 +02:00
parent a845209085
commit 7fad31604f
35 changed files with 1426 additions and 85 deletions

View File

@ -1,3 +1,4 @@
import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
import 'package:famedlysdk/famedlysdk.dart';
@ -6,7 +7,10 @@ import 'package:path/path.dart' as p;
import 'package:flutter/material.dart';
import 'package:moor/moor.dart';
import 'package:moor/isolate.dart';
import '../platform_infos.dart';
import 'cipher_db.dart' as cipher;
import 'package:moor/ffi.dart' as moor;
import 'package:sqlite3/open.dart';
bool _inited = false;
@ -47,17 +51,39 @@ Future<Database> constructDb(
{bool logStatements = false,
String filename = 'database.sqlite',
String password = ''}) async {
debugPrint('[Moor] using encrypted moor');
final dbFolder = await getDatabasesPath();
final targetPath = p.join(dbFolder, filename);
final receivePort = ReceivePort();
await Isolate.spawn(
_startBackground,
_IsolateStartRequest(
receivePort.sendPort, targetPath, password, logStatements),
);
final isolate = (await receivePort.first as MoorIsolate);
return Database.connect(await isolate.connect());
if (PlatformInfos.isMobile || Platform.isMacOS) {
debugPrint('[Moor] using encrypted moor');
final dbFolder = await getDatabasesPath();
final targetPath = p.join(dbFolder, filename);
final receivePort = ReceivePort();
await Isolate.spawn(
_startBackground,
_IsolateStartRequest(
receivePort.sendPort, targetPath, password, logStatements),
);
final isolate = (await receivePort.first as MoorIsolate);
return Database.connect(await isolate.connect());
} else if (Platform.isLinux) {
debugPrint('[Moor] using desktop moor');
open.overrideFor(OperatingSystem.linux, _openOnLinux);
return Database(moor.VmDatabase.memory());
} else if (Platform.isWindows) {
debugPrint('[Moor] using desktop moor');
open.overrideFor(OperatingSystem.linux, _openOnWindows);
return Database(moor.VmDatabase.memory());
}
throw Exception('Platform not supported');
}
DynamicLibrary _openOnLinux() {
final libraryNextToScript = File('/usr/lib/x86_64-linux-gnu/libsqlite3.so');
return DynamicLibrary.open(libraryNextToScript.path);
}
DynamicLibrary _openOnWindows() {
final script = File(Platform.script.toFilePath());
final libraryNextToScript = File('${script.path}/sqlite3.dll');
return DynamicLibrary.open(libraryNextToScript.path);
}
Future<String> getLocalstorage(String key) async {

1
linux/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
flutter/ephemeral

98
linux/CMakeLists.txt Normal file
View File

@ -0,0 +1,98 @@
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
set(BINARY_NAME "fluffychat")
set(APPLICATION_ID "chat.fluffy.fluffychat")
cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Configure build options.
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()
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Application build
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
apply_standard_settings(${BINARY_NAME})
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
add_dependencies(${BINARY_NAME} flutter_assemble)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
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.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()

View File

@ -0,0 +1,88 @@
cmake_minimum_required(VERSION 3.10)
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.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# 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/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
PkgConfig::BLKID
)
add_dependencies(flutter 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.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
linux-x64 ${CMAKE_BUILD_TYPE}
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)

View File

@ -0,0 +1,13 @@
//
// Generated file. Do not edit.
//
#include "generated_plugin_registrant.h"
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}

View File

@ -0,0 +1,13 @@
//
// Generated file. Do not edit.
//
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@ -0,0 +1,16 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_linux
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)

10
linux/main.cc Normal file
View File

@ -0,0 +1,10 @@
#include "my_application.h"
int main(int argc, char** argv) {
// Only X11 is currently supported.
// Wayland support is being developed: https://github.com/flutter/flutter/issues/57932.
gdk_set_allowed_backends("x11");
g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv);
}

46
linux/my_application.cc Normal file
View File

@ -0,0 +1,46 @@
#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#include "flutter/generated_plugin_registrant.h"
struct _MyApplication {
GtkApplication parent_instance;
};
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "fluffychat");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();
FlView* view = fl_view_new(project);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
nullptr));
}

18
linux/my_application.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
GtkApplication)
/**
* my_application_new:
*
* Creates a new Flutter-based application.
*
* Returns: a new #MyApplication.
*/
MyApplication* my_application_new();
#endif // FLUTTER_MY_APPLICATION_H_

View File

@ -7,21 +7,21 @@ packages:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.0"
version: "7.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "0.39.16"
version: "0.39.17"
ansicolor:
dependency: transitive
description:
name: ansicolor
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
version: "1.0.5"
archive:
dependency: transitive
description:
@ -49,7 +49,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.2"
version: "2.5.0-nullsafety"
base58check:
dependency: transitive
description:
@ -63,21 +63,21 @@ packages:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.1.0-nullsafety"
bot_toast:
dependency: "direct main"
description:
name: bot_toast
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
version: "3.0.4"
cached_network_image:
dependency: "direct main"
description:
name: cached_network_image
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.1"
version: "2.3.2+1"
canonical_json:
dependency: transitive
description:
@ -91,35 +91,35 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
version: "1.1.0-nullsafety.2"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.3"
version: "1.2.0-nullsafety"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
version: "0.2.0"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.1.0-nullsafety"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.14.13"
version: "1.15.0-nullsafety.2"
convert:
dependency: transitive
description:
@ -133,7 +133,7 @@ packages:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.0"
version: "0.14.1"
crypto:
dependency: transitive
description:
@ -168,14 +168,14 @@ packages:
name: encrypt
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.2"
version: "4.0.3"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.1.0-nullsafety"
famedlysdk:
dependency: "direct main"
description:
@ -205,7 +205,7 @@ packages:
name: file_picker
url: "https://pub.dartlang.org"
source: hosted
version: "1.13.2"
version: "1.13.3"
file_picker_platform_interface:
dependency: transitive
description:
@ -266,14 +266,14 @@ packages:
name: flutter_cache_manager
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.1"
version: "1.4.2"
flutter_keyboard_visibility:
dependency: transitive
description:
name: flutter_keyboard_visibility
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.1"
version: "3.2.2"
flutter_launcher_icons:
dependency: "direct dev"
description:
@ -287,7 +287,7 @@ packages:
name: flutter_local_notifications
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.4+2"
version: "1.4.4+4"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
@ -320,21 +320,21 @@ packages:
name: flutter_plugin_android_lifecycle
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.8"
version: "1.0.11"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
url: "https://pub.dartlang.org"
source: hosted
version: "3.3.3"
version: "3.3.4"
flutter_slidable:
dependency: "direct main"
description:
name: flutter_slidable
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.5"
version: "0.5.7"
flutter_sound:
dependency: "direct main"
description:
@ -353,7 +353,7 @@ packages:
name: flutter_typeahead
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.7"
version: "1.8.8"
flutter_web_plugins:
dependency: transitive
description: flutter
@ -379,7 +379,7 @@ packages:
name: html_unescape
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1+3"
version: "1.0.2"
http:
dependency: "direct main"
description:
@ -407,21 +407,21 @@ packages:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.14"
version: "2.1.18"
image_picker:
dependency: transitive
description:
name: image_picker
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.7+4"
version: "0.6.7+11"
image_picker_platform_interface:
dependency: transitive
description:
name: image_picker_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.1.1"
intl:
dependency: "direct main"
description:
@ -435,7 +435,7 @@ packages:
name: intl_translation
url: "https://pub.dartlang.org"
source: hosted
version: "0.17.10"
version: "0.17.10+1"
io:
dependency: transitive
description:
@ -456,7 +456,7 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.2"
version: "0.6.3-nullsafety.1"
localstorage:
dependency: "direct main"
description:
@ -477,14 +477,14 @@ packages:
name: markdown
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.7"
version: "2.1.8"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.8"
version: "0.12.10-nullsafety"
matrix_file_e2ee:
dependency: transitive
description:
@ -512,14 +512,14 @@ packages:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.8"
version: "1.3.0-nullsafety.2"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.6+3"
version: "0.9.7"
mime_type:
dependency: "direct main"
description:
@ -570,7 +570,7 @@ packages:
name: octo_image
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.1"
version: "0.3.0"
olm:
dependency: transitive
description:
@ -605,14 +605,14 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
version: "1.8.0-nullsafety"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.11"
version: "1.6.18"
path_provider_linux:
dependency: transitive
description:
@ -626,28 +626,35 @@ packages:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4+3"
version: "0.0.4+4"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
version: "1.0.3"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4+1"
pedantic:
dependency: "direct dev"
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.0"
version: "1.10.0-nullsafety.1"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.4"
version: "3.1.0"
photo_view:
dependency: "direct main"
description:
@ -662,13 +669,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
platform_detect:
dependency: transitive
description:
name: platform_detect
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
plugin_platform_interface:
dependency: transitive
description:
@ -689,7 +689,7 @@ packages:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
version: "1.5.0-nullsafety.1"
process:
dependency: transitive
description:
@ -724,7 +724,7 @@ packages:
name: receive_sharing_intent
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0+2"
version: "1.4.1"
rxdart:
dependency: transitive
description:
@ -752,14 +752,14 @@ packages:
name: share
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.4+3"
version: "0.6.5+2"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.7"
version: "0.7.9"
shelf_packages_handler:
dependency: transitive
description:
@ -792,28 +792,28 @@ packages:
name: source_map_stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.1.0-nullsafety.2"
source_maps:
dependency: transitive
description:
name: source_maps
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.9"
version: "0.10.10-nullsafety.1"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
version: "1.8.0-nullsafety"
sqflite:
dependency: "direct main"
description:
name: sqflite
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
version: "1.3.1+1"
sqflite_common:
dependency: transitive
description:
@ -827,28 +827,35 @@ packages:
name: sqlite3
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
version: "0.1.5"
sqlite3_flutter_libs:
dependency: "direct main"
description:
name: sqlite3_flutter_libs
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.5"
version: "1.10.0-nullsafety"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.1.0-nullsafety"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
version: "1.1.0-nullsafety"
synchronized:
dependency: transitive
description:
@ -862,35 +869,35 @@ packages:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.0-nullsafety"
test:
dependency: transitive
description:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.2"
version: "1.16.0-nullsafety.4"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.17"
version: "0.2.19-nullsafety"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.10"
version: "0.3.12-nullsafety.4"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.0-nullsafety.2"
universal_html:
dependency: "direct main"
description:
@ -918,7 +925,7 @@ packages:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "5.5.0"
version: "5.7.2"
url_launcher_linux:
dependency: transitive
description:
@ -932,21 +939,28 @@ packages:
name: url_launcher_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+7"
version: "0.0.1+8"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.7"
version: "1.0.8"
url_launcher_web:
dependency: "direct main"
description:
name: url_launcher_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2"
version: "0.1.4+1"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+1"
usage:
dependency: transitive
description:
@ -967,14 +981,14 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
version: "2.1.0-nullsafety.2"
vm_service:
dependency: transitive
description:
name: vm_service
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.0"
version: "5.2.0"
watcher:
dependency: transitive
description:
@ -1002,21 +1016,28 @@ packages:
name: webview_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.22+1"
version: "0.3.24"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.3"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0"
version: "0.1.2"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.0"
version: "4.5.1"
yaml:
dependency: transitive
description:
@ -1032,5 +1053,5 @@ packages:
source: hosted
version: "0.1.2"
sdks:
dart: ">=2.9.0-14.0.dev <3.0.0"
dart: ">=2.10.0-0.0.dev <2.10.0"
flutter: ">=1.20.0 <2.0.0"

View File

@ -52,6 +52,7 @@ dependencies:
bot_toast: ^3.0.0
flutter_matrix_html: ^0.1.5
moor: ^3.3.1
sqlite3_flutter_libs: ^0.2.0
sqlite3: ^0.1.4
random_string: ^2.0.1
flutter_typeahead: ^1.8.1

17
windows/.gitignore vendored Normal file
View File

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

95
windows/CMakeLists.txt Normal file
View File

@ -0,0 +1,95 @@
cmake_minimum_required(VERSION 3.15)
project(fluffychat LANGUAGES CXX)
set(BINARY_NAME "fluffychat")
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 "$<$<CONFIG:Debug>:_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 "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# 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)

View File

@ -0,0 +1 @@
5

View File

@ -0,0 +1,99 @@
cmake_minimum_required(VERSION 3.15)
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"
)
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
"engine_method_result.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.
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_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG>
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
)

View File

@ -0,0 +1,12 @@
//
// Generated file. Do not edit.
//
#include "generated_plugin_registrant.h"
#include <url_launcher_windows/url_launcher_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
UrlLauncherPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherPlugin"));
}

View File

@ -0,0 +1,13 @@
//
// Generated file. Do not edit.
//
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter/plugin_registry.h>
// Registers Flutter plugins.
void RegisterPlugins(flutter::PluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@ -0,0 +1,16 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
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 $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)

View File

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.15)
project(runner LANGUAGES CXX)
add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp"
"main.cpp"
"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_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
add_dependencies(${BINARY_NAME} flutter_assemble)

70
windows/runner/Runner.rc Normal file
View File

@ -0,0 +1,70 @@
// 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\\app_icon.ico"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -0,0 +1,29 @@
#include "flutter_window.h"
#include "flutter/generated_plugin_registrant.h"
FlutterWindow::FlutterWindow(RunLoop* run_loop,
const flutter::DartProject& project)
: run_loop_(run_loop), project_(project) {}
FlutterWindow::~FlutterWindow() {}
void FlutterWindow::OnCreate() {
Win32Window::OnCreate();
// The size here is arbitrary since SetChildContent will resize it.
flutter_controller_ =
std::make_unique<flutter::FlutterViewController>(100, 100, project_);
RegisterPlugins(flutter_controller_.get());
run_loop_->RegisterFlutterInstance(flutter_controller_.get());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
}
void FlutterWindow::OnDestroy() {
if (flutter_controller_) {
run_loop_->UnregisterFlutterInstance(flutter_controller_.get());
flutter_controller_ = nullptr;
}
Win32Window::OnDestroy();
}

View File

@ -0,0 +1,37 @@
#ifndef FLUTTER_WINDOW_H_
#define FLUTTER_WINDOW_H_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include "run_loop.h"
#include "win32_window.h"
#include <memory>
// 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);
virtual ~FlutterWindow();
protected:
// Win32Window:
void OnCreate() override;
void OnDestroy() override;
private:
// The run loop driving events for this window.
RunLoop* run_loop_;
// The project to run.
flutter::DartProject project_;
// The Flutter instance hosted by this window.
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
};
#endif // FLUTTER_WINDOW_H_

37
windows/runner/main.cpp Normal file
View File

@ -0,0 +1,37 @@
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>
#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) {
// 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);
RunLoop run_loop;
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)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);
run_loop.Run();
::CoUninitialize();
return EXIT_SUCCESS;
}

16
windows/runner/resource.h Normal file
View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -0,0 +1,70 @@
#include "run_loop.h"
#include <Windows.h>
// Don't stomp std::min/std::max
#undef max
#undef min
#include <algorithm>
RunLoop::RunLoop() {}
RunLoop::~RunLoop() {}
void RunLoop::Run() {
bool keep_running = true;
TimePoint next_flutter_event_time = TimePoint::clock::now();
while (keep_running) {
std::chrono::nanoseconds wait_duration =
std::max(std::chrono::nanoseconds(0),
next_flutter_event_time - TimePoint::clock::now());
::MsgWaitForMultipleObjects(
0, nullptr, FALSE, static_cast<DWORD>(wait_duration.count() / 1000),
QS_ALLINPUT);
bool processed_events = false;
MSG message;
// All pending Windows messages must be processed; MsgWaitForMultipleObjects
// won't return again for items left in the queue after PeekMessage.
while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
processed_events = true;
if (message.message == WM_QUIT) {
keep_running = false;
break;
}
::TranslateMessage(&message);
::DispatchMessage(&message);
// Allow Flutter to process messages each time a Windows message is
// processed, to prevent starvation.
next_flutter_event_time =
std::min(next_flutter_event_time, ProcessFlutterMessages());
}
// If the PeekMessage loop didn't run, process Flutter messages.
if (!processed_events) {
next_flutter_event_time =
std::min(next_flutter_event_time, ProcessFlutterMessages());
}
}
}
void RunLoop::RegisterFlutterInstance(
flutter::FlutterViewController* flutter_instance) {
flutter_instances_.insert(flutter_instance);
}
void RunLoop::UnregisterFlutterInstance(
flutter::FlutterViewController* 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();
if (wait_duration != std::chrono::nanoseconds::max()) {
next_event_time =
std::min(next_event_time, TimePoint::clock::now() + wait_duration);
}
}
return next_event_time;
}

40
windows/runner/run_loop.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef RUN_LOOP_H_
#define RUN_LOOP_H_
#include <flutter/flutter_view_controller.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::FlutterViewController* flutter_instance);
// Unregisters the given Flutter instance from event servicing.
void UnregisterFlutterInstance(
flutter::FlutterViewController* 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::FlutterViewController*> flutter_instances_;
};
#endif // RUN_LOOP_H_

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>

22
windows/runner/utils.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "utils.h"
#include <flutter_windows.h>
#include <io.h>
#include <stdio.h>
#include <windows.h>
#include <iostream>
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();
}
}

8
windows/runner/utils.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef CONSOLE_UTILS_H_
#define CONSOLE_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_

View File

@ -0,0 +1,249 @@
#include "win32_window.h"
#include <flutter_windows.h>
#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<int>(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<EnableNonClientDpiScaling*>(
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<LONG>(origin.x),
static_cast<LONG>(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);
OnCreate();
return window != nullptr;
}
// 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<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
auto that = static_cast<Win32Window*>(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 {
auto window =
reinterpret_cast<Win32Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (window == nullptr) {
return 0;
}
switch (message) {
case WM_DESTROY:
window_handle_ = nullptr;
Destroy();
if (quit_on_close_) {
PostQuitMessage(0);
}
return 0;
case WM_DPICHANGED: {
auto newRectSize = reinterpret_cast<RECT*>(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;
GetClientRect(hwnd, &rect);
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;
// 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);
}
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<Win32Window*>(
GetWindowLongPtr(window, GWLP_USERDATA));
}
void Win32Window::SetChildContent(HWND content) {
child_content_ = content;
SetParent(content, window_handle_);
RECT frame;
GetClientRect(window_handle_, &frame);
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
frame.bottom - frame.top, true);
SetFocus(child_content_);
}
HWND Win32Window::GetHandle() {
return window_handle_;
}
void Win32Window::SetQuitOnClose(bool quit_on_close) {
quit_on_close_ = quit_on_close;
}
void Win32Window::OnCreate() {
// No-op; provided for subclasses.
}
void Win32Window::OnDestroy() {
// No-op; provided for subclasses.
}

View File

@ -0,0 +1,96 @@
#ifndef WIN32_WINDOW_H_
#define WIN32_WINDOW_H_
#include <Windows.h>
#include <Windowsx.h>
#include <functional>
#include <memory>
#include <string>
// 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);
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.
virtual void 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 // WIN32_WINDOW_H_

View File

@ -0,0 +1,7 @@
#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;

View File

@ -0,0 +1,18 @@
#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_