提交 ff484d4f 编写于 作者: J James Clarke 提交者: stuartmorgan

[Windows] Alternative Windows shell platform implementation (#9835)

Start work on flutter/flutter#30726 by adding an alternative win32 shell platform implementation for Windows that is not based on GLFW and that uses LIBANGLE for rendering and native win32 windowing and input. This change does not replace the GLFW implementation but rather runs side by side with it producing a secondary flutter_windows_win32.dll artifact. The following items must be added to attain parity with the GLFW implementation:
- Custom task scheduling
- Support for keyboard modifier keys
- Async texture uploads
- Correct high DPI handling on Windows versions < 1703
and will be added in subsequent changes.
上级 c5e30553
......@@ -71,6 +71,10 @@ group("flutter") {
"$flutter_root/third_party/txt:txt_unittests",
]
if (is_win) {
public_deps += [ "$flutter_root/shell/platform/windows/client_wrapper:client_wrapper_windows_unittests" ]
}
if (!is_win) {
public_deps += [
"$flutter_root/fml:fml_benchmarks",
......
......@@ -419,6 +419,9 @@ deps = {
'src/third_party/swiftshader':
Var('swiftshader_git') + '/SwiftShader.git' + '@' + 'd70129a3d3409dac58e14f819b62620393afb652',
'src/third_party/angle':
Var('github_git') + '/google/angle.git' + '@' + '2d0a9acb05511a8fdaf0cd27d0da22599516a68b',
'src/third_party/pkg/when':
Var('dart_git') + '/when.git' + '@' + '0.2.0',
......
......@@ -982,6 +982,29 @@ FILE: ../../../flutter/shell/platform/glfw/platform_handler.h
FILE: ../../../flutter/shell/platform/glfw/public/flutter_glfw.h
FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc
FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h
FILE: ../../../flutter/shell/platform/windows/angle_surface_manager.cc
FILE: ../../../flutter/shell/platform/windows/angle_surface_manager.h
FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_window_controller.cc
FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_window_controller_unittests.cc
FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_window.h
FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_window_controller.h
FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/plugin_registrar_windows.h
FILE: ../../../flutter/shell/platform/windows/flutter_windows.cc
FILE: ../../../flutter/shell/platform/windows/key_event_handler.cc
FILE: ../../../flutter/shell/platform/windows/key_event_handler.h
FILE: ../../../flutter/shell/platform/windows/keyboard_hook_handler.h
FILE: ../../../flutter/shell/platform/windows/platform_handler.cc
FILE: ../../../flutter/shell/platform/windows/platform_handler.h
FILE: ../../../flutter/shell/platform/windows/public/flutter_windows.h
FILE: ../../../flutter/shell/platform/windows/text_input_plugin.cc
FILE: ../../../flutter/shell/platform/windows/text_input_plugin.h
FILE: ../../../flutter/shell/platform/windows/win32_dpi_helper.cc
FILE: ../../../flutter/shell/platform/windows/win32_dpi_helper.h
FILE: ../../../flutter/shell/platform/windows/win32_flutter_window.cc
FILE: ../../../flutter/shell/platform/windows/win32_flutter_window.h
FILE: ../../../flutter/shell/platform/windows/win32_window.cc
FILE: ../../../flutter/shell/platform/windows/win32_window.h
FILE: ../../../flutter/shell/platform/windows/window_state.h
FILE: ../../../flutter/shell/version/version.cc
FILE: ../../../flutter/shell/version/version.h
FILE: ../../../flutter/sky/packages/flutter_services/lib/empty.dart
......
因为 它太大了无法显示 source diff 。你可以改为 查看blob
......@@ -3,43 +3,12 @@
# found in the LICENSE file.
import("$flutter_root/testing/testing.gni")
import("publish.gni")
_wrapper_includes = [
"include/flutter/basic_message_channel.h",
"include/flutter/binary_messenger.h",
"include/flutter/encodable_value.h",
"include/flutter/engine_method_result.h",
"include/flutter/json_message_codec.h",
"include/flutter/json_method_codec.h",
"include/flutter/json_type.h",
"include/flutter/message_codec.h",
"include/flutter/method_call.h",
"include/flutter/method_channel.h",
"include/flutter/method_codec.h",
"include/flutter/method_result.h",
"include/flutter/plugin_registrar.h",
"include/flutter/standard_message_codec.h",
"include/flutter/standard_method_codec.h",
]
# TODO: Once the wrapper API is more stable, consolidate to as few files as is
# reasonable (without forcing different kinds of clients to take unnecessary
# code) to simplify use.
_wrapper_sources = [
"byte_stream_wrappers.h",
"engine_method_result.cc",
"json_message_codec.cc", # TODO combine into a single json_codec.cc.
"json_method_codec.cc", # TODO combine into a single json_codec.cc.
"plugin_registrar.cc",
"standard_codec_serializer.h",
"standard_codec.cc",
]
import("core_wrapper_files.gni")
# Client library build for internal use by the shell implementation.
source_set("client_wrapper") {
sources = _wrapper_sources
public = _wrapper_includes
sources = core_cpp_client_wrapper_sources
public = core_cpp_client_wrapper_includes
deps = [
"$flutter_root/shell/platform/common/cpp:common_cpp_library_headers",
......@@ -57,12 +26,6 @@ source_set("client_wrapper") {
]
}
# Copies the client wrapper code to the output directory.
publish_client_wrapper("publish_wrapper") {
public = _wrapper_includes
sources = _wrapper_sources + [ "README" ]
}
source_set("client_wrapper_library_stubs") {
sources = [
"testing/stub_flutter_api.cc",
......
# Copyright 2013 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
core_cpp_client_wrapper_includes =
get_path_info([
"include/flutter/basic_message_channel.h",
"include/flutter/binary_messenger.h",
"include/flutter/encodable_value.h",
"include/flutter/engine_method_result.h",
"include/flutter/json_message_codec.h",
"include/flutter/json_method_codec.h",
"include/flutter/json_type.h",
"include/flutter/message_codec.h",
"include/flutter/method_call.h",
"include/flutter/method_channel.h",
"include/flutter/method_codec.h",
"include/flutter/method_result.h",
"include/flutter/plugin_registrar.h",
"include/flutter/standard_message_codec.h",
"include/flutter/standard_method_codec.h",
],
"abspath")
# TODO: Once the wrapper API is more stable, consolidate to as few files as is
# reasonable (without forcing different kinds of clients to take unnecessary
# code) to simplify use.
core_cpp_client_wrapper_sources =
get_path_info(
[
"byte_stream_wrappers.h",
"engine_method_result.cc",
"json_message_codec.cc", # TODO combine into a single json_codec.cc.
"json_method_codec.cc", # TODO combine into a single json_codec.cc.
"plugin_registrar.cc",
"standard_codec_serializer.h",
"standard_codec.cc",
],
"abspath")
......@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("core_wrapper_files.gni")
# Publishes client wrapper files to the output directory for distribution.
# This can be used multiple times to combine various portions of a wrapper
# library into one cohesive library for clients to consume.
......@@ -11,15 +13,28 @@
#
# All public code is assumed to be in the 'flutter' namespace.
template("publish_client_wrapper") {
forward_variables_from(invoker, [ "directory_suffix" ])
if (defined(directory_suffix)) {
publish_dir_root = "$root_out_dir/cpp_client_wrapper_$directory_suffix"
} else {
publish_dir_root = "$root_out_dir/cpp_client_wrapper"
}
template_target_name = target_name
publish_dir_root = "$root_out_dir/cpp_client_wrapper"
namespace = "flutter"
group(template_target_name) {
forward_variables_from(invoker,
[
"public_deps",
"visibility",
])
deps = [
":${template_target_name}_publish_includes",
":${template_target_name}_publish_sources",
]
if (defined(invoker.deps)) {
deps += invoker.deps
}
}
copy("${template_target_name}_publish_includes") {
......@@ -50,3 +65,46 @@ template("publish_client_wrapper") {
]
}
}
_wrapper_readme = get_path_info("README", "abspath")
# Copies the client wrapper code to the output directory.
template("publish_client_wrapper_core") {
publish_client_wrapper(target_name) {
forward_variables_from(invoker,
[
"directory_suffix",
"visibility",
])
public = core_cpp_client_wrapper_includes
sources = core_cpp_client_wrapper_sources + [ _wrapper_readme ]
}
}
# A wrapper for publish_client_wrapper that will also
# publish_client_wrapper_core into the same directory.
#
# This is a convenience utility for the common case of wanting to publish
# the core wrapper and a single set of extra wrapper files corresponding to
# the platform.
template("publish_client_wrapper_extension") {
extension_target_name = target_name
core_target_name = "${target_name}_core"
publish_client_wrapper_core(core_target_name) {
visibility = [ ":$extension_target_name" ]
forward_variables_from(invoker, [ "directory_suffix" ])
}
publish_client_wrapper(extension_target_name) {
forward_variables_from(invoker,
[
"public",
"sources",
"directory_suffix",
])
public_deps = [
":$core_target_name",
]
}
}
......@@ -16,7 +16,6 @@ group("macos") {
if (build_glfw_shell) {
deps += [
":flutter_macos_glfw",
"$flutter_root/shell/platform/common/cpp/client_wrapper:publish_wrapper",
"$flutter_root/shell/platform/glfw:publish_headers_glfw",
"$flutter_root/shell/platform/glfw/client_wrapper:publish_wrapper_glfw",
]
......
......@@ -44,13 +44,22 @@ source_set("client_wrapper_glfw") {
]
}
# Copies the GLFW client wrapper code to the output directory, merging it into
# the core wrapper.
publish_client_wrapper("publish_wrapper_glfw") {
# Copies the GLFW client wrapper code to the output directory in the legacy
# unsuffixed location.
# TODO: Remove this copy once build recipes are using the _glfw version below.
publish_client_wrapper_extension("publish_wrapper_unprefixed") {
public = _wrapper_includes
sources = _wrapper_sources
}
# Copies the GLFW client wrapper code to the output directory with a _glfw
# suffix.
publish_client_wrapper_extension("publish_wrapper_glfw") {
public = _wrapper_includes
sources = _wrapper_sources
directory_suffix = "glfw"
}
source_set("client_wrapper_library_stubs_glfw") {
sources = [
"testing/stub_flutter_glfw_api.cc",
......
......@@ -10,9 +10,9 @@ group("linux") {
if (build_glfw_shell) {
deps = [
":flutter_linux",
"$flutter_root/shell/platform/common/cpp/client_wrapper:publish_wrapper",
"$flutter_root/shell/platform/glfw:publish_headers_glfw",
"$flutter_root/shell/platform/glfw/client_wrapper:publish_wrapper_glfw",
"$flutter_root/shell/platform/glfw/client_wrapper:publish_wrapper_unprefixed",
]
}
}
......
......@@ -4,15 +4,102 @@
assert(is_win)
group("windows") {
import("$flutter_root/shell/platform/glfw/config.gni")
_public_headers = [ "public/flutter_windows.h" ]
config("relative_angle_headers") {
include_dirs = [ "//third_party/angle/include" ]
}
# Any files that are built by clients (client_wrapper code, library headers for
# implementations using this shared code, etc.) include the public headers
# assuming they are in the include path. This configuration should be added to
# any such code that is also built by GN to make the includes work.
config("relative_flutter_windows_headers") {
include_dirs = [ "public" ]
}
# The headers are a separate source set since the client wrapper is allowed
# to depend on the public headers, but none of the rest of the code.
source_set("flutter_windows_headers") {
public = _public_headers
public_deps = [
"$flutter_root/shell/platform/common/cpp:common_cpp_library_headers",
]
configs += [
"$flutter_root/shell/platform/common/cpp:desktop_library_implementation",
]
public_configs = [
"$flutter_root/shell/platform/common/cpp:relative_flutter_library_headers",
]
}
source_set("flutter_windows_source") {
sources = [
"angle_surface_manager.cc",
"angle_surface_manager.h",
"flutter_windows.cc",
"key_event_handler.cc",
"key_event_handler.h",
"keyboard_hook_handler.h",
"platform_handler.cc",
"platform_handler.h",
"text_input_plugin.cc",
"text_input_plugin.h",
"win32_dpi_helper.cc",
"win32_dpi_helper.h",
"win32_flutter_window.cc",
"win32_flutter_window.h",
"win32_window.cc",
"win32_window.h",
"window_state.h",
]
defines = [ "USE_RAPID_JSON" ]
configs += [
"$flutter_root/shell/platform/common/cpp:desktop_library_implementation",
"//third_party/angle:gl_prototypes",
]
public_configs = [ ":relative_angle_headers" ]
deps = [
":flutter_windows",
"$flutter_root/shell/platform/common/cpp/client_wrapper:publish_wrapper",
"$flutter_root/shell/platform/glfw:publish_headers_glfw",
"$flutter_root/shell/platform/glfw/client_wrapper:publish_wrapper_glfw",
":flutter_windows_headers",
"$flutter_root/shell/platform/common/cpp:common_cpp",
"$flutter_root/shell/platform/common/cpp/client_wrapper:client_wrapper",
"$flutter_root/shell/platform/embedder:embedder_with_symbol_prefix",
"$flutter_root/shell/platform/windows/client_wrapper:client_wrapper_windows",
"//third_party/angle:libEGL_static", # the order of libEGL_static and libGLESv2_static is important.. if reversed, will cause a linker error DllMain already defined in LIBCMTD.lib
"//third_party/angle:libGLESv2_static",
"//third_party/rapidjson",
]
}
copy("publish_headers_windows") {
sources = _public_headers
outputs = [
"$root_out_dir/{{source_file_part}}",
]
# The Windows header assumes the presence of the common headers.
deps = [
"$flutter_root/shell/platform/common/cpp:publish_headers",
]
}
shared_library("flutter_windows_win32") {
deps = [
":flutter_windows_source",
]
public_configs = [ "$flutter_root:config" ]
}
shared_library("flutter_windows") {
deps = [
"$flutter_root/shell/platform/glfw:flutter_glfw",
......@@ -20,3 +107,29 @@ shared_library("flutter_windows") {
public_configs = [ "$flutter_root:config" ]
}
group("windows_win32") {
deps = [
":flutter_windows_win32",
":publish_headers_windows",
"$flutter_root/shell/platform/windows/client_wrapper:publish_wrapper_windows",
]
}
group("windows_glfw") {
deps = [
":flutter_windows",
"$flutter_root/shell/platform/glfw:publish_headers_glfw",
"$flutter_root/shell/platform/glfw/client_wrapper:publish_wrapper_glfw",
"$flutter_root/shell/platform/glfw/client_wrapper:publish_wrapper_unprefixed",
]
}
group("windows") {
deps = [
":windows_win32",
]
if (build_glfw_shell) {
deps += [ ":windows_glfw" ]
}
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/windows/angle_surface_manager.h"
namespace flutter {
AngleSurfaceManager::AngleSurfaceManager()
: egl_config_(nullptr),
egl_display_(EGL_NO_DISPLAY),
egl_context_(EGL_NO_CONTEXT) {
initialize_succeeded_ = Initialize();
}
AngleSurfaceManager::~AngleSurfaceManager() {
CleanUp();
}
bool AngleSurfaceManager::Initialize() {
const EGLint configAttributes[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8,
EGL_NONE};
const EGLint display_context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE};
const EGLint default_display_attributes[] = {
// These are prefered display attributes and request ANGLE's D3D11
// renderer. eglInitialize will only succeed with these attributes if the
// hardware supports D3D11 Feature Level 10_0+.
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
// EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that will
// enable ANGLE to automatically call the IDXGIDevice3::Trim method on
// behalf of the application when it gets suspended.
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
EGL_TRUE,
EGL_NONE,
};
const EGLint fl9_3_display_attributes[] = {
// These are used to request ANGLE's D3D11 renderer, with D3D11 Feature
// Level 9_3.
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
9,
EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE,
3,
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
EGL_TRUE,
EGL_NONE,
};
const EGLint warp_display_attributes[] = {
// These attributes request D3D11 WARP (software rendering fallback) as a
// last resort.
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
EGL_TRUE,
EGL_NONE,
};
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
eglGetProcAddress("eglGetPlatformDisplayEXT"));
if (!eglGetPlatformDisplayEXT) {
OutputDebugString(L"EGL: Failed to get a compatible EGLdisplay");
return false;
}
// Try to initialize EGL to D3D11 Feature Level 10_0+.
egl_display_ =
eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY,
default_display_attributes);
if (egl_display_ == EGL_NO_DISPLAY) {
OutputDebugString(L"EGL: Failed to get a compatible EGLdisplay");
return false;
}
if (eglInitialize(egl_display_, nullptr, nullptr) == EGL_FALSE) {
// If above failed, try to initialize EGL to D3D11 Feature Level 9_3, if
// 10_0+ is unavailable.
egl_display_ =
eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY,
fl9_3_display_attributes);
if (egl_display_ == EGL_NO_DISPLAY) {
OutputDebugString(L"EGL: Failed to get a compatible EGLdisplay");
return false;
}
if (eglInitialize(egl_display_, nullptr, nullptr) == EGL_FALSE) {
// If all else fails, attempt D3D11 Feature Level 11_0 on WARP as a last
// resort
egl_display_ = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
EGL_DEFAULT_DISPLAY,
warp_display_attributes);
if (egl_display_ == EGL_NO_DISPLAY) {
OutputDebugString(L"EGL: Failed to get a compatible EGLdisplay");
return false;
}
if (eglInitialize(egl_display_, nullptr, nullptr) == EGL_FALSE) {
OutputDebugString(L"EGL: Failed to initialize EGL");
return false;
}
}
}
EGLint numConfigs = 0;
if ((eglChooseConfig(egl_display_, configAttributes, &egl_config_, 1,
&numConfigs) == EGL_FALSE) ||
(numConfigs == 0)) {
OutputDebugString(L"EGL: Failed to choose first context");
return false;
}
egl_context_ = eglCreateContext(egl_display_, egl_config_, EGL_NO_CONTEXT,
display_context_attributes);
if (egl_context_ == EGL_NO_CONTEXT) {
OutputDebugString(L"EGL: Failed to create EGL context");
return false;
}
return true;
}
void AngleSurfaceManager::CleanUp() {
if (egl_display_ != EGL_NO_DISPLAY && egl_context_ != EGL_NO_CONTEXT) {
eglDestroyContext(egl_display_, egl_context_);
egl_context_ = EGL_NO_CONTEXT;
}
if (egl_display_ != EGL_NO_DISPLAY) {
eglTerminate(egl_display_);
egl_display_ = EGL_NO_DISPLAY;
}
}
EGLSurface AngleSurfaceManager::CreateSurface(HWND window) {
if (!window || !initialize_succeeded_) {
return EGL_NO_SURFACE;
}
EGLSurface surface = EGL_NO_SURFACE;
const EGLint surfaceAttributes[] = {EGL_NONE};
surface = eglCreateWindowSurface(egl_display_, egl_config_,
static_cast<EGLNativeWindowType>(window),
surfaceAttributes);
return surface;
}
void AngleSurfaceManager::GetSurfaceDimensions(const EGLSurface surface,
EGLint* width,
EGLint* height) {
if (surface == EGL_NO_SURFACE || !initialize_succeeded_) {
width = 0;
height = 0;
return;
}
eglQuerySurface(egl_display_, surface, EGL_WIDTH, width);
eglQuerySurface(egl_display_, surface, EGL_HEIGHT, height);
}
void AngleSurfaceManager::DestroySurface(const EGLSurface surface) {
if (egl_display_ != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE) {
eglDestroySurface(egl_display_, surface);
}
}
bool AngleSurfaceManager::MakeCurrent(const EGLSurface surface) {
return (eglMakeCurrent(egl_display_, surface, surface, egl_context_) ==
EGL_TRUE);
}
EGLBoolean AngleSurfaceManager::SwapBuffers(const EGLSurface surface) {
return (eglSwapBuffers(egl_display_, surface));
}
} // namespace flutter
\ No newline at end of file
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_ANGLE_SURFACE_MANAGER_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_ANGLE_SURFACE_MANAGER_H_
// OpenGL ES and EGL includes
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
// Windows platform specific includes
#include <windows.h>
namespace flutter {
// An manager for inializing ANGLE correctly and using it to create and
// destroy surfaces
class AngleSurfaceManager {
public:
AngleSurfaceManager();
~AngleSurfaceManager();
// Disallow copy/move.
AngleSurfaceManager(const AngleSurfaceManager&) = delete;
AngleSurfaceManager& operator=(const AngleSurfaceManager&) = delete;
// Creates and returns an EGLSurface wrapper and backing DirectX 11 SwapChain
// asociated with window, in the appropriate format for display in a
// HWND-backed window.
EGLSurface CreateSurface(HWND window);
// queries EGL for the dimensions of surface in physical
// pixels returning width and height as out params.
void GetSurfaceDimensions(const EGLSurface surface,
EGLint* width,
EGLint* height);
// Releases the pass-in EGLSurface wrapping and backing resources if not null.
void DestroySurface(const EGLSurface surface);
// Binds egl_context_ to the current rendering thread and to the draw and read
// surfaces returning a boolean result reflecting success.
bool MakeCurrent(const EGLSurface surface);
// Swaps the front and back buffers of the DX11 swapchain backing surface if
// not null.
EGLBoolean SwapBuffers(const EGLSurface surface);
private:
bool Initialize();
void CleanUp();
private:
// EGL representation of native display
EGLDisplay egl_display_;
// EGL representation of current rendering context
EGLContext egl_context_;
// current frame buffer configuration
EGLConfig egl_config_;
// State representing success or failure of display initialization used when
// creating surfaces.
bool initialize_succeeded_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_ANGLE_SURFACE_MANAGER_H_
# Copyright 2013 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("$flutter_root/shell/platform/common/cpp/client_wrapper/publish.gni")
import("$flutter_root/testing/testing.gni")
_wrapper_includes = [ "include/flutter/flutter_window_controller.h" ]
_wrapper_sources = [ "flutter_window_controller.cc" ]
# This code will be merged into .../common/cpp/client_wrapper for client use,
# so uses header paths that assume the merged state. Include the header
# header directory of the core wrapper files so these includes will work.
config("relative_core_wrapper_headers") {
include_dirs = [
"$flutter_root/shell/platform/common/cpp/client_wrapper/include/flutter",
]
}
# Windows client wrapper build for internal use by the shell implementation.
source_set("client_wrapper_windows") {
sources = _wrapper_sources
public = _wrapper_includes
deps = [
"$flutter_root/shell/platform/common/cpp:common_cpp_library_headers",
"$flutter_root/shell/platform/common/cpp/client_wrapper:client_wrapper",
"$flutter_root/shell/platform/windows:flutter_windows_headers",
]
configs += [
"$flutter_root/shell/platform/common/cpp:desktop_library_implementation",
]
public_configs = [
":relative_core_wrapper_headers",
"$flutter_root/shell/platform/common/cpp:relative_flutter_library_headers",
"$flutter_root/shell/platform/windows:relative_flutter_windows_headers",
]
}
# Copies the Windows client wrapper code to the output directory with a _windows
# suffix.
publish_client_wrapper_extension("publish_wrapper_windows") {
public = _wrapper_includes
sources = _wrapper_sources
directory_suffix = "windows"
}
source_set("client_wrapper_library_stubs_windows") {
sources = [
"testing/stub_flutter_windows_api.cc",
"testing/stub_flutter_windows_api.h",
]
defines = [ "FLUTTER_DESKTOP_LIBRARY" ]
public_deps = [
"$flutter_root/shell/platform/windows:flutter_windows_headers",
]
}
test_fixtures("client_wrapper_windows_fixtures") {
fixtures = []
}
executable("client_wrapper_windows_unittests") {
testonly = true
# TODO: Add more unit tests.
sources = [
"flutter_window_controller_unittests.cc",
]
deps = [
":client_wrapper_library_stubs_windows",
":client_wrapper_windows",
":client_wrapper_windows_fixtures",
"$flutter_root/shell/platform/common/cpp/client_wrapper:client_wrapper_library_stubs",
"$flutter_root/testing",
# TODO: Consider refactoring flutter_root/testing so that there's a testing
# target that doesn't require a Dart runtime to be linked in.
"//third_party/dart/runtime:libdart_jit",
]
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "include/flutter/flutter_window_controller.h"
#include <algorithm>
#include <iostream>
namespace flutter {
FlutterWindowController::FlutterWindowController(
const std::string& icu_data_path)
: icu_data_path_(icu_data_path) {
init_succeeded_ = FlutterDesktopInit();
}
FlutterWindowController::~FlutterWindowController() {
if (controller_) {
FlutterDesktopDestroyWindow(controller_);
}
if (init_succeeded_) {
FlutterDesktopTerminate();
}
}
bool FlutterWindowController::CreateWindow(
int width,
int height,
const std::string& title,
const std::string& assets_path,
const std::vector<std::string>& arguments) {
if (!init_succeeded_) {
std::cerr << "Could not create window; FlutterDesktopInit failed."
<< std::endl;
return false;
}
if (controller_) {
std::cerr << "Only one Flutter window can exist at a time." << std::endl;
return false;
}
std::vector<const char*> engine_arguments;
std::transform(
arguments.begin(), arguments.end(), std::back_inserter(engine_arguments),
[](const std::string& arg) -> const char* { return arg.c_str(); });
size_t arg_count = engine_arguments.size();
controller_ = FlutterDesktopCreateWindow(
width, height, title.c_str(), assets_path.c_str(), icu_data_path_.c_str(),
arg_count > 0 ? &engine_arguments[0] : nullptr, arg_count);
if (!controller_) {
std::cerr << "Failed to create window." << std::endl;
return false;
}
window_ =
std::make_unique<FlutterWindow>(FlutterDesktopGetWindow(controller_));
return true;
}
FlutterDesktopPluginRegistrarRef FlutterWindowController::GetRegistrarForPlugin(
const std::string& plugin_name) {
if (!controller_) {
std::cerr << "Cannot get plugin registrar without a window; call "
"CreateWindow first."
<< std::endl;
return nullptr;
}
return FlutterDesktopGetPluginRegistrar(controller_, plugin_name.c_str());
}
void FlutterWindowController::RunEventLoop() {
if (controller_) {
FlutterDesktopRunWindowLoop(controller_);
}
window_ = nullptr;
controller_ = nullptr;
}
} // namespace flutter
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include "flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_window_controller.h"
#include "flutter/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h"
#include "gtest/gtest.h"
namespace flutter {
namespace {
// Stub implementation to validate calls to the API.
class TestWindowsApi : public testing::StubFlutterWindowsApi {
public:
// |flutter::testing::StubFlutterWindowsApi|
bool Init() override {
init_called_ = true;
return true;
}
// |flutter::testing::StubFlutterWindowsApi|
void Terminate() override { terminate_called_ = true; }
bool init_called() { return init_called_; }
bool terminate_called() { return terminate_called_; }
private:
bool init_called_ = false;
bool terminate_called_ = false;
};
} // namespace
TEST(FlutterViewControllerTest, CreateDestroy) {
const std::string icu_data_path = "fake/path/to/icu";
testing::ScopedStubFlutterWindowsApi scoped_api_stub(
std::make_unique<TestWindowsApi>());
auto test_api = static_cast<TestWindowsApi*>(scoped_api_stub.stub());
{
FlutterWindowController controller(icu_data_path);
EXPECT_EQ(test_api->init_called(), true);
EXPECT_EQ(test_api->terminate_called(), false);
}
EXPECT_EQ(test_api->init_called(), true);
EXPECT_EQ(test_api->terminate_called(), true);
}
} // namespace flutter
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_H_
#include <flutter_windows.h>
#include <string>
#include <vector>
#include "plugin_registrar.h"
namespace flutter {
// A data type for window position and size.
struct WindowFrame {
int left;
int top;
int width;
int height;
};
// A window displaying Flutter content.
class FlutterWindow {
public:
explicit FlutterWindow(FlutterDesktopWindowRef window) : window_(window) {}
~FlutterWindow() = default;
// Prevent copying.
FlutterWindow(FlutterWindow const&) = delete;
FlutterWindow& operator=(FlutterWindow const&) = delete;
// Enables or disables hover tracking.
//
// If hover is enabled, mouse movement will send hover events to the Flutter
// engine, rather than only tracking the mouse while the button is pressed.
// Defaults to off.
void SetHoverEnabled(bool enabled) {
FlutterDesktopWindowSetHoverEnabled(window_, enabled);
}
// Sets the displayed title of the window.
void SetTitle(const std::string& title) {
FlutterDesktopWindowSetTitle(window_, title.c_str());
}
// Sets the displayed icon for the window.
//
// The pixel format is 32-bit RGBA. The provided image data only needs to be
// valid for the duration of the call to this method. Pass a nullptr to revert
// to the default icon.
void SetIcon(uint8_t* pixel_data, int width, int height) {
FlutterDesktopWindowSetIcon(window_, pixel_data, width, height);
}
// Returns the frame of the window, including any decoration (e.g., title
// bar), in screen coordinates.
WindowFrame GetFrame() {
WindowFrame frame = {};
FlutterDesktopWindowGetFrame(window_, &frame.left, &frame.top, &frame.width,
&frame.height);
return frame;
}
// Set the frame of the window, including any decoration (e.g., title
// bar), in screen coordinates.
void SetFrame(const WindowFrame& frame) {
FlutterDesktopWindowSetFrame(window_, frame.left, frame.top, frame.width,
frame.height);
}
// Returns the number of pixels per screen coordinate for the window.
//
// Flutter uses pixel coordinates, so this is the ratio of positions and sizes
// seen by Flutter as compared to the screen.
double GetScaleFactor() {
return FlutterDesktopWindowGetScaleFactor(window_);
}
private:
// Handle for interacting with the C API's window.
//
// Note: window_ is conceptually owned by the controller, not this object.
FlutterDesktopWindowRef window_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_CONTROLLER_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_CONTROLLER_H_
#include <flutter_windows.h>
#include <string>
#include <vector>
#include "flutter_window.h"
#include "plugin_registrar.h"
namespace flutter {
// A controller for a window displaying Flutter content.
//
// This is the primary wrapper class for the desktop C API.
// If you use this class, you should not call any of the setup or teardown
// methods in the C API directly, as this class will do that internally.
//
// Note: This is an early implementation which
// requires control of the application's event loop, and is thus useful
// primarily for building a simple one-window shell hosting a Flutter
// application. The final implementation and API will be very different.
class FlutterWindowController {
public:
// There must be only one instance of this class in an application at any
// given time, as Flutter does not support multiple engines in one process,
// or multiple views in one engine.
explicit FlutterWindowController(const std::string& icu_data_path);
~FlutterWindowController();
// Prevent copying.
FlutterWindowController(FlutterWindowController const&) = delete;
FlutterWindowController& operator=(FlutterWindowController const&) = delete;
// Creates and displays a window for displaying Flutter content.
//
// The |assets_path| is the path to the flutter_assets folder for the Flutter
// application to be run. |icu_data_path| is the path to the icudtl.dat file
// for the version of Flutter you are using.
//
// The |arguments| are passed to the Flutter engine. See:
// https://github.com/flutter/engine/blob/master/shell/common/switches.h for
// for details. Not all arguments will apply to desktop.
//
// Only one Flutter window can exist at a time; see constructor comment.
bool CreateWindow(int width,
int height,
const std::string& title,
const std::string& assets_path,
const std::vector<std::string>& arguments);
// Returns the FlutterDesktopPluginRegistrarRef to register a plugin with the
// given name.
//
// The name must be unique across the application.
FlutterDesktopPluginRegistrarRef GetRegistrarForPlugin(
const std::string& plugin_name);
// The FlutterWindow managed by this controller, if any. Returns nullptr
// before CreateWindow is called, and after RunEventLoop returns;
FlutterWindow* window() { return window_.get(); }
// Loops on Flutter window events until the window closes.
void RunEventLoop();
private:
// The path to the ICU data file. Set at creation time since it is the same
// for any window created.
std::string icu_data_path_;
// Whether or not FlutterDesktopInit succeeded at creation time.
bool init_succeeded_ = false;
// The owned FlutterWindow, if any.
std::unique_ptr<FlutterWindow> window_;
// Handle for interacting with the C API's window controller, if any.
FlutterDesktopWindowControllerRef controller_ = nullptr;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_CONTROLLER_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_WINDOWS_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_WINDOWS_H_
#include <flutter_windows.h>
#include <memory>
#include "flutter_window.h"
#include "plugin_registrar.h"
namespace flutter {
// An extension to PluginRegistrar providing access to windows-shell-specific
// functionality.
class PluginRegistrarWindows : public PluginRegistrar {
public:
// Creates a new PluginRegistrar. |core_registrar| and the messenger it
// provides must remain valid as long as this object exists.
explicit PluginRegistrarWindows(
FlutterDesktopPluginRegistrarRef core_registrar)
: PluginRegistrar(core_registrar) {
window_ = std::make_unique<FlutterWindow>(
FlutterDesktopRegistrarGetWindow(core_registrar));
}
virtual ~PluginRegistrarWindows() = default;
// Prevent copying.
PluginRegistrarWindows(PluginRegistrarWindows const&) = delete;
PluginRegistrarWindows& operator=(PluginRegistrarWindows const&) = delete;
FlutterWindow* window() { return window_.get(); }
private:
// The owned FlutterWindow, if any.
std::unique_ptr<FlutterWindow> window_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_WINDOWS_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h"
static flutter::testing::StubFlutterWindowsApi* s_stub_implementation;
namespace flutter {
namespace testing {
// static
void StubFlutterWindowsApi::SetTestStub(StubFlutterWindowsApi* stub) {
s_stub_implementation = stub;
}
// static
StubFlutterWindowsApi* StubFlutterWindowsApi::GetTestStub() {
return s_stub_implementation;
}
ScopedStubFlutterWindowsApi::ScopedStubFlutterWindowsApi(
std::unique_ptr<StubFlutterWindowsApi> stub)
: stub_(std::move(stub)) {
previous_stub_ = StubFlutterWindowsApi::GetTestStub();
StubFlutterWindowsApi::SetTestStub(stub_.get());
}
ScopedStubFlutterWindowsApi::~ScopedStubFlutterWindowsApi() {
StubFlutterWindowsApi::SetTestStub(previous_stub_);
}
} // namespace testing
} // namespace flutter
// Forwarding dummy implementations of the C API.
bool FlutterDesktopInit() {
if (s_stub_implementation) {
s_stub_implementation->Init();
}
return true;
}
void FlutterDesktopTerminate() {
if (s_stub_implementation) {
s_stub_implementation->Terminate();
}
}
FlutterDesktopWindowControllerRef FlutterDesktopCreateWindow(
int initial_width,
int initial_height,
const char* title,
const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t argument_count) {
if (s_stub_implementation) {
return s_stub_implementation->CreateWindow(
initial_width, initial_height, title, assets_path, icu_data_path,
arguments, argument_count);
}
return nullptr;
}
void FlutterDesktopDestroyWindow(FlutterDesktopWindowControllerRef controller) {
if (s_stub_implementation) {
s_stub_implementation->DestroyWindow();
}
}
void FlutterDesktopSetHoverEnabled(FlutterDesktopWindowRef flutter_window,
bool enabled) {
if (s_stub_implementation) {
s_stub_implementation->SetHoverEnabled(enabled);
}
}
void FlutterDesktopSetWindowTitle(FlutterDesktopWindowRef flutter_window,
const char* title) {
if (s_stub_implementation) {
s_stub_implementation->SetWindowTitle(title);
}
}
void FlutterDesktopSetWindowIcon(FlutterDesktopWindowRef flutter_window,
uint8_t* pixel_data,
int width,
int height) {
if (s_stub_implementation) {
s_stub_implementation->SetWindowIcon(pixel_data, width, height);
}
}
void FlutterDesktopRunWindowLoop(FlutterDesktopWindowControllerRef controller) {
if (s_stub_implementation) {
s_stub_implementation->RunWindowLoop();
}
}
FlutterDesktopEngineRef FlutterDesktopRunEngine(const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t argument_count) {
if (s_stub_implementation) {
return s_stub_implementation->RunEngine(assets_path, icu_data_path,
arguments, argument_count);
}
return nullptr;
}
bool FlutterDesktopShutDownEngine(FlutterDesktopEngineRef engine_ref) {
if (s_stub_implementation) {
return s_stub_implementation->ShutDownEngine();
}
return true;
}
FlutterDesktopWindowRef FlutterDesktopGetWindow(
FlutterDesktopWindowControllerRef controller) {
// The stub ignores this, so just return an arbitrary non-zero value.
return reinterpret_cast<FlutterDesktopWindowRef>(1);
}
FlutterDesktopPluginRegistrarRef FlutterDesktopGetPluginRegistrar(
FlutterDesktopWindowControllerRef controller,
const char* plugin_name) {
// The stub ignores this, so just return an arbitrary non-zero value.
return reinterpret_cast<FlutterDesktopPluginRegistrarRef>(1);
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_WRAPPER_TESTING_STUB_FLUTTER_WINDOWS_API_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_WRAPPER_TESTING_STUB_FLUTTER_WINDOWS_API_H_
#include <memory>
#include "flutter/shell/platform/windows/public/flutter_windows.h"
namespace flutter {
namespace testing {
// Base class for a object that provides test implementations of the APIs in
// the headers in platform/windows/public/.
// Linking this class into a test binary will provide dummy forwarding
// implementantions of that C API, so that the wrapper can be tested separately
// from the actual library.
class StubFlutterWindowsApi {
public:
// Sets |stub| as the instance to which calls to the Flutter library C APIs
// will be forwarded.
static void SetTestStub(StubFlutterWindowsApi* stub);
// Returns the current stub, as last set by SetTestFluttterStub.
static StubFlutterWindowsApi* GetTestStub();
virtual ~StubFlutterWindowsApi() {}
// Called for FlutterDesktopInit.
virtual bool Init() { return true; }
// Called for FlutterDesktopTerminate.
virtual void Terminate() {}
// Called for FlutterDesktopCreateWindow.
virtual FlutterDesktopWindowControllerRef CreateWindow(
int initial_width,
int initial_height,
const char* title,
const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t argument_count) {
return nullptr;
}
// Called for FlutterDesktopDestroyWindow.
virtual void DestroyWindow() {}
// Called for FlutterDesktopSetHoverEnabled.
virtual void SetHoverEnabled(bool enabled) {}
// Called for FlutterDesktopSetWindowTitle.
virtual void SetWindowTitle(const char* title) {}
// Called for FlutterDesktopSetWindowIcon.
virtual void SetWindowIcon(uint8_t* pixel_data, int width, int height) {}
// Called for FlutterDesktopRunWindowLoop.
virtual void RunWindowLoop() {}
// Called for FlutterDesktopRunEngine.
virtual FlutterDesktopEngineRef RunEngine(const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t argument_count) {
return nullptr;
}
// Called for FlutterDesktopShutDownEngine.
virtual bool ShutDownEngine() { return true; }
};
// A test helper that owns a stub implementation, making it the test stub for
// the lifetime of the object, then restoring the previous value.
class ScopedStubFlutterWindowsApi {
public:
// Calls SetTestFlutterStub with |stub|.
ScopedStubFlutterWindowsApi(std::unique_ptr<StubFlutterWindowsApi> stub);
// Restores the previous test stub.
~ScopedStubFlutterWindowsApi();
StubFlutterWindowsApi* stub() { return stub_.get(); }
private:
std::unique_ptr<StubFlutterWindowsApi> stub_;
// The previous stub.
StubFlutterWindowsApi* previous_stub_;
};
} // namespace testing
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_TESTING_STUB_FLUTTER_WINDOWS_API_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/windows/public/flutter_windows.h"
#include <assert.h>
#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <vector>
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h"
#include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/key_event_handler.h"
#include "flutter/shell/platform/windows/keyboard_hook_handler.h"
#include "flutter/shell/platform/windows/platform_handler.h"
#include "flutter/shell/platform/windows/text_input_plugin.h"
#include "flutter/shell/platform/windows/win32_flutter_window.h"
#include "flutter/shell/platform/windows/window_state.h"
static_assert(FLUTTER_ENGINE_VERSION == 1, "");
// Spins up an instance of the Flutter Engine.
//
// This function launches the Flutter Engine in a background thread, supplying
// the necessary callbacks for rendering within a win32window (if one is
// provided).
//
// Returns a caller-owned pointer to the engine.
static FLUTTER_API_SYMBOL(FlutterEngine)
RunFlutterEngine(flutter::Win32FlutterWindow* window,
const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t arguments_count) {
// FlutterProjectArgs is expecting a full argv, so when processing it for
// flags the first item is treated as the executable and ignored. Add a dummy
// value so that all provided arguments are used.
std::vector<const char*> argv = {"placeholder"};
if (arguments_count > 0) {
argv.insert(argv.end(), &arguments[0], &arguments[arguments_count]);
}
window->CreateRenderSurface();
FlutterRendererConfig config = {};
// Provide the necessary callbacks for rendering within a win32 window.
config.type = kOpenGL;
config.open_gl.struct_size = sizeof(config.open_gl);
config.open_gl.make_current = [](void* user_data) -> bool {
auto host = static_cast<flutter::Win32FlutterWindow*>(user_data);
return host->MakeCurrent();
};
config.open_gl.clear_current = [](void* user_data) -> bool {
auto host = static_cast<flutter::Win32FlutterWindow*>(user_data);
return host->ClearContext();
};
config.open_gl.present = [](void* user_data) -> bool {
auto host = static_cast<flutter::Win32FlutterWindow*>(user_data);
return host->SwapBuffers();
};
config.open_gl.fbo_callback = [](void* user_data) -> uint32_t { return 0; };
config.open_gl.gl_proc_resolver = [](void* user_data,
const char* what) -> void* {
return eglGetProcAddress(what);
};
FlutterProjectArgs args = {};
args.struct_size = sizeof(FlutterProjectArgs);
args.assets_path = assets_path;
args.icu_data_path = icu_data_path;
args.command_line_argc = static_cast<int>(argv.size());
args.command_line_argv = &argv[0];
args.platform_message_callback =
[](const FlutterPlatformMessage* engine_message,
void* user_data) -> void {
auto window = reinterpret_cast<flutter::Win32FlutterWindow*>(user_data);
return window->HandlePlatformMessage(engine_message);
};
// args.custom_task_runners = custom_task_runners; TODO
FLUTTER_API_SYMBOL(FlutterEngine) engine = nullptr;
auto result =
FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &args, window, &engine);
if (result != kSuccess || engine == nullptr) {
std::cerr << "Failed to start Flutter engine: error " << result
<< std::endl;
return nullptr;
}
return engine;
}
bool FlutterDesktopInit() {
return true;
}
void FlutterDesktopTerminate() {}
FlutterDesktopWindowControllerRef FlutterDesktopCreateWindow(
int initial_width,
int initial_height,
const char* title,
const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t argument_count) {
FlutterDesktopWindowControllerRef state =
flutter::Win32FlutterWindow::CreateWin32FlutterWindow(
title, 10, 10, initial_width, initial_height);
auto engine = RunFlutterEngine(state->window.get(), assets_path,
icu_data_path, arguments, argument_count);
if (engine == nullptr) {
return nullptr;
}
state->window->SetState(engine);
// Trigger an initial size callback to send size information to Flutter.
state->window->SendWindowMetrics();
return state;
}
void FlutterDesktopDestroyWindow(FlutterDesktopWindowControllerRef controller) {
FlutterEngineShutdown(controller->engine);
delete controller;
}
void FlutterDesktopWindowSetHoverEnabled(FlutterDesktopWindowRef flutter_window,
bool enabled) {
// todo either implement or remove once embedder project has moved
}
void FlutterDesktopWindowSetTitle(FlutterDesktopWindowRef flutter_window,
const char* title) {
// todo either implement or remove
}
void FlutterDesktopWindowSetIcon(FlutterDesktopWindowRef flutter_window,
uint8_t* pixel_data,
int width,
int height) {
// todo either implement or remove
}
void FlutterDesktopRunWindowLoop(FlutterDesktopWindowControllerRef controller) {
controller->window->FlutterMessageLoop();
FlutterDesktopDestroyWindow(controller);
}
FlutterDesktopWindowRef FlutterDesktopGetWindow(
FlutterDesktopWindowControllerRef controller) {
// Currently, one registrar acts as the registrar for all plugins, so the
// name is ignored. It is part of the API to reduce churn in the future when
// aligning more closely with the Flutter registrar system.
return controller->window_wrapper.get();
}
FlutterDesktopPluginRegistrarRef FlutterDesktopGetPluginRegistrar(
FlutterDesktopWindowControllerRef controller,
const char* plugin_name) {
// Currently, one registrar acts as the registrar for all plugins, so the
// name is ignored. It is part of the API to reduce churn in the future when
// aligning more closely with the Flutter registrar system.
return controller->window->GetRegistrar();
}
FlutterDesktopEngineRef FlutterDesktopRunEngine(const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t argument_count) {
auto engine = RunFlutterEngine(nullptr, assets_path, icu_data_path, arguments,
argument_count);
if (engine == nullptr) {
return nullptr;
}
auto engine_state = new FlutterDesktopEngineState();
engine_state->engine = engine;
return engine_state;
}
bool FlutterDesktopShutDownEngine(FlutterDesktopEngineRef engine_ref) {
std::cout << "Shutting down flutter engine process." << std::endl;
auto result = FlutterEngineShutdown(engine_ref->engine);
delete engine_ref;
return (result == kSuccess);
}
void FlutterDesktopRegistrarEnableInputBlocking(
FlutterDesktopPluginRegistrarRef registrar,
const char* channel) {
registrar->messenger->dispatcher->EnableInputBlockingForChannel(channel);
}
FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(
FlutterDesktopPluginRegistrarRef registrar) {
return registrar->messenger.get();
}
bool FlutterDesktopMessengerSendWithReply(FlutterDesktopMessengerRef messenger,
const char* channel,
const uint8_t* message,
const size_t message_size,
const FlutterDesktopBinaryReply reply,
void* user_data) {
FlutterPlatformMessageResponseHandle* response_handle = nullptr;
if (reply != nullptr && user_data != nullptr) {
FlutterEngineResult result = FlutterPlatformMessageCreateResponseHandle(
messenger->engine, reply, user_data, &response_handle);
if (result != kSuccess) {
std::cout << "Failed to create response handle\n";
return false;
}
}
FlutterPlatformMessage platform_message = {
sizeof(FlutterPlatformMessage),
channel,
message,
message_size,
response_handle,
};
FlutterEngineResult message_result =
FlutterEngineSendPlatformMessage(messenger->engine, &platform_message);
if (response_handle != nullptr) {
FlutterPlatformMessageReleaseResponseHandle(messenger->engine,
response_handle);
}
return message_result == kSuccess;
}
bool FlutterDesktopMessengerSend(FlutterDesktopMessengerRef messenger,
const char* channel,
const uint8_t* message,
const size_t message_size) {
return FlutterDesktopMessengerSendWithReply(messenger, channel, message,
message_size, nullptr, nullptr);
}
void FlutterDesktopMessengerSendResponse(
FlutterDesktopMessengerRef messenger,
const FlutterDesktopMessageResponseHandle* handle,
const uint8_t* data,
size_t data_length) {
FlutterEngineSendPlatformMessageResponse(messenger->engine, handle, data,
data_length);
}
void FlutterDesktopMessengerSetCallback(FlutterDesktopMessengerRef messenger,
const char* channel,
FlutterDesktopMessageCallback callback,
void* user_data) {
messenger->dispatcher->SetMessageCallback(channel, callback, user_data);
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/windows/key_event_handler.h"
#include <windows.h>
#include <iostream>
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h"
static constexpr char kChannelName[] = "flutter/keyevent";
static constexpr char kKeyCodeKey[] = "keyCode";
static constexpr char kKeyMapKey[] = "keymap";
static constexpr char kTypeKey[] = "type";
static constexpr char kAndroidKeyMap[] = "android";
static constexpr char kKeyUp[] = "keyup";
static constexpr char kKeyDown[] = "keydown";
namespace flutter {
KeyEventHandler::KeyEventHandler(flutter::BinaryMessenger* messenger)
: channel_(
std::make_unique<flutter::BasicMessageChannel<rapidjson::Document>>(
messenger,
kChannelName,
&flutter::JsonMessageCodec::GetInstance())) {}
KeyEventHandler::~KeyEventHandler() = default;
void KeyEventHandler::CharHook(Win32FlutterWindow* window,
unsigned int code_point) {}
void KeyEventHandler::KeyboardHook(Win32FlutterWindow* window,
int key,
int scancode,
int action,
int mods) {
// TODO: Translate to a cross-platform key code system rather than passing
// the native key code.
rapidjson::Document event(rapidjson::kObjectType);
auto& allocator = event.GetAllocator();
event.AddMember(kKeyCodeKey, key, allocator);
event.AddMember(kKeyMapKey, kAndroidKeyMap, allocator);
switch (action) {
case WM_KEYDOWN:
event.AddMember(kTypeKey, kKeyDown, allocator);
break;
case WM_KEYUP:
event.AddMember(kTypeKey, kKeyUp, allocator);
break;
default:
std::cerr << "Unknown key event action: " << action << std::endl;
return;
}
channel_->Send(event);
}
} // namespace flutter
\ No newline at end of file
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_KEY_EVENT_HANDLER_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_KEY_EVENT_HANDLER_H_
#include <memory>
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h"
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h"
#include "flutter/shell/platform/windows/keyboard_hook_handler.h"
#include "flutter/shell/platform/windows/public/flutter_windows.h"
#include "rapidjson/document.h"
namespace flutter {
class Win32FlutterWindow;
// Implements a KeyboardHookHandler
//
// Handles key events and forwards them to the Flutter engine.
class KeyEventHandler : public KeyboardHookHandler {
public:
explicit KeyEventHandler(flutter::BinaryMessenger* messenger);
virtual ~KeyEventHandler();
// |KeyboardHookHandler|
void KeyboardHook(Win32FlutterWindow* window,
int key,
int scancode,
int action,
int mods) override;
// |KeyboardHookHandler|
void CharHook(Win32FlutterWindow* window, unsigned int code_point) override;
private:
// The Flutter system channel for key event messages.
std::unique_ptr<flutter::BasicMessageChannel<rapidjson::Document>> channel_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_KEY_EVENT_HANDLER_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_HOOK_HANDLER_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_HOOK_HANDLER_H_
#include "flutter/shell/platform/windows/public/flutter_windows.h"
namespace flutter {
class Win32FlutterWindow;
// Abstract class for handling keyboard input events.
class KeyboardHookHandler {
public:
virtual ~KeyboardHookHandler() = default;
// A function for hooking into keyboard input.
virtual void KeyboardHook(Win32FlutterWindow* window,
int key,
int scancode,
int action,
int mods) = 0;
// A function for hooking into unicode code point input.
virtual void CharHook(Win32FlutterWindow* window,
unsigned int code_point) = 0;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_HOOK_HANDLER_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/windows/platform_handler.h"
#include <windows.h>
#include <iostream>
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h"
static constexpr char kChannelName[] = "flutter/platform";
static constexpr char kGetClipboardDataMethod[] = "Clipboard.getData";
static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData";
static constexpr char kTextPlainFormat[] = "text/plain";
static constexpr char kTextKey[] = "text";
static constexpr char kUnknownClipboardFormatError[] =
"Unknown clipboard format error";
namespace flutter {
PlatformHandler::PlatformHandler(flutter::BinaryMessenger* messenger,
Win32FlutterWindow* window)
: channel_(std::make_unique<flutter::MethodChannel<rapidjson::Document>>(
messenger,
kChannelName,
&flutter::JsonMethodCodec::GetInstance())),
window_(window) {
channel_->SetMethodCallHandler(
[this](
const flutter::MethodCall<rapidjson::Document>& call,
std::unique_ptr<flutter::MethodResult<rapidjson::Document>> result) {
HandleMethodCall(call, std::move(result));
});
}
void PlatformHandler::HandleMethodCall(
const flutter::MethodCall<rapidjson::Document>& method_call,
std::unique_ptr<flutter::MethodResult<rapidjson::Document>> result) {
const std::string& method = method_call.method_name();
if (method.compare(kGetClipboardDataMethod) == 0) {
// Only one string argument is expected.
const rapidjson::Value& format = method_call.arguments()[0];
if (strcmp(format.GetString(), kTextPlainFormat) != 0) {
result->Error(kUnknownClipboardFormatError,
"Windows clipboard API only supports text.");
return;
}
auto clipboardData = GetClipboardString();
if (clipboardData.empty()) {
result->Error(kUnknownClipboardFormatError,
"Failed to retrieve clipboard data from win32 api.");
return;
}
rapidjson::Document document;
document.SetObject();
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
document.AddMember(rapidjson::Value(kTextKey, allocator),
rapidjson::Value(clipboardData, allocator), allocator);
result->Success(&document);
} else if (method.compare(kSetClipboardDataMethod) == 0) {
const rapidjson::Value& document = *method_call.arguments();
rapidjson::Value::ConstMemberIterator itr = document.FindMember(kTextKey);
if (itr == document.MemberEnd()) {
result->Error(kUnknownClipboardFormatError,
"Missing text to store on clipboard.");
return;
}
SetClipboardString(std::string(itr->value.GetString()));
result->Success();
} else {
result->NotImplemented();
}
}
std::string PlatformHandler::GetClipboardString() {
if (!OpenClipboard(nullptr)) {
return nullptr;
}
HANDLE data = GetClipboardData(CF_TEXT);
if (data == nullptr) {
CloseClipboard();
return nullptr;
}
const char* clipboardData = static_cast<char*>(GlobalLock(data));
if (clipboardData == nullptr) {
CloseClipboard();
return nullptr;
}
auto result = std::string(clipboardData);
GlobalUnlock(data);
CloseClipboard();
return result;
}
void PlatformHandler::SetClipboardString(std::string data) {
if (!OpenClipboard(nullptr)) {
return;
}
auto htext = GlobalAlloc(GMEM_MOVEABLE, data.size());
memcpy(GlobalLock(htext), data.c_str(), data.size());
SetClipboardData(CF_TEXT, htext);
CloseClipboard();
}
} // namespace flutter
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_PLATFORM_HANDLER_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_PLATFORM_HANDLER_H_
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h"
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h"
#include "flutter/shell/platform/windows/public/flutter_windows.h"
#include "rapidjson/document.h"
namespace flutter {
class Win32FlutterWindow;
// Handler for internal system channels.
class PlatformHandler {
public:
explicit PlatformHandler(flutter::BinaryMessenger* messenger,
Win32FlutterWindow* window);
private:
// Called when a method is called on |channel_|;
void HandleMethodCall(
const flutter::MethodCall<rapidjson::Document>& method_call,
std::unique_ptr<flutter::MethodResult<rapidjson::Document>> result);
// The MethodChannel used for communication with the Flutter engine.
std::unique_ptr<flutter::MethodChannel<rapidjson::Document>> channel_;
static std::string GetClipboardString();
static void SetClipboardString(std::string data);
// A reference to the win32 window.
Win32FlutterWindow* window_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_PLATFORM_HANDLER_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_PUBLIC_FLUTTER_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_PUBLIC_FLUTTER_H_
#include <stddef.h>
#include <stdint.h>
#include "flutter_export.h"
#include "flutter_messenger.h"
#include "flutter_plugin_registrar.h"
#if defined(__cplusplus)
extern "C" {
#endif
// Opaque reference to a Flutter window controller.
typedef struct FlutterDesktopWindowControllerState*
FlutterDesktopWindowControllerRef;
// Opaque reference to a Flutter window.
typedef struct FlutterDesktopWindow* FlutterDesktopWindowRef;
// Opaque reference to a Flutter engine instance.
typedef struct FlutterDesktopEngineState* FlutterDesktopEngineRef;
// TODO: remove once the embedder project has swiched to native windows
FLUTTER_EXPORT bool FlutterDesktopInit();
// TODO: remove once the embedder project has swiched to native windows
FLUTTER_EXPORT void FlutterDesktopTerminate();
// Creates a Window running a Flutter Application.
//
// FlutterDesktopInit() must be called prior to this function.
//
// The |assets_path| is the path to the flutter_assets folder for the Flutter
// application to be run. |icu_data_path| is the path to the icudtl.dat file
// for the version of Flutter you are using.
//
// The |arguments| are passed to the Flutter engine. See:
// https://github.com/flutter/engine/blob/master/shell/common/switches.h for
// for details. Not all arguments will apply to desktop.
//
// Returns a null pointer in the event of an error. Otherwise, the pointer is
// valid until FlutterDesktopRunWindowLoop has been called and returned.
// Note that calling FlutterDesktopCreateWindow without later calling
// FlutterDesktopRunWindowLoop on the returned reference is a memory leak.
FLUTTER_EXPORT FlutterDesktopWindowControllerRef
FlutterDesktopCreateWindow(int initial_width,
int initial_height,
const char* title,
const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t argument_count);
// Shuts down the engine instance associated with |controller|, and cleans up
// associated state.
//
// |controller| is no longer valid after this call.
FLUTTER_EXPORT void FlutterDesktopDestroyWindow(
FlutterDesktopWindowControllerRef controller);
// Loops on Flutter window events until the window is closed.
//
// Once this function returns, |controller| is no longer valid, and must not be
// be used again, as it calls FlutterDesktopDestroyWindow internally.
//
// TODO: Replace this with a method that allows running the runloop
// incrementally.
FLUTTER_EXPORT void FlutterDesktopRunWindowLoop(
FlutterDesktopWindowControllerRef controller);
// Returns the window handle for the window associated with
// FlutterDesktopWindowControllerRef.
//
// Its lifetime is the same as the |controller|'s.
FLUTTER_EXPORT FlutterDesktopWindowRef
FlutterDesktopGetWindow(FlutterDesktopWindowControllerRef controller);
// Returns the plugin registrar handle for the plugin with the given name.
//
// The name must be unique across the application.
FLUTTER_EXPORT FlutterDesktopPluginRegistrarRef
FlutterDesktopGetPluginRegistrar(FlutterDesktopWindowControllerRef controller,
const char* plugin_name);
// Enables or disables hover tracking.
//
// If hover is enabled, mouse movement will send hover events to the Flutter
// engine, rather than only tracking the mouse while the button is pressed.
// Defaults to on.
FLUTTER_EXPORT void FlutterDesktopWindowSetHoverEnabled(
FlutterDesktopWindowRef flutter_window,
bool enabled);
// Sets the displayed title for |flutter_window|.
FLUTTER_EXPORT void FlutterDesktopWindowSetTitle(
FlutterDesktopWindowRef flutter_window,
const char* title);
// Sets the displayed icon for |flutter_window|.
//
// The pixel format is 32-bit RGBA. The provided image data only needs to be
// valid for the duration of the call to this method. Pass a nullptr to revert
// to the default icon.
FLUTTER_EXPORT void FlutterDesktopWindowSetIcon(
FlutterDesktopWindowRef flutter_window,
uint8_t* pixel_data,
int width,
int height);
// Gets the position and size of |flutter_window| in screen coordinates.
FLUTTER_EXPORT void FlutterDesktopWindowGetFrame(
FlutterDesktopWindowRef flutter_window,
int* x,
int* y,
int* width,
int* height);
// Sets the position and size of |flutter_window| in screen coordinates.
FLUTTER_EXPORT void FlutterDesktopWindowSetFrame(
FlutterDesktopWindowRef flutter_window,
int x,
int y,
int width,
int height);
// Returns the scale factor--the number of pixels per screen coordinate--for
// |flutter_window|.
FLUTTER_EXPORT double FlutterDesktopWindowGetScaleFactor(
FlutterDesktopWindowRef flutter_window);
// Runs an instance of a headless Flutter engine.
//
// The |assets_path| is the path to the flutter_assets folder for the Flutter
// application to be run. |icu_data_path| is the path to the icudtl.dat file
// for the version of Flutter you are using.
//
// The |arguments| are passed to the Flutter engine. See:
// https://github.com/flutter/engine/blob/master/shell/common/switches.h for
// for details. Not all arguments will apply to desktop.
//
// Returns a null pointer in the event of an error.
FLUTTER_EXPORT FlutterDesktopEngineRef
FlutterDesktopRunEngine(const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t argument_count);
// Shuts down the given engine instance. Returns true if the shutdown was
// successful. |engine_ref| is no longer valid after this call.
FLUTTER_EXPORT bool FlutterDesktopShutDownEngine(
FlutterDesktopEngineRef engine_ref);
// TODO: remove once embedder project has switched to native windows
// imlpementation
FLUTTER_EXPORT FlutterDesktopWindowRef
FlutterDesktopRegistrarGetWindow(FlutterDesktopPluginRegistrarRef registrar);
#if defined(__cplusplus)
} // extern "C"
#endif
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_PUBLIC_FLUTTER_WINDOWS_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/windows/text_input_plugin.h"
#include <windows.h>
#include <cstdint>
#include <iostream>
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h"
static constexpr char kSetEditingStateMethod[] = "TextInput.setEditingState";
static constexpr char kClearClientMethod[] = "TextInput.clearClient";
static constexpr char kSetClientMethod[] = "TextInput.setClient";
static constexpr char kShowMethod[] = "TextInput.show";
static constexpr char kHideMethod[] = "TextInput.hide";
static constexpr char kMultilineInputType[] = "TextInputType.multiline";
static constexpr char kUpdateEditingStateMethod[] =
"TextInputClient.updateEditingState";
static constexpr char kPerformActionMethod[] = "TextInputClient.performAction";
static constexpr char kSelectionBaseKey[] = "selectionBase";
static constexpr char kSelectionExtentKey[] = "selectionExtent";
static constexpr char kTextKey[] = "text";
static constexpr char kChannelName[] = "flutter/textinput";
static constexpr char kBadArgumentError[] = "Bad Arguments";
static constexpr char kInternalConsistencyError[] =
"Internal Consistency Error";
static constexpr uint32_t kInputModelLimit = 256;
namespace flutter {
void TextInputPlugin::CharHook(Win32FlutterWindow* window,
unsigned int code_point) {
if (active_model_ == nullptr) {
return;
}
// TODO bug 30661
active_model_->AddCharacter(static_cast<char>(code_point));
SendStateUpdate(*active_model_);
}
void TextInputPlugin::KeyboardHook(Win32FlutterWindow* window,
int key,
int scancode,
int action,
int mods) {
if (active_model_ == nullptr) {
return;
}
if (action == WM_KEYDOWN) {
switch (key) {
case VK_LEFT:
if (active_model_->MoveCursorBack()) {
SendStateUpdate(*active_model_);
}
break;
case VK_RIGHT:
if (active_model_->MoveCursorForward()) {
SendStateUpdate(*active_model_);
}
break;
case VK_END:
active_model_->MoveCursorToEnd();
SendStateUpdate(*active_model_);
break;
case VK_HOME:
active_model_->MoveCursorToBeginning();
SendStateUpdate(*active_model_);
break;
case VK_BACK:
if (active_model_->Backspace()) {
SendStateUpdate(*active_model_);
}
break;
case VK_DELETE:
if (active_model_->Delete()) {
SendStateUpdate(*active_model_);
}
break;
case VK_RETURN:
EnterPressed(active_model_);
break;
default:
break;
}
}
}
TextInputPlugin::TextInputPlugin(flutter::BinaryMessenger* messenger)
: channel_(std::make_unique<flutter::MethodChannel<rapidjson::Document>>(
messenger,
kChannelName,
&flutter::JsonMethodCodec::GetInstance())),
active_model_(nullptr) {
channel_->SetMethodCallHandler(
[this](
const flutter::MethodCall<rapidjson::Document>& call,
std::unique_ptr<flutter::MethodResult<rapidjson::Document>> result) {
HandleMethodCall(call, std::move(result));
});
}
TextInputPlugin::~TextInputPlugin() = default;
void TextInputPlugin::HandleMethodCall(
const flutter::MethodCall<rapidjson::Document>& method_call,
std::unique_ptr<flutter::MethodResult<rapidjson::Document>> result) {
const std::string& method = method_call.method_name();
if (method.compare(kShowMethod) == 0 || method.compare(kHideMethod) == 0) {
// These methods are no-ops.
} else if (method.compare(kClearClientMethod) == 0) {
active_model_ = nullptr;
} else {
// Every following method requires args.
if (!method_call.arguments() || method_call.arguments()->IsNull()) {
result->Error(kBadArgumentError, "Method invoked without args");
return;
}
const rapidjson::Document& args = *method_call.arguments();
if (method.compare(kSetClientMethod) == 0) {
const rapidjson::Value& client_id_json = args[0];
const rapidjson::Value& client_config = args[1];
if (client_id_json.IsNull()) {
result->Error(kBadArgumentError, "Could not set client, ID is null.");
return;
}
if (client_config.IsNull()) {
result->Error(kBadArgumentError,
"Could not set client, missing arguments.");
return;
}
int client_id = client_id_json.GetInt();
if (input_models_.find(client_id) == input_models_.end()) {
// Skips out on adding a new input model once over the limit.
if (input_models_.size() > kInputModelLimit) {
result->Error(
kInternalConsistencyError,
"Input models over limit. Aborting creation of new text model.");
return;
}
input_models_.insert(std::make_pair(
client_id,
std::make_unique<TextInputModel>(client_id, client_config)));
}
active_model_ = input_models_[client_id].get();
} else if (method.compare(kSetEditingStateMethod) == 0) {
if (active_model_ == nullptr) {
result->Error(
kInternalConsistencyError,
"Set editing state has been invoked, but no client is set.");
return;
}
auto text = args.FindMember(kTextKey);
if (text == args.MemberEnd() || text->value.IsNull()) {
result->Error(kBadArgumentError,
"Set editing state has been invoked, but without text.");
return;
}
auto selection_base = args.FindMember(kSelectionBaseKey);
auto selection_extent = args.FindMember(kSelectionExtentKey);
if (selection_base == args.MemberEnd() ||
selection_base->value.IsNull() ||
selection_extent == args.MemberEnd() ||
selection_extent->value.IsNull()) {
result->Error(kInternalConsistencyError,
"Selection base/extent values invalid.");
return;
}
active_model_->SetEditingState(selection_base->value.GetInt(),
selection_extent->value.GetInt(),
text->value.GetString());
} else {
// Unhandled method.
result->NotImplemented();
return;
}
}
// All error conditions return early, so if nothing has gone wrong indicate
// success.
result->Success();
}
void TextInputPlugin::SendStateUpdate(const TextInputModel& model) {
channel_->InvokeMethod(kUpdateEditingStateMethod, model.GetState());
}
void TextInputPlugin::EnterPressed(TextInputModel* model) {
if (model->input_type() == kMultilineInputType) {
model->AddCharacter('\n');
SendStateUpdate(*model);
}
auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
auto& allocator = args->GetAllocator();
args->PushBack(model->client_id(), allocator);
args->PushBack(rapidjson::Value(model->input_action(), allocator).Move(),
allocator);
channel_->InvokeMethod(kPerformActionMethod, std::move(args));
}
} // namespace flutter
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_TEXT_INPUT_PLUGIN_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_TEXT_INPUT_PLUGIN_H_
#include <map>
#include <memory>
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h"
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h"
#include "flutter/shell/platform/common/cpp/text_input_model.h"
#include "flutter/shell/platform/windows/keyboard_hook_handler.h"
#include "flutter/shell/platform/windows/public/flutter_windows.h"
namespace flutter {
class Win32FlutterWindow;
// Implements a text input plugin.
//
// Specifically handles window events within windows.
class TextInputPlugin : public KeyboardHookHandler {
public:
explicit TextInputPlugin(flutter::BinaryMessenger* messenger);
virtual ~TextInputPlugin();
// |KeyboardHookHandler|
void KeyboardHook(Win32FlutterWindow* window,
int key,
int scancode,
int action,
int mods) override;
// |KeyboardHookHandler|
void CharHook(Win32FlutterWindow* window, unsigned int code_point) override;
private:
// Sends the current state of the given model to the Flutter engine.
void SendStateUpdate(const TextInputModel& model);
// Sends an action triggered by the Enter key to the Flutter engine.
void EnterPressed(TextInputModel* model);
// Called when a method is called on |channel_|;
void HandleMethodCall(
const flutter::MethodCall<rapidjson::Document>& method_call,
std::unique_ptr<flutter::MethodResult<rapidjson::Document>> result);
// The MethodChannel used for communication with the Flutter engine.
std::unique_ptr<flutter::MethodChannel<rapidjson::Document>> channel_;
// Mapping of client IDs to text input models.
std::map<int, std::unique_ptr<TextInputModel>> input_models_;
// The active model. nullptr if not set.
TextInputModel* active_model_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_TEXT_INPUT_PLUGIN_H_
#include "flutter/shell/platform/windows/win32_dpi_helper.h"
namespace flutter {
namespace {
template <typename T>
bool AssignProcAddress(HMODULE comBaseModule, const char* name, T*& outProc) {
outProc = reinterpret_cast<T*>(GetProcAddress(comBaseModule, name));
return *outProc != nullptr;
}
} // namespace
Win32DpiHelper::Win32DpiHelper() {
// TODO ensure that this helper works correctly on downlevel builds.
user32_module_ = LoadLibraryA("User32.dll");
if (user32_module_ == nullptr) {
return;
}
if (!AssignProcAddress(user32_module_, "EnableNonClientDpiScaling",
enable_non_client_dpi_scaling_)) {
return;
}
if (!AssignProcAddress(user32_module_, "GetDpiForWindow",
get_dpi_for_window_)) {
return;
}
if (!AssignProcAddress(user32_module_, "SetProcessDpiAwarenessContext",
set_process_dpi_awareness_context_)) {
return;
}
permonitorv2_supported_ = true;
}
Win32DpiHelper::~Win32DpiHelper() {
if (user32_module_ != nullptr) {
FreeLibrary(user32_module_);
}
}
bool Win32DpiHelper::IsPerMonitorV2Available() {
return permonitorv2_supported_;
}
BOOL Win32DpiHelper::EnableNonClientDpiScaling(HWND hwnd) {
if (!permonitorv2_supported_) {
return false;
}
return enable_non_client_dpi_scaling_(hwnd);
}
UINT Win32DpiHelper::GetDpiForWindow(HWND hwnd) {
if (!permonitorv2_supported_) {
return false;
}
return get_dpi_for_window_(hwnd);
}
BOOL Win32DpiHelper::SetProcessDpiAwarenessContext(
DPI_AWARENESS_CONTEXT context) {
if (!permonitorv2_supported_) {
return false;
}
return set_process_dpi_awareness_context_(context);
}
} // namespace flutter
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_DPI_HELPER_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_DPI_HELPER_H_
#include <ShellScalingApi.h>
#include <VersionHelpers.h>
namespace flutter {
// A helper class for abstracting various Windows DPI related functions across
// Windows OS versions.
class Win32DpiHelper {
public:
Win32DpiHelper();
~Win32DpiHelper();
// Check if Windows Per Monitor V2 DPI scaling functionality is available on
// current system.
bool IsPerMonitorV2Available();
// Wrapper for OS functionality to turn on automatic window non-client scaling
BOOL EnableNonClientDpiScaling(HWND);
// Wrapper for OS functionality to return the DPI for |HWND|
UINT GetDpiForWindow(HWND);
// Sets the current process to a specified DPI awareness context.
BOOL SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT);
private:
using EnableNonClientDpiScaling_ = BOOL __stdcall(HWND);
using GetDpiForWindow_ = UINT __stdcall(HWND);
using SetProcessDpiAwarenessContext_ = BOOL __stdcall(DPI_AWARENESS_CONTEXT);
EnableNonClientDpiScaling_* enable_non_client_dpi_scaling_ = nullptr;
GetDpiForWindow_* get_dpi_for_window_ = nullptr;
SetProcessDpiAwarenessContext_* set_process_dpi_awareness_context_ = nullptr;
HMODULE user32_module_ = nullptr;
bool permonitorv2_supported_ = false;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_DPI_HELPER_H_
#include "flutter/shell/platform/windows/win32_flutter_window.h"
#include <chrono>
namespace flutter {
// the Windows DPI system is based on this
// constant for machines running at 100% scaling.
constexpr int base_dpi = 96;
Win32FlutterWindow::Win32FlutterWindow() {
surface_manager = std::make_unique<AngleSurfaceManager>();
}
Win32FlutterWindow::Win32FlutterWindow(const char* title,
const int x,
const int y,
const int width,
const int height) noexcept
: Win32FlutterWindow() {
Win32Window::Initialize(title, x, y, width, height);
}
Win32FlutterWindow::~Win32FlutterWindow() {
DestroyRenderSurface();
}
FlutterDesktopWindowControllerRef Win32FlutterWindow::CreateWin32FlutterWindow(
const char* title,
const int x,
const int y,
const int width,
const int height) {
auto state = std::make_unique<FlutterDesktopWindowControllerState>();
state->window = std::make_unique<flutter::Win32FlutterWindow>(title, 10, 10,
width, height);
// a window wrapper for the state block, distinct from the
// window_wrapper handed to plugin_registrar.
state->window_wrapper = std::make_unique<FlutterDesktopWindow>();
state->window_wrapper->window = state->window.get();
return state.release();
}
void Win32FlutterWindow::SetState(FLUTTER_API_SYMBOL(FlutterEngine) eng) {
engine_ = eng;
auto messenger = std::make_unique<FlutterDesktopMessenger>();
message_dispatcher_ =
std::make_unique<flutter::IncomingMessageDispatcher>(messenger.get());
messenger->engine = engine_;
messenger->dispatcher = message_dispatcher_.get();
window_wrapper_ = std::make_unique<FlutterDesktopWindow>();
window_wrapper_->window = this;
plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
plugin_registrar_->messenger = std::move(messenger);
plugin_registrar_->window = window_wrapper_.get();
internal_plugin_registrar_ =
std::make_unique<flutter::PluginRegistrar>(plugin_registrar_.get());
// Set up the keyboard handlers.
auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
keyboard_hook_handlers_.push_back(
std::make_unique<flutter::KeyEventHandler>(internal_plugin_messenger));
keyboard_hook_handlers_.push_back(
std::make_unique<flutter::TextInputPlugin>(internal_plugin_messenger));
platform_handler_ = std::make_unique<flutter::PlatformHandler>(
internal_plugin_messenger, this);
auto state = std::make_unique<FlutterDesktopWindowControllerState>();
state->engine = engine_;
process_events_ = true;
}
FlutterDesktopPluginRegistrarRef Win32FlutterWindow::GetRegistrar() {
return plugin_registrar_.get();
}
// Converts a FlutterPlatformMessage to an equivalent FlutterDesktopMessage.
static FlutterDesktopMessage ConvertToDesktopMessage(
const FlutterPlatformMessage& engine_message) {
FlutterDesktopMessage message = {};
message.struct_size = sizeof(message);
message.channel = engine_message.channel;
message.message = engine_message.message;
message.message_size = engine_message.message_size;
message.response_handle = engine_message.response_handle;
return message;
}
// The Flutter Engine calls out to this function when new platform messages
// are available.
void Win32FlutterWindow::HandlePlatformMessage(
const FlutterPlatformMessage* engine_message) {
if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) {
std::cerr << "Invalid message size received. Expected: "
<< sizeof(FlutterPlatformMessage) << " but received "
<< engine_message->struct_size << std::endl;
return;
}
auto message = ConvertToDesktopMessage(*engine_message);
message_dispatcher_->HandleMessage(
message, [this] { this->process_events_ = false; },
[this] { this->process_events_ = true; });
}
void Win32FlutterWindow::OnDpiScale(unsigned int dpi){};
// When DesktopWindow notifies that a WM_Size message has come in
// lets FlutterEngine know about the new size.
void Win32FlutterWindow::OnResize(unsigned int width, unsigned int height) {
SendWindowMetrics();
}
void Win32FlutterWindow::OnPointerMove(double x, double y) {
if (process_events_) {
SendPointerMove(x, y);
}
}
void Win32FlutterWindow::OnPointerDown(double x, double y) {
if (process_events_) {
SendPointerDown(x, y);
}
}
void Win32FlutterWindow::OnPointerUp(double x, double y) {
if (process_events_) {
SendPointerUp(x, y);
}
}
void Win32FlutterWindow::OnChar(unsigned int code_point) {
if (process_events_) {
SendChar(code_point);
}
}
void Win32FlutterWindow::OnKey(int key, int scancode, int action, int mods) {
if (process_events_) {
SendKey(key, scancode, action, 0);
}
}
void Win32FlutterWindow::OnScroll(double delta_x, double delta_y) {
if (process_events_) {
SendScroll(delta_x, delta_y);
}
}
void Win32FlutterWindow::OnClose() {
messageloop_running_ = false;
}
void Win32FlutterWindow::FlutterMessageLoop() {
MSG message;
messageloop_running_ = true;
// TODO: need either non-blocking meesage loop or custom dispatch
// implementation per https://github.com/flutter/flutter/issues/36420
while (GetMessage(&message, nullptr, 0, 0) && messageloop_running_) {
TranslateMessage(&message);
DispatchMessage(&message);
__FlutterEngineFlushPendingTasksNow();
}
}
// Sends new size information to FlutterEngine.
void Win32FlutterWindow::SendWindowMetrics() {
if (engine_ == nullptr) {
return;
}
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
event.width = GetCurrentWidth();
event.height = GetCurrentHeight();
event.pixel_ratio = static_cast<double>(GetCurrentDPI()) / base_dpi;
auto result = FlutterEngineSendWindowMetricsEvent(engine_, &event);
}
// Updates |event_data| with the current location of the mouse cursor.
void Win32FlutterWindow::SetEventLocationFromCursorPosition(
FlutterPointerEvent* event_data) {
POINT point;
GetCursorPos(&point);
ScreenToClient(GetWindowHandle(), &point);
event_data->x = point.x;
event_data->y = point.y;
}
// Set's |event_data|'s phase to either kMove or kHover depending on the current
// primary mouse button state.
void Win32FlutterWindow::SetEventPhaseFromCursorButtonState(
FlutterPointerEvent* event_data) {
event_data->phase = pointer_is_down_ ? FlutterPointerPhase::kMove
: FlutterPointerPhase::kHover;
}
void Win32FlutterWindow::SendPointerMove(double x, double y) {
FlutterPointerEvent event = {};
event.x = x;
event.y = y;
SetEventPhaseFromCursorButtonState(&event);
SendPointerEventWithData(event);
}
void Win32FlutterWindow::SendPointerDown(double x, double y) {
pointer_is_down_ = true;
FlutterPointerEvent event = {};
event.phase = FlutterPointerPhase::kDown;
event.x = x;
event.y = y;
SendPointerEventWithData(event);
}
void Win32FlutterWindow::SendPointerUp(double x, double y) {
pointer_is_down_ = false;
FlutterPointerEvent event = {};
event.phase = FlutterPointerPhase::kUp;
event.x = x;
event.y = y;
SendPointerEventWithData(event);
}
void Win32FlutterWindow::SendChar(unsigned int code_point) {
for (const auto& handler : keyboard_hook_handlers_) {
handler->CharHook(this, code_point);
}
}
void Win32FlutterWindow::SendKey(int key, int scancode, int action, int mods) {
for (const auto& handler : keyboard_hook_handlers_) {
handler->KeyboardHook(this, key, scancode, action, mods);
}
}
void Win32FlutterWindow::SendScroll(double delta_x, double delta_y) {
FlutterPointerEvent event = {};
SetEventLocationFromCursorPosition(&event);
SetEventPhaseFromCursorButtonState(&event);
event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
// TODO: See if this can be queried from the OS; this value is chosen
// arbitrarily to get something that feels reasonable.
const int kScrollOffsetMultiplier = 20;
event.scroll_delta_x = delta_x * kScrollOffsetMultiplier;
event.scroll_delta_y = delta_y * kScrollOffsetMultiplier;
SendPointerEventWithData(event);
}
void Win32FlutterWindow::SendPointerEventWithData(
const FlutterPointerEvent& event_data) {
// If sending anything other than an add, and the pointer isn't already added,
// synthesize an add to satisfy Flutter's expectations about events.
if (!pointer_currently_added_ &&
event_data.phase != FlutterPointerPhase::kAdd) {
FlutterPointerEvent event = {};
event.phase = FlutterPointerPhase::kAdd;
event.x = event_data.x;
event.y = event_data.y;
SendPointerEventWithData(event);
}
// Don't double-add (e.g., if events are delivered out of order, so an add has
// already been synthesized).
if (pointer_currently_added_ &&
event_data.phase == FlutterPointerPhase::kAdd) {
return;
}
FlutterPointerEvent event = event_data;
// Set metadata that's always the same regardless of the event.
event.struct_size = sizeof(event);
event.timestamp =
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
// Windows passes all input in either physical pixels (Per-monitor, System
// DPI) or pre-scaled to match bitmap scaling of output where process is
// running in DPI unaware more. In either case, no need to manually scale
// input here. For more information see DPIHelper.
event.scroll_delta_x;
event.scroll_delta_y;
FlutterEngineSendPointerEvent(engine_, &event, 1);
if (event_data.phase == FlutterPointerPhase::kAdd) {
pointer_currently_added_ = true;
} else if (event_data.phase == FlutterPointerPhase::kRemove) {
pointer_currently_added_ = false;
}
}
bool Win32FlutterWindow::MakeCurrent() {
return surface_manager->MakeCurrent(render_surface);
}
bool Win32FlutterWindow::ClearContext() {
return surface_manager->MakeCurrent(nullptr);
}
bool Win32FlutterWindow::SwapBuffers() {
return surface_manager->SwapBuffers(render_surface);
}
void Win32FlutterWindow::CreateRenderSurface() {
if (surface_manager && render_surface == EGL_NO_SURFACE) {
render_surface = surface_manager->CreateSurface(GetWindowHandle());
}
}
void Win32FlutterWindow::DestroyRenderSurface() {
if (surface_manager) {
surface_manager->DestroySurface(render_surface);
}
render_surface = EGL_NO_SURFACE;
}
} // namespace flutter
\ No newline at end of file
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_H_
#include <windowsx.h>
#include <string>
#include <vector>
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h"
#include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/angle_surface_manager.h"
#include "flutter/shell/platform/windows/key_event_handler.h"
#include "flutter/shell/platform/windows/keyboard_hook_handler.h"
#include "flutter/shell/platform/windows/platform_handler.h"
#include "flutter/shell/platform/windows/public/flutter_windows.h"
#include "flutter/shell/platform/windows/text_input_plugin.h"
#include "flutter/shell/platform/windows/win32_window.h"
#include "flutter/shell/platform/windows/window_state.h"
namespace flutter {
// A win32 flutter window. In the future, there will likely be a
// CoreWindow-based FlutterWindow as well. At the point may make sense to
// dependency inject the native window rather than inherit.
class Win32FlutterWindow : public Win32Window {
public:
Win32FlutterWindow();
Win32FlutterWindow(const char* title,
const int x,
const int y,
const int width,
const int height) noexcept;
~Win32FlutterWindow();
static FlutterDesktopWindowControllerRef CreateWin32FlutterWindow(
const char* title,
const int x,
const int y,
const int width,
const int height);
// Run a Windows message pump that also pumps plugin messages.
void FlutterMessageLoop();
// |Win32Window|
void OnDpiScale(unsigned int dpi) override;
// |Win32Window|
void OnResize(unsigned int width, unsigned int height) override;
// |Win32Window|
void OnPointerMove(double x, double y) override;
// |Win32Window|
void OnPointerDown(double x, double y) override;
// |Win32Window|
void OnPointerUp(double x, double y) override;
// |Win32Window|
void OnChar(unsigned int code_point) override;
// |Win32Window|
void OnKey(int key, int scancode, int action, int mods) override;
// |Win32Window|
void OnScroll(double delta_x, double delta_y) override;
// |Win32Window|
void OnClose();
// Configures the window instance with an instance of a running Flutter engine
// returning a configured FlutterDesktopWindowControllerRef.
void SetState(FLUTTER_API_SYMBOL(FlutterEngine) state);
// Returns the currently configured Plugin Registrar.
FlutterDesktopPluginRegistrarRef GetRegistrar();
// Callback passed to Flutter engine for notifying window of platform
// messages.
void HandlePlatformMessage(const FlutterPlatformMessage*);
// Create a surface for Flutter engine to render into.
void CreateRenderSurface();
// Destroy current rendering surface if one has been allocated.
void DestroyRenderSurface();
// Callbacks for clearing context, settings context and swapping buffers.
bool ClearContext();
bool MakeCurrent();
bool SwapBuffers();
// Sends a window metrics update to the Flutter engine using current window
// dimensions in physical
void SendWindowMetrics();
private:
// Reports a mouse movement to Flutter engine.
void SendPointerMove(double x, double y);
// Reports mouse press to Flutter engine.
void SendPointerDown(double x, double y);
// Reports mouse release to Flutter engine.
void SendPointerUp(double x, double y);
// Reports a keyboard character to Flutter engine.
void SendChar(unsigned int code_point);
// Reports a raw keyboard message to Flutter engine.
void SendKey(int key, int scancode, int action, int mods);
// Reports scroll wheel events to Flutter engine.
void SendScroll(double delta_x, double delta_y);
// Updates |event_data| with the current location of the mouse cursor.
void SetEventLocationFromCursorPosition(FlutterPointerEvent* event_data);
// Set's |event_data|'s phase to either kMove or kHover depending on the
// current
// primary mouse button state.
void SetEventPhaseFromCursorButtonState(FlutterPointerEvent* event_data);
// Sends a pointer event to the Flutter engine based on givern data. Since
// all input messages are passed in physical pixel values, no translation is
// needed before passing on to engine.
void SendPointerEventWithData(const FlutterPointerEvent& event_data);
std::unique_ptr<AngleSurfaceManager> surface_manager = nullptr;
EGLSurface render_surface = EGL_NO_SURFACE;
// state of the mouse button
bool pointer_is_down_ = false;
// The handle to the Flutter engine instance.
FLUTTER_API_SYMBOL(FlutterEngine) engine_ = nullptr;
// Whether or not to track mouse movements to send kHover events.
bool hover_tracking_is_enabled_ = false;
// Whether or not the pointer has been added (or if tracking is enabled, has
// been added since it was last removed).
bool pointer_currently_added_ = false;
// The window handle given to API clients.
std::unique_ptr<FlutterDesktopWindow> window_wrapper_;
// The plugin registrar handle given to API clients.
std::unique_ptr<FlutterDesktopPluginRegistrar> plugin_registrar_;
// Message dispatch manager for messages from the Flutter engine.
std::unique_ptr<flutter::IncomingMessageDispatcher> message_dispatcher_;
// The plugin registrar managing internal plugins.
std::unique_ptr<flutter::PluginRegistrar> internal_plugin_registrar_;
// Handlers for keyboard events from Windows.
std::vector<std::unique_ptr<flutter::KeyboardHookHandler>>
keyboard_hook_handlers_;
// Handler for the flutter/platform channel.
std::unique_ptr<flutter::PlatformHandler> platform_handler_;
// should we forword input messages or not
bool process_events_ = false;
// flag indicating if the message loop should be running
bool messageloop_running_ = false;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_H_
\ No newline at end of file
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/windows/win32_window.h"
namespace flutter {
Win32Window::Win32Window() {
// Assume Windows 10 1703 or greater for DPI handling. When running on a
// older release of Windows where this context doesn't exist, DPI calls will
// fail and Flutter rendering will be impacted until this is fixed.
// To handle downlevel correctly, dpi_helper must use the most recent DPI
// context available should be used: Windows 1703: Per-Monitor V2, 8.1:
// Per-Monitor V1, Windows 7: System See
// https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
// for more information.
// TODO the calling applicaiton should participate in setting the DPI.
// Currently dpi_helper is asserting per-monitor V2. There are two problems
// with this: 1) it is advised that the awareness mode is set using manifest,
// not programatically. 2) The calling executable should be responsible for
// setting an appropriate scaling mode, not a library. This will be
// particularly important once there is a means of hosting Flutter content in
// an existing app.
BOOL result = dpi_helper_->SetProcessDpiAwarenessContext(
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
if (result != TRUE) {
OutputDebugString(L"Failed to set PMV2");
}
}
Win32Window::~Win32Window() {
Destroy();
}
void Win32Window::Initialize(const char* title,
const unsigned int x,
const unsigned int y,
const unsigned int width,
const unsigned int height) {
Destroy();
std::wstring converted_title = NarrowToWide(title);
WNDCLASS window_class = ResgisterWindowClass(converted_title);
CreateWindow(window_class.lpszClassName, converted_title.c_str(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, width, height, nullptr,
nullptr, window_class.hInstance, this);
}
std::wstring Win32Window::NarrowToWide(const char* source) {
size_t length = strlen(source);
size_t outlen = 0;
std::wstring wideTitle(length, L'#');
mbstowcs_s(&outlen, &wideTitle[0], length + 1, source, length);
return wideTitle;
}
WNDCLASS Win32Window::ResgisterWindowClass(std::wstring& title) {
window_class_name_ = title;
WNDCLASS window_class{};
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.lpszClassName = title.c_str();
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon = nullptr;
window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = WndProc;
RegisterClass(&window_class);
return window_class;
}
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
if (message == WM_NCCREATE) {
auto cs = reinterpret_cast<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
auto that = static_cast<Win32Window*>(cs->lpCreateParams);
// Since the application is running in Per-monitor V2 mode, turn on
// automatic titlebar scaling
BOOL result = that->dpi_helper_->EnableNonClientDpiScaling(window);
if (result != TRUE) {
OutputDebugString(L"Failed to enable non-client area autoscaling");
}
that->current_dpi_ = that->dpi_helper_->GetDpiForWindow(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 {
int xPos = 0, yPos = 0;
UINT width = 0, height = 0;
auto window =
reinterpret_cast<Win32Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (window != nullptr) {
switch (message) {
case WM_DPICHANGED:
return HandleDpiChange(window_handle_, wparam, lparam);
break;
case WM_DESTROY:
window->OnClose();
return 0;
break;
case WM_SIZE:
width = LOWORD(lparam);
height = HIWORD(lparam);
current_width_ = width;
current_height_ = height;
window->HandleResize(width, height);
break;
case WM_MOUSEMOVE:
xPos = GET_X_LPARAM(lparam);
yPos = GET_Y_LPARAM(lparam);
window->OnPointerMove(static_cast<double>(xPos),
static_cast<double>(yPos));
break;
case WM_LBUTTONDOWN:
xPos = GET_X_LPARAM(lparam);
yPos = GET_Y_LPARAM(lparam);
window->OnPointerDown(static_cast<double>(xPos),
static_cast<double>(yPos));
break;
case WM_LBUTTONUP:
xPos = GET_X_LPARAM(lparam);
yPos = GET_Y_LPARAM(lparam);
window->OnPointerUp(static_cast<double>(xPos),
static_cast<double>(yPos));
break;
case WM_MOUSEWHEEL:
window->OnScroll(
0.0, -(static_cast<short>(HIWORD(wparam)) / (double)WHEEL_DELTA));
break;
case WM_CHAR:
case WM_SYSCHAR:
case WM_UNICHAR:
if (wparam != VK_BACK) {
window->OnChar(static_cast<unsigned int>(wparam));
}
break;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
unsigned char scancode = ((unsigned char*)&lparam)[2];
unsigned int virtualKey = MapVirtualKey(scancode, MAPVK_VSC_TO_VK);
const int key = virtualKey;
const int action = message == WM_KEYDOWN ? WM_KEYDOWN : WM_KEYUP;
window->OnKey(key, scancode, action, 0);
break;
}
return DefWindowProc(hwnd, message, wparam, lparam);
}
return DefWindowProc(window_handle_, message, wparam, lparam);
}
UINT Win32Window::GetCurrentDPI() {
return current_dpi_;
}
UINT Win32Window::GetCurrentWidth() {
return current_width_;
}
UINT Win32Window::GetCurrentHeight() {
return current_height_;
}
HWND Win32Window::GetWindowHandle() {
return window_handle_;
}
void Win32Window::Destroy() {
if (window_handle_) {
DestroyWindow(window_handle_);
window_handle_ = nullptr;
}
UnregisterClass(window_class_name_.c_str(), nullptr);
}
// DPI Change handler. on WM_DPICHANGE resize the window
LRESULT
Win32Window::HandleDpiChange(HWND hwnd, WPARAM wparam, LPARAM lparam) {
if (hwnd != nullptr) {
auto window =
reinterpret_cast<Win32Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
UINT uDpi = HIWORD(wparam);
current_dpi_ = uDpi;
window->OnDpiScale(uDpi);
// Resize the window
auto lprcNewScale = reinterpret_cast<RECT*>(lparam);
LONG newWidth = lprcNewScale->right - lprcNewScale->left;
LONG newHeight = lprcNewScale->bottom - lprcNewScale->top;
SetWindowPos(hwnd, nullptr, lprcNewScale->left, lprcNewScale->top, newWidth,
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
}
return 0;
}
void Win32Window::HandleResize(UINT width, UINT height) {
current_width_ = width;
current_height_ = height;
OnResize(width, height);
}
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
return reinterpret_cast<Win32Window*>(
GetWindowLongPtr(window, GWLP_USERDATA));
}
} // namespace flutter
\ No newline at end of file
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WIN32_WINDOW_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WIN32_WINDOW_H_
#include <Windows.h>
#include <Windowsx.h>
#include <memory>
#include <string>
#include "flutter/shell/platform/windows/win32_dpi_helper.h"
namespace flutter {
// 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:
Win32Window();
~Win32Window();
// Initializes and shows window with |title| and position and size using |x|,
// |y|, |width| and |height|
void Initialize(const char* title,
const unsigned int x,
const unsigned int y,
const unsigned int width,
const unsigned int height);
// Release OS resources asociated with window.
virtual void Destroy();
protected:
// Converts a c string to a wide unicode string.
std::wstring NarrowToWide(const char* source);
// Registers a window class with default style attributes, cursor and
// icon.
WNDCLASS ResgisterWindowClass(std::wstring& title);
// 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;
// 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.
LRESULT
MessageHandler(HWND window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// When WM_DPICHANGE resizes the window to the new suggested
// size and notifies inheriting class.
LRESULT
HandleDpiChange(HWND hWnd, WPARAM wParam, LPARAM lParam);
// Called when the DPI changes either when a
// user drags the window between monitors of differing DPI or when the user
// manually changes the scale factor.
virtual void OnDpiScale(UINT dpi) = 0;
// Called when a resize occurs.
virtual void OnResize(UINT width, UINT height) = 0;
// Called when the pointer moves within the
// window bounds.
virtual void OnPointerMove(double x, double y) = 0;
// Called when the left mouse button goes down
virtual void OnPointerDown(double x, double y) = 0;
// Called when the left mouse button goes from
// down to up
virtual void OnPointerUp(double x, double y) = 0;
// Called when character input occurs.
virtual void OnChar(unsigned int code_point) = 0;
// Called when raw keyboard input occurs.
virtual void OnKey(int key, int scancode, int action, int mods) = 0;
// Called when mouse scrollwheel input occurs.
virtual void OnScroll(double delta_x, double delta_y) = 0;
// Called when the user closes the Windows
virtual void OnClose() = 0;
UINT GetCurrentDPI();
UINT GetCurrentWidth();
UINT GetCurrentHeight();
HWND GetWindowHandle();
private:
// Stores new width and height and calls |OnResize| to notify inheritors
void HandleResize(UINT width, UINT height);
// Retrieves a class instance pointer for |window|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
int current_dpi_ = 0;
int current_width_ = 0;
int current_height_ = 0;
// Member variable to hold window handle.
HWND window_handle_ = nullptr;
// Member variable to hold the window title.
std::wstring window_class_name_;
// Member variable referencing an instance of dpi_helper used to abstract some
// aspects of win32 High DPI handling across different OS versions.
std::unique_ptr<Win32DpiHelper> dpi_helper_ =
std::make_unique<Win32DpiHelper>();
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WIN32_WINDOW_H_
\ No newline at end of file
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_STATE_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_STATE_H_
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h"
#include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/key_event_handler.h"
#include "flutter/shell/platform/windows/keyboard_hook_handler.h"
#include "flutter/shell/platform/windows/platform_handler.h"
#include "flutter/shell/platform/windows/text_input_plugin.h"
struct flutter::Win32FlutterWindow;
// Struct for storing state within an instance of the windows native (HWND or
// CoreWindow) Window.
struct FlutterDesktopWindowControllerState {
//// The win32 window that owns this state object.
std::unique_ptr<flutter::Win32FlutterWindow> window;
// The handle to the Flutter engine instance.
FLUTTER_API_SYMBOL(FlutterEngine) engine;
// The window handle given to API clients.
std::unique_ptr<FlutterDesktopWindow> window_wrapper;
};
// Opaque reference for the native windows itself. This is separate from the
// controller so that it can be provided to plugins without giving them access
// to all of the controller-based functionality.
struct FlutterDesktopWindow {
// The window that (indirectly) owns this state object.
flutter::Win32FlutterWindow* window;
};
// Struct for storing state of a Flutter engine instance.
struct FlutterDesktopEngineState {
// The handle to the Flutter engine instance.
FLUTTER_API_SYMBOL(FlutterEngine) engine;
};
// State associated with the plugin registrar.
struct FlutterDesktopPluginRegistrar {
// The plugin messenger handle given to API clients.
std::unique_ptr<FlutterDesktopMessenger> messenger;
// The handle for the window associated with this registrar.
FlutterDesktopWindow* window;
};
// State associated with the messenger used to communicate with the engine.
struct FlutterDesktopMessenger {
// The Flutter engine this messenger sends outgoing messages to.
FLUTTER_API_SYMBOL(FlutterEngine) engine;
// The message dispatcher for handling incoming messages.
flutter::IncomingMessageDispatcher* dispatcher;
};
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_STATE_H_
此差异已折叠。
......@@ -239,6 +239,11 @@ def to_gn_args(args):
# Configure Skia for Vulkan support.
gn_args['skia_use_vulkan'] = True
# The buildroot currently isn't set up to support Vulkan in the
# Windows ANGLE build, so disable it regardless of enable_vulkan's value.
if sys.platform.startswith(('cygwin', 'win')):
gn_args['angle_enable_vulkan'] = False
# We should not need a special case for x86, but this seems to introduce text relocations
# even with -fPIC everywhere.
# gn_args['enable_profiling'] = args.runtime_mode != 'release' and args.android_cpu != 'x86'
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册