暂时精简了仓库,后边再放吧

This commit is contained in:
FloatGaming
2026-07-15 01:42:01 +08:00
parent d9990d6fdb
commit 4ff12cd6a6
92 changed files with 0 additions and 9400 deletions
-45
View File
@@ -1,45 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
-30
View File
@@ -1,30 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "f6ff1529fd6d8af5f706051d9251ac9231c83407"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407
base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407
- platform: windows
create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407
base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
-16
View File
@@ -1,16 +0,0 @@
# fd_gamer
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
-32
View File
@@ -1,32 +0,0 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
- third_party/**
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
Binary file not shown.

Before

Width:  |  Height:  |  Size: 423 KiB

Binary file not shown.
File diff suppressed because it is too large Load Diff
-284
View File
@@ -1,284 +0,0 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.13.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
characters:
dependency: transitive
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.19.1"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.9"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.3"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.11.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.flutter-io.cn"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
lints:
dependency: transitive
description:
name: lints
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.1.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.17"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.17.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.9.1"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.8"
screen_retriever:
dependency: transitive
description:
name: screen_retriever
sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
screen_retriever_linux:
dependency: transitive
description:
name: screen_retriever_linux
sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
screen_retriever_macos:
dependency: transitive
description:
name: screen_retriever_macos
sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
screen_retriever_platform_interface:
dependency: transitive
description:
name: screen_retriever_platform_interface
sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
screen_retriever_windows:
dependency: transitive
description:
name: screen_retriever_windows
sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_span:
dependency: transitive
description:
name: source_span
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.2"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.4"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.2"
test_api:
dependency: transitive
description:
name: test_api
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.7"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "046d3928e16fa4dc46e8350415661755ab759d9fc97fc21b5ab295f71e4f0499"
url: "https://pub.flutter-io.cn"
source: hosted
version: "15.1.0"
webview_windows:
dependency: "direct main"
description:
path: "third_party/webview_windows"
relative: true
source: path
version: "0.4.0"
window_manager:
dependency: "direct main"
description:
name: window_manager
sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.1"
sdks:
dart: ">=3.10.4 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
-101
View File
@@ -1,101 +0,0 @@
name: floating_mini_games
description: "独立小游戏托管窗口。"
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: ^3.10.4
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
webview_windows: ^0.4.0
window_manager: ^0.5.1
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^6.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- assets/bansonic icon.png
fonts:
- family: Bansonic
fonts:
- asset: assets/font.otf
dependency_overrides:
webview_windows:
path: third_party/webview_windows
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/to/asset-from-package
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package
-29
View File
@@ -1,29 +0,0 @@
@echo off
setlocal
title 浮动小游戏 - 调试启动
set FLUTTER_BIN=E:\mcz_transform_flutter\flutter_windows_3.38.5-stable\flutter\bin
set PUB_HOSTED_URL=https://pub.flutter-io.cn
set FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
set PATH=%FLUTTER_BIN%;%PATH%
cd /d %~dp0
echo [fdGamer] Flutter SDK: %FLUTTER_BIN%
echo [fdGamer] Running flutter pub get...
call flutter.bat pub get
if errorlevel 1 goto :fail
echo [fdGamer] Starting Windows debug session...
call flutter.bat run -d windows -- --server-base=http://127.0.0.1:8080 %*
if errorlevel 1 goto :fail
goto :end
:fail
echo [fdGamer] Failed.
pause
exit /b 1
:end
endlocal
-14
View File
@@ -1,14 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:floating_mini_games/main.dart';
void main() {
testWidgets('floating mini games app boots', (tester) async {
await tester.pumpWidget(
const FloatingMiniGamesApp(
config: AppLaunchConfig(serverBase: 'http://127.0.0.1:8080'),
),
);
expect(find.text('小游戏大厅'), findsOneWidget);
});
}
-136
View File
@@ -1,136 +0,0 @@
## 0.4.0
* Enable MSVC coroutine support ([#278](https://github.com/jnschulze/flutter-webview-windows/pull/278))
* Enable scrolling with trackpad ([#274](https://github.com/jnschulze/flutter-webview-windows/pull/274))
## 0.3.0
* Add full-screen support ([#189](https://github.com/jnschulze/flutter-webview-windows/pull/189))
* Make `loadingState` a broadcast stream ([#193](https://github.com/jnschulze/flutter-webview-windows/pull/193))
* Add `getWebViewVersion()` method ([#197](https://github.com/jnschulze/flutter-webview-windows/pull/197))
* Fix string casting of paths that may contain Unicode characters ([#199](https://github.com/jnschulze/flutter-webview-windows/pull/199))
* Add high-DPI screen support ([#203](https://github.com/jnschulze/flutter-webview-windows/pull/203))
* Add `setZoomFactor` ([#214](https://github.com/jnschulze/flutter-webview-windows/pull/214))
* Fix example ([#215](https://github.com/jnschulze/flutter-webview-windows/pull/215))
* Fix Visual Studio 17.6 builds ([#252](https://github.com/jnschulze/flutter-webview-windows/pull/252))
## 0.2.2
* Remove `libfmt` dependency in favor of C++20 `std::format`
* Enable D3D texture bridge by default
* Make `executeScript` return the script's result
## 0.2.1
* Add `WebviewController.addScriptToExecuteOnDocumentCreated` and `WebviewController.removeScriptToExecuteOnDocumentCreated`
* Add `WebviewController.onLoadError` stream
* Change `WebviewController.webMessage` stream type from `Map<dynamic, dynamic>` to `dynamic`
* Add virtual hostname mapping support
* Add multi-touch support
## 0.2.0
* Fix Flutter 3.0 null safety warning in example
* Bump WebView2 SDK version to `1.0.1210.3`
* Add an option for limiting the FPS
* Change data directory base path from `RoamingAppData` to `LocalAppData`
## 0.1.9
* Fix Flutter 3.0 compatibility
## 0.1.8
* Prefix CMake build target names to prevent collisions with other plugins
## 0.1.7
* Add method for opening DevTools
* Update `TextureBridgeGpu`
* Update `libfmt` dependency
## 0.1.7-dev.2
* Ensure Flutter apps referencing `webview_windows` still work on Windows 8.
## 0.1.7-dev.1
* Remove windowsapp.lib dependency
## 0.1.6
* Improve WebView creation error handling
## 0.1.5
* Fix a potential crash during WebView creation
## 0.1.4
* Improve error handling for Webview environment creation
## 0.1.3
* Stability fixes
## 0.1.2
* Unregister method channel handlers upon WebView destruction
## 0.1.1
* Fix unicode string conversion in ExecuteScript and LoadStringContent
* Load CoreMessaging.dll on demand
## 0.1.0
* Fix a string conversion issue
* Add an option for controlling popup window behavior
* Update Microsoft.Web.WebView2 and Microsoft.Windows.ImplementationLibrary
## 0.0.9
* Fix resizing issues
* Add preliminary GpuSurfaceTexture support
## 0.0.8
* Don't rely on AVX2 support
* Add history controls
* Add suspend/resume support
* Add support for disabling cache, clearing cookies etc.
## 0.0.7
* Add support for handling permission requests
* Allow setting the background color
* Automatically download nuget
## 0.0.6
* Fix mousewheel event handling
* Make text selection work
* Add method for setting the user agent
* Add support for JavaScript injection
* Add support for JSON message passing between Dart and JS
* Fix WebView disposal
## 0.0.5
* Fix input field focus issue
## 0.0.4
* Minor cleanup
## 0.0.3
* Add support for additional cursor types
## 0.0.2
* Add support for loading string content
## 0.0.1
* Initial release
-29
View File
@@ -1,29 +0,0 @@
BSD 3-Clause License
Copyright (c) 2021 Niklas Schulze
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-38
View File
@@ -1,38 +0,0 @@
# webview_windows
[![CI](https://github.com/jnschulze/flutter-webview-windows/actions/workflows/ci.yml/badge.svg)](https://github.com/jnschulze/flutter-webview-windows/actions/workflows/ci.yml)
[![Pub](https://img.shields.io/pub/v/webview_windows.svg)](https://pub.dartlang.org/packages/webview_windows)
A [Flutter](https://flutter.dev/) WebView plugin for Windows built on [Microsoft Edge WebView2](https://docs.microsoft.com/en-us/microsoft-edge/webview2/).
### Target platform requirements
- [WebView2 Runtime](https://developer.microsoft.com/en-us/microsoft-edge/webview2/)
Before initializing the webview, call `getWebViewVersion()` to check whether the required **WebView2 Runtime** is installed or not on the current system. If `getWebViewVersion()` returns null, guide your user to install **WebView2 Runtime** from this [page](https://developer.microsoft.com/en-us/microsoft-edge/webview2/).
- Windows 10 1809+
### Development platform requirements
- Visual Studio 2019 or higher
- Windows 11 SDK (10.0.22000.194 or higher)
- (recommended) nuget.exe in your $PATH *(The makefile attempts to download nuget if it's not installed, however, this fallback might not work in China)*
## Demo
![image](https://user-images.githubusercontent.com/720469/116823636-d8b9fe00-ab85-11eb-9f91-b7bc819615ed.png)
https://user-images.githubusercontent.com/720469/116716747-66f08180-a9d8-11eb-86ca-63ad5c24f07b.mp4
## Limitations
This plugin provides seamless composition of web-based contents with other Flutter widgets by rendering off-screen.
Unfortunately, [Microsoft Edge WebView2](https://docs.microsoft.com/en-us/microsoft-edge/webview2/) doesn't currently have an explicit API for offscreen rendering.
In order to still be able to obtain a pixel buffer upon rendering a new frame, this plugin currently relies on the `Windows.Graphics.Capture` API provided by Windows 10.
The downside is that older Windows versions aren't currently supported.
Older Windows versions might still be targeted by using `BitBlt` for the time being.
See:
- https://github.com/MicrosoftEdge/WebView2Feedback/issues/20
- https://github.com/MicrosoftEdge/WebView2Feedback/issues/526
- https://github.com/MicrosoftEdge/WebView2Feedback/issues/547
@@ -1,61 +0,0 @@
# Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
#
# Google internally enforced rules. See README.md for more information,
# including a list of lints that are intentionally _not_ enforced.
linter:
rules:
- always_declare_return_types
- always_require_non_null_named_parameters
- annotate_overrides
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_relative_lib_imports
- avoid_return_types_on_setters
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_types_as_parameter_names
- await_only_futures
- camel_case_extensions
- curly_braces_in_flow_control_structures
- empty_catches
- empty_constructor_bodies
- library_names
- library_prefixes
- no_duplicate_case_values
- null_closures
- omit_local_variable_types
- prefer_adjacent_string_concatenation
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_contains
- prefer_equal_for_default_values
- prefer_final_fields
- prefer_for_elements_to_map_fromIterable
- prefer_generic_function_type_aliases
- prefer_if_null_operators
- prefer_inlined_adds
- prefer_is_empty
- prefer_is_not_empty
- prefer_iterable_whereType
- prefer_single_quotes
- prefer_spread_collections
- recursive_getters
- slash_for_doc_comments
- sort_child_properties_last
- type_init_formals
- unawaited_futures
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_new
- unnecessary_null_in_if_null_operators
- unnecessary_this
- unrelated_type_equality_checks
- unsafe_html
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
- use_rethrow_when_possible
- valid_regexps
-16
View File
@@ -1,16 +0,0 @@
# webview_windows_example
Demonstrates how to use the webview_windows plugin.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
@@ -1,233 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'package:webview_windows/webview_windows.dart';
import 'package:window_manager/window_manager.dart';
final navigatorKey = GlobalKey<NavigatorState>();
void main() async {
// For full-screen example
WidgetsFlutterBinding.ensureInitialized();
await windowManager.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(navigatorKey: navigatorKey, home: ExampleBrowser());
}
}
class ExampleBrowser extends StatefulWidget {
@override
State<ExampleBrowser> createState() => _ExampleBrowser();
}
class _ExampleBrowser extends State<ExampleBrowser> {
final _controller = WebviewController();
final _textController = TextEditingController();
final List<StreamSubscription> _subscriptions = [];
bool _isWebviewSuspended = false;
@override
void initState() {
super.initState();
initPlatformState();
}
Future<void> initPlatformState() async {
// Optionally initialize the webview environment using
// a custom user data directory
// and/or a custom browser executable directory
// and/or custom chromium command line flags
//await WebviewController.initializeEnvironment(
// additionalArguments: '--show-fps-counter');
try {
await _controller.initialize();
_subscriptions.add(_controller.url.listen((url) {
_textController.text = url;
}));
_subscriptions
.add(_controller.containsFullScreenElementChanged.listen((flag) {
debugPrint('Contains fullscreen element: $flag');
windowManager.setFullScreen(flag);
}));
await _controller.setBackgroundColor(Colors.transparent);
await _controller.setPopupWindowPolicy(WebviewPopupWindowPolicy.deny);
await _controller.loadUrl('https://flutter.dev');
if (!mounted) return;
setState(() {});
} on PlatformException catch (e) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Error'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Code: ${e.code}'),
Text('Message: ${e.message}'),
],
),
actions: [
TextButton(
child: Text('Continue'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
});
}
}
Widget compositeView() {
if (!_controller.value.isInitialized) {
return const Text(
'Not Initialized',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.w900,
),
);
} else {
return Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
Card(
elevation: 0,
child: Row(children: [
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: 'URL',
contentPadding: EdgeInsets.all(10.0),
),
textAlignVertical: TextAlignVertical.center,
controller: _textController,
onSubmitted: (val) {
_controller.loadUrl(val);
},
),
),
IconButton(
icon: Icon(Icons.refresh),
splashRadius: 20,
onPressed: () {
_controller.reload();
},
),
IconButton(
icon: Icon(Icons.developer_mode),
tooltip: 'Open DevTools',
splashRadius: 20,
onPressed: () {
_controller.openDevTools();
},
)
]),
),
Expanded(
child: Card(
color: Colors.transparent,
elevation: 0,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Stack(
children: [
Webview(
_controller,
permissionRequested: _onPermissionRequested,
),
StreamBuilder<LoadingState>(
stream: _controller.loadingState,
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.data == LoadingState.loading) {
return LinearProgressIndicator();
} else {
return SizedBox();
}
}),
],
))),
],
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
tooltip: _isWebviewSuspended ? 'Resume webview' : 'Suspend webview',
onPressed: () async {
if (_isWebviewSuspended) {
await _controller.resume();
} else {
await _controller.suspend();
}
setState(() {
_isWebviewSuspended = !_isWebviewSuspended;
});
},
child: Icon(_isWebviewSuspended ? Icons.play_arrow : Icons.pause),
),
appBar: AppBar(
title: StreamBuilder<String>(
stream: _controller.title,
builder: (context, snapshot) {
return Text(
snapshot.hasData ? snapshot.data! : 'WebView (Windows) Example');
},
)),
body: Center(
child: compositeView(),
),
);
}
Future<WebviewPermissionDecision> _onPermissionRequested(
String url, WebviewPermissionKind kind, bool isUserInitiated) async {
final decision = await showDialog<WebviewPermissionDecision>(
context: navigatorKey.currentContext!,
builder: (BuildContext context) => AlertDialog(
title: const Text('WebView permission requested'),
content: Text('WebView has requested permission \'$kind\''),
actions: <Widget>[
TextButton(
onPressed: () =>
Navigator.pop(context, WebviewPermissionDecision.deny),
child: const Text('Deny'),
),
TextButton(
onPressed: () =>
Navigator.pop(context, WebviewPermissionDecision.allow),
child: const Text('Allow'),
),
],
),
);
return decision ?? WebviewPermissionDecision.none;
}
@override
void dispose() {
_subscriptions.forEach((s) => s.cancel());
_controller.dispose();
super.dispose();
}
}
@@ -1,22 +0,0 @@
name: webview_windows_example
description: Demonstrates how to use the webview_windows plugin.
publish_to: 'none'
environment:
sdk: ">=2.12.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
webview_windows:
path: ../
window_manager: ^0.2.7
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
@@ -1 +0,0 @@
void main() {}
@@ -1,98 +0,0 @@
cmake_minimum_required(VERSION 3.15)
set(CMAKE_SYSTEM_VERSION 10.0.22000 CACHE STRING INTERNAL FORCE)
project(webview_windows_example LANGUAGES CXX)
set(BINARY_NAME "webview_windows_example")
cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Configure build options.
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTICONFIG)
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
CACHE STRING "" FORCE)
else()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
endif()
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
# Use Unicode for all projects.
add_definitions(-DUNICODE -D_UNICODE)
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_17)
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
target_compile_options(${TARGET} PRIVATE /EHsc)
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build
add_subdirectory("runner")
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# Support files are copied into place next to the executable, so that it can
# run in place. This is done instead of making a separate bundle (as on Linux)
# so that building and running from within Visual Studio will work.
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# Make the "install" step default, as it's required to run.
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
CONFIGURATIONS Profile;Release
COMPONENT Runtime)
@@ -1,103 +0,0 @@
cmake_minimum_required(VERSION 3.15)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"flutter_export.h"
"flutter_windows.h"
"flutter_messenger.h"
"flutter_plugin_registrar.h"
"flutter_texture_registrar.h"
)
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
add_dependencies(flutter flutter_assemble)
# === Wrapper ===
list(APPEND CPP_WRAPPER_SOURCES_CORE
"core_implementations.cc"
"standard_codec.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
"plugin_registrar.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_APP
"flutter_engine.cc"
"flutter_view_controller.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
# Wrapper sources needed for a plugin.
add_library(flutter_wrapper_plugin STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
)
apply_standard_settings(flutter_wrapper_plugin)
set_target_properties(flutter_wrapper_plugin PROPERTIES
POSITION_INDEPENDENT_CODE ON)
set_target_properties(flutter_wrapper_plugin PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
target_include_directories(flutter_wrapper_plugin PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_plugin flutter_assemble)
# Wrapper sources needed for the runner.
add_library(flutter_wrapper_app STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_APP}
)
apply_standard_settings(flutter_wrapper_app)
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
target_include_directories(flutter_wrapper_app PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_app flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
${PHONY_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
)
@@ -1,18 +0,0 @@
cmake_minimum_required(VERSION 3.15)
project(runner LANGUAGES CXX)
add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp"
"main.cpp"
"utils.cpp"
"win32_window.cpp"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
"Runner.rc"
"runner.exe.manifest"
)
apply_standard_settings(${BINARY_NAME})
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
add_dependencies(${BINARY_NAME} flutter_assemble)
@@ -1,121 +0,0 @@
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_APP_ICON ICON "resources\\app_icon.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
#ifdef FLUTTER_BUILD_NUMBER
#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
#else
#define VERSION_AS_NUMBER 1,0,0
#endif
#ifdef FLUTTER_BUILD_NAME
#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
#else
#define VERSION_AS_STRING "1.0.0"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_AS_NUMBER
PRODUCTVERSION VERSION_AS_NUMBER
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "FileDescription", "Demonstrates how to use the webview_windows plugin." "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "webview_windows_example" "\0"
VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0"
VALUE "OriginalFilename", "webview_windows_example.exe" "\0"
VALUE "ProductName", "webview_windows_example" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
@@ -1,61 +0,0 @@
#include "flutter_window.h"
#include <optional>
#include "flutter/generated_plugin_registrant.h"
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
: project_(project) {}
FlutterWindow::~FlutterWindow() {}
bool FlutterWindow::OnCreate() {
if (!Win32Window::OnCreate()) {
return false;
}
RECT frame = GetClientArea();
// The size here must match the window dimensions to avoid unnecessary surface
// creation / destruction in the startup path.
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
frame.right - frame.left, frame.bottom - frame.top, project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false;
}
RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
return true;
}
void FlutterWindow::OnDestroy() {
if (flutter_controller_) {
flutter_controller_ = nullptr;
}
Win32Window::OnDestroy();
}
LRESULT
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
// Give Flutter, including plugins, an opportunity to handle window messages.
if (flutter_controller_) {
std::optional<LRESULT> result =
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
lparam);
if (result) {
return *result;
}
}
switch (message) {
case WM_FONTCHANGE:
flutter_controller_->engine()->ReloadSystemFonts();
break;
}
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
}
@@ -1,33 +0,0 @@
#ifndef RUNNER_FLUTTER_WINDOW_H_
#define RUNNER_FLUTTER_WINDOW_H_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <memory>
#include "win32_window.h"
// A window that does nothing but host a Flutter view.
class FlutterWindow : public Win32Window {
public:
// Creates a new FlutterWindow hosting a Flutter view running |project|.
explicit FlutterWindow(const flutter::DartProject& project);
virtual ~FlutterWindow();
protected:
// Win32Window:
bool OnCreate() override;
void OnDestroy() override;
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
LPARAM const lparam) noexcept override;
private:
// The project to run.
flutter::DartProject project_;
// The Flutter instance hosted by this window.
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
};
#endif // RUNNER_FLUTTER_WINDOW_H_
@@ -1,43 +0,0 @@
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>
#include "flutter_window.h"
#include "utils.h"
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
_In_ wchar_t *command_line, _In_ int show_command) {
// Attach to console when present (e.g., 'flutter run') or create a
// new console when running with a debugger.
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
CreateAndAttachConsole();
}
// Initialize COM, so that it is available for use in the library and/or
// plugins.
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
flutter::DartProject project(L"data");
std::vector<std::string> command_line_arguments =
GetCommandLineArguments();
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"webview_windows_example", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);
::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
::CoUninitialize();
return EXIT_SUCCESS;
}
@@ -1,16 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Runner.rc
//
#define IDI_APP_ICON 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>
@@ -1,64 +0,0 @@
#include "utils.h"
#include <flutter_windows.h>
#include <io.h>
#include <stdio.h>
#include <windows.h>
#include <iostream>
void CreateAndAttachConsole() {
if (::AllocConsole()) {
FILE *unused;
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
_dup2(_fileno(stdout), 2);
}
std::ios::sync_with_stdio();
FlutterDesktopResyncOutputStreams();
}
}
std::vector<std::string> GetCommandLineArguments() {
// Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
int argc;
wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
if (argv == nullptr) {
return std::vector<std::string>();
}
std::vector<std::string> command_line_arguments;
// Skip the first argument as it's the binary name.
for (int i = 1; i < argc; i++) {
command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
}
::LocalFree(argv);
return command_line_arguments;
}
std::string Utf8FromUtf16(const wchar_t* utf16_string) {
if (utf16_string == nullptr) {
return std::string();
}
int target_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, nullptr, 0, nullptr, nullptr);
if (target_length == 0) {
return std::string();
}
std::string utf8_string;
utf8_string.resize(target_length);
int converted_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, utf8_string.data(),
target_length, nullptr, nullptr);
if (converted_length == 0) {
return std::string();
}
return utf8_string;
}
@@ -1,19 +0,0 @@
#ifndef RUNNER_UTILS_H_
#define RUNNER_UTILS_H_
#include <string>
#include <vector>
// Creates a console for the process, and redirects stdout and stderr to
// it for both the runner and the Flutter library.
void CreateAndAttachConsole();
// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
// encoded in UTF-8. Returns an empty std::string on failure.
std::string Utf8FromUtf16(const wchar_t* utf16_string);
// Gets the command line arguments passed in as a std::vector<std::string>,
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
std::vector<std::string> GetCommandLineArguments();
#endif // RUNNER_UTILS_H_
@@ -1,245 +0,0 @@
#include "win32_window.h"
#include <flutter_windows.h>
#include "resource.h"
namespace {
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
// The number of Win32Window objects that currently exist.
static int g_active_window_count = 0;
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
// Scale helper to convert logical scaler values to physical using passed in
// scale factor
int Scale(int source, double scale_factor) {
return static_cast<int>(source * scale_factor);
}
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
// This API is only needed for PerMonitor V1 awareness mode.
void EnableFullDpiSupportIfAvailable(HWND hwnd) {
HMODULE user32_module = LoadLibraryA("User32.dll");
if (!user32_module) {
return;
}
auto enable_non_client_dpi_scaling =
reinterpret_cast<EnableNonClientDpiScaling*>(
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
if (enable_non_client_dpi_scaling != nullptr) {
enable_non_client_dpi_scaling(hwnd);
FreeLibrary(user32_module);
}
}
} // namespace
// Manages the Win32Window's window class registration.
class WindowClassRegistrar {
public:
~WindowClassRegistrar() = default;
// Returns the singleton registar instance.
static WindowClassRegistrar* GetInstance() {
if (!instance_) {
instance_ = new WindowClassRegistrar();
}
return instance_;
}
// Returns the name of the window class, registering the class if it hasn't
// previously been registered.
const wchar_t* GetWindowClass();
// Unregisters the window class. Should only be called if there are no
// instances of the window.
void UnregisterWindowClass();
private:
WindowClassRegistrar() = default;
static WindowClassRegistrar* instance_;
bool class_registered_ = false;
};
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
const wchar_t* WindowClassRegistrar::GetWindowClass() {
if (!class_registered_) {
WNDCLASS window_class{};
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.lpszClassName = kWindowClassName;
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc;
RegisterClass(&window_class);
class_registered_ = true;
}
return kWindowClassName;
}
void WindowClassRegistrar::UnregisterWindowClass() {
UnregisterClass(kWindowClassName, nullptr);
class_registered_ = false;
}
Win32Window::Win32Window() {
++g_active_window_count;
}
Win32Window::~Win32Window() {
--g_active_window_count;
Destroy();
}
bool Win32Window::CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size) {
Destroy();
const wchar_t* window_class =
WindowClassRegistrar::GetInstance()->GetWindowClass();
const POINT target_point = {static_cast<LONG>(origin.x),
static_cast<LONG>(origin.y)};
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
double scale_factor = dpi / 96.0;
HWND window = CreateWindow(
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
nullptr, nullptr, GetModuleHandle(nullptr), this);
if (!window) {
return false;
}
return OnCreate();
}
// static
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
if (message == WM_NCCREATE) {
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
EnableFullDpiSupportIfAvailable(window);
that->window_handle_ = window;
} else if (Win32Window* that = GetThisFromHandle(window)) {
return that->MessageHandler(window, message, wparam, lparam);
}
return DefWindowProc(window, message, wparam, lparam);
}
LRESULT
Win32Window::MessageHandler(HWND hwnd,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
switch (message) {
case WM_DESTROY:
window_handle_ = nullptr;
Destroy();
if (quit_on_close_) {
PostQuitMessage(0);
}
return 0;
case WM_DPICHANGED: {
auto newRectSize = reinterpret_cast<RECT*>(lparam);
LONG newWidth = newRectSize->right - newRectSize->left;
LONG newHeight = newRectSize->bottom - newRectSize->top;
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
case WM_SIZE: {
RECT rect = GetClientArea();
if (child_content_ != nullptr) {
// Size and position the child window.
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, TRUE);
}
return 0;
}
case WM_ACTIVATE:
if (child_content_ != nullptr) {
SetFocus(child_content_);
}
return 0;
}
return DefWindowProc(window_handle_, message, wparam, lparam);
}
void Win32Window::Destroy() {
OnDestroy();
if (window_handle_) {
DestroyWindow(window_handle_);
window_handle_ = nullptr;
}
if (g_active_window_count == 0) {
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
}
}
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
return reinterpret_cast<Win32Window*>(
GetWindowLongPtr(window, GWLP_USERDATA));
}
void Win32Window::SetChildContent(HWND content) {
child_content_ = content;
SetParent(content, window_handle_);
RECT frame = GetClientArea();
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
frame.bottom - frame.top, true);
SetFocus(child_content_);
}
RECT Win32Window::GetClientArea() {
RECT frame;
GetClientRect(window_handle_, &frame);
return frame;
}
HWND Win32Window::GetHandle() {
return window_handle_;
}
void Win32Window::SetQuitOnClose(bool quit_on_close) {
quit_on_close_ = quit_on_close;
}
bool Win32Window::OnCreate() {
// No-op; provided for subclasses.
return true;
}
void Win32Window::OnDestroy() {
// No-op; provided for subclasses.
}
@@ -1,98 +0,0 @@
#ifndef RUNNER_WIN32_WINDOW_H_
#define RUNNER_WIN32_WINDOW_H_
#include <windows.h>
#include <functional>
#include <memory>
#include <string>
// A class abstraction for a high DPI-aware Win32 Window. Intended to be
// inherited from by classes that wish to specialize with custom
// rendering and input handling
class Win32Window {
public:
struct Point {
unsigned int x;
unsigned int y;
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
};
struct Size {
unsigned int width;
unsigned int height;
Size(unsigned int width, unsigned int height)
: width(width), height(height) {}
};
Win32Window();
virtual ~Win32Window();
// Creates and shows a win32 window with |title| and position and size using
// |origin| and |size|. New windows are created on the default monitor. Window
// sizes are specified to the OS in physical pixels, hence to ensure a
// consistent size to will treat the width height passed in to this function
// as logical pixels and scale to appropriate for the default monitor. Returns
// true if the window was created successfully.
bool CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size);
// Release OS resources associated with window.
void Destroy();
// Inserts |content| into the window tree.
void SetChildContent(HWND content);
// Returns the backing Window handle to enable clients to set icon and other
// window properties. Returns nullptr if the window has been destroyed.
HWND GetHandle();
// If true, closing this window will quit the application.
void SetQuitOnClose(bool quit_on_close);
// Return a RECT representing the bounds of the current client area.
RECT GetClientArea();
protected:
// Processes and route salient window messages for mouse handling,
// size change and DPI. Delegates handling of these to member overloads that
// inheriting classes can handle.
virtual LRESULT MessageHandler(HWND window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Called when CreateAndShow is called, allowing subclass window-related
// setup. Subclasses should return false if setup fails.
virtual bool OnCreate();
// Called when Destroy is called.
virtual void OnDestroy();
private:
friend class WindowClassRegistrar;
// OS callback called by message pump. Handles the WM_NCCREATE message which
// is passed when the non-client area is being created and enables automatic
// non-client DPI scaling so that the non-client area automatically
// responsponds to changes in DPI. All other messages are handled by
// MessageHandler.
static LRESULT CALLBACK WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Retrieves a class instance pointer for |window|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
bool quit_on_close_ = false;
// window handle for top level window.
HWND window_handle_ = nullptr;
// window handle for hosted content.
HWND child_content_ = nullptr;
};
#endif // RUNNER_WIN32_WINDOW_H_
-43
View File
@@ -1,43 +0,0 @@
import 'package:flutter/services.dart';
const Map<String, SystemMouseCursor> _cursors = {
'none': SystemMouseCursors.none,
'basic': SystemMouseCursors.basic,
'click': SystemMouseCursors.click,
'forbidden': SystemMouseCursors.forbidden,
'wait': SystemMouseCursors.wait,
'progress': SystemMouseCursors.progress,
'contextMenu': SystemMouseCursors.contextMenu,
'help': SystemMouseCursors.help,
'text': SystemMouseCursors.text,
'verticalText': SystemMouseCursors.verticalText,
'cell': SystemMouseCursors.cell,
'precise': SystemMouseCursors.precise,
'move': SystemMouseCursors.move,
'grab': SystemMouseCursors.grab,
'grabbing': SystemMouseCursors.grabbing,
'noDrop': SystemMouseCursors.noDrop,
'alias': SystemMouseCursors.alias,
'copy': SystemMouseCursors.copy,
'disappearing': SystemMouseCursors.disappearing,
'allScroll': SystemMouseCursors.allScroll,
'resizeLeftRight': SystemMouseCursors.resizeLeftRight,
'resizeUpDown': SystemMouseCursors.resizeUpDown,
'resizeUpLeftDownRight': SystemMouseCursors.resizeUpLeftDownRight,
'resizeUpRightDownLeft': SystemMouseCursors.resizeUpRightDownLeft,
'resizeUp': SystemMouseCursors.resizeUp,
'resizeDown': SystemMouseCursors.resizeDown,
'resizeLeft': SystemMouseCursors.resizeLeft,
'resizeRight': SystemMouseCursors.resizeRight,
'resizeUpLeft': SystemMouseCursors.resizeUpLeft,
'resizeUpRight': SystemMouseCursors.resizeUpRight,
'resizeDownLeft': SystemMouseCursors.resizeDownLeft,
'resizeDownRight': SystemMouseCursors.resizeDownRight,
'resizeColumn': SystemMouseCursors.resizeColumn,
'resizeRow': SystemMouseCursors.resizeRow,
'zoomIn': SystemMouseCursors.zoomIn,
'zoomOut': SystemMouseCursors.zoomOut,
};
SystemMouseCursor getCursorByName(String name) =>
_cursors[name] ?? SystemMouseCursors.basic;
-66
View File
@@ -1,66 +0,0 @@
/// Loading state
// Order must match WebviewLoadingState (see webview.h)
enum LoadingState { none, loading, navigationCompleted }
/// Pointer button type
// Order must match WebviewPointerButton (see webview.h)
enum PointerButton { none, primary, secondary, tertiary }
/// Pointer Event kind
// Order must match WebviewPointerEventKind (see webview.h)
enum WebviewPointerEventKind { activate, down, enter, leave, up, update }
/// Permission kind
// Order must match WebviewPermissionKind (see webview.h)
enum WebviewPermissionKind {
unknown,
microphone,
camera,
geoLocation,
notifications,
otherSensors,
clipboardRead
}
enum WebviewPermissionDecision { none, allow, deny }
/// The policy for popup requests.
///
/// [allow] allows popups and will create new windows.
/// [deny] suppresses popups.
/// [sameWindow] displays popup contents in the current WebView.
enum WebviewPopupWindowPolicy { allow, deny, sameWindow }
/// The kind of cross origin resource access for virtual hosts
///
/// [deny] all cross origin requests are denied.
/// [allow] all cross origin requests are allowed.
/// [denyCors] sub resource cross origin requests are allowed, otherwise denied.
///
/// For more detailed information, please refer to
/// [Microsofts](https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2#corewebview2_host_resource_access_kind)
/// documentation.
// Order must match WebviewHostResourceAccessKind (see webview.h)
enum WebviewHostResourceAccessKind { deny, allow, denyCors }
enum WebErrorStatus {
WebErrorStatusUnknown,
WebErrorStatusCertificateCommonNameIsIncorrect,
WebErrorStatusCertificateExpired,
WebErrorStatusClientCertificateContainsErrors,
WebErrorStatusCertificateRevoked,
WebErrorStatusCertificateIsInvalid,
WebErrorStatusServerUnreachable,
WebErrorStatusTimeout,
WebErrorStatusErrorHTTPInvalidServerResponse,
WebErrorStatusConnectionAborted,
WebErrorStatusConnectionReset,
WebErrorStatusDisconnected,
WebErrorStatusCannotConnect,
WebErrorStatusHostNameNotResolved,
WebErrorStatusOperationCanceled,
WebErrorStatusRedirectFailed,
WebErrorStatusUnexpectedError,
WebErrorStatusValidAuthenticationCredentialsRequired,
WebErrorStatusValidProxyAuthenticationRequired,
}
-739
View File
@@ -1,739 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'enums.dart';
import 'cursor.dart';
class HistoryChanged {
final bool canGoBack;
final bool canGoForward;
const HistoryChanged(this.canGoBack, this.canGoForward);
}
typedef PermissionRequestedDelegate
= FutureOr<WebviewPermissionDecision> Function(
String url, WebviewPermissionKind permissionKind, bool isUserInitiated);
typedef ScriptID = String;
/// Attempts to translate a button constant such as [kPrimaryMouseButton]
/// to a [PointerButton]
PointerButton getButton(int value) {
switch (value) {
case kPrimaryMouseButton:
return PointerButton.primary;
case kSecondaryMouseButton:
return PointerButton.secondary;
case kTertiaryButton:
return PointerButton.tertiary;
default:
return PointerButton.none;
}
}
const String _pluginChannelPrefix = 'io.jns.webview.win';
const MethodChannel _pluginChannel = MethodChannel(_pluginChannelPrefix);
class WebviewValue {
const WebviewValue({
required this.isInitialized,
});
final bool isInitialized;
WebviewValue copyWith({
bool? isInitialized,
}) {
return WebviewValue(
isInitialized: isInitialized ?? this.isInitialized,
);
}
WebviewValue.uninitialized()
: this(
isInitialized: false,
);
}
/// Controls a WebView and provides streams for various change events.
class WebviewController extends ValueNotifier<WebviewValue> {
/// Explicitly initializes the underlying WebView environment
/// using an optional [browserExePath], an optional [userDataPath]
/// and optional Chromium command line arguments [additionalArguments].
///
/// The environment is shared between all WebviewController instances and
/// can be initialized only once. Initialization must take place before any
/// WebviewController is created/initialized.
///
/// Throws [PlatformException] if the environment was initialized before.
static Future<void> initializeEnvironment(
{String? userDataPath,
String? browserExePath,
String? additionalArguments}) async {
return _pluginChannel
.invokeMethod('initializeEnvironment', <String, dynamic>{
'userDataPath': userDataPath,
'browserExePath': browserExePath,
'additionalArguments': additionalArguments
});
}
/// Get the browser version info including channel name if it is not the
/// WebView2 Runtime.
/// Returns [null] if the webview2 runtime is not installed.
static Future<String?> getWebViewVersion() async {
return _pluginChannel.invokeMethod<String>('getWebViewVersion');
}
late Completer<void> _creatingCompleter;
int _textureId = 0;
bool _isDisposed = false;
Future<void> get ready => _creatingCompleter.future;
PermissionRequestedDelegate? _permissionRequested;
late MethodChannel _methodChannel;
late EventChannel _eventChannel;
StreamSubscription? _eventStreamSubscription;
final StreamController<String> _urlStreamController =
StreamController<String>();
/// A stream reflecting the current URL.
Stream<String> get url => _urlStreamController.stream;
final StreamController<LoadingState> _loadingStateStreamController =
StreamController<LoadingState>.broadcast();
final StreamController<WebErrorStatus> _onLoadErrorStreamController =
StreamController<WebErrorStatus>();
/// A stream reflecting the current loading state.
Stream<LoadingState> get loadingState => _loadingStateStreamController.stream;
/// A stream reflecting the navigation error when navigation completed with an error.
Stream<WebErrorStatus> get onLoadError => _onLoadErrorStreamController.stream;
final StreamController<HistoryChanged> _historyChangedStreamController =
StreamController<HistoryChanged>();
/// A stream reflecting the current history state.
Stream<HistoryChanged> get historyChanged =>
_historyChangedStreamController.stream;
final StreamController<String> _securityStateChangedStreamController =
StreamController<String>();
/// A stream reflecting the current security state.
Stream<String> get securityStateChanged =>
_securityStateChangedStreamController.stream;
final StreamController<String> _titleStreamController =
StreamController<String>();
/// A stream reflecting the current document title.
Stream<String> get title => _titleStreamController.stream;
final StreamController<SystemMouseCursor> _cursorStreamController =
StreamController<SystemMouseCursor>.broadcast();
/// A stream reflecting the current cursor style.
Stream<SystemMouseCursor> get _cursor => _cursorStreamController.stream;
final StreamController<dynamic> _webMessageStreamController =
StreamController<dynamic>();
Stream<dynamic> get webMessage => _webMessageStreamController.stream;
final StreamController<bool>
_containsFullScreenElementChangedStreamController =
StreamController<bool>.broadcast();
/// A stream reflecting whether the document currently contains full-screen elements.
Stream<bool> get containsFullScreenElementChanged =>
_containsFullScreenElementChangedStreamController.stream;
WebviewController() : super(WebviewValue.uninitialized());
/// Initializes the underlying platform view.
Future<void> initialize() async {
if (_isDisposed) {
return Future<void>.value();
}
_creatingCompleter = Completer<void>();
try {
final reply =
await _pluginChannel.invokeMapMethod<String, dynamic>('initialize');
_textureId = reply!['textureId'];
_methodChannel = MethodChannel('$_pluginChannelPrefix/$_textureId');
_eventChannel = EventChannel('$_pluginChannelPrefix/$_textureId/events');
_eventStreamSubscription =
_eventChannel.receiveBroadcastStream().listen((event) {
final map = event as Map<dynamic, dynamic>;
switch (map['type']) {
case 'urlChanged':
_urlStreamController.add(map['value']);
break;
case 'onLoadError':
final value = WebErrorStatus.values[map['value']];
_onLoadErrorStreamController.add(value);
break;
case 'loadingStateChanged':
final value = LoadingState.values[map['value']];
_loadingStateStreamController.add(value);
break;
case 'historyChanged':
final value = HistoryChanged(
map['value']['canGoBack'], map['value']['canGoForward']);
_historyChangedStreamController.add(value);
break;
case 'securityStateChanged':
_securityStateChangedStreamController.add(map['value']);
break;
case 'titleChanged':
_titleStreamController.add(map['value']);
break;
case 'cursorChanged':
_cursorStreamController.add(getCursorByName(map['value']));
break;
case 'webMessageReceived':
try {
final message = json.decode(map['value']);
_webMessageStreamController.add(message);
} catch (ex) {
_webMessageStreamController.addError(ex);
}
break;
case 'containsFullScreenElementChanged':
_containsFullScreenElementChangedStreamController.add(map['value']);
break;
}
});
_methodChannel.setMethodCallHandler((call) {
if (call.method == 'permissionRequested') {
return _onPermissionRequested(
call.arguments as Map<dynamic, dynamic>);
}
throw MissingPluginException('Unknown method ${call.method}');
});
value = value.copyWith(isInitialized: true);
_creatingCompleter.complete();
} on PlatformException catch (e) {
_creatingCompleter.completeError(e);
}
return _creatingCompleter.future;
}
Future<bool?> _onPermissionRequested(Map<dynamic, dynamic> args) async {
if (_permissionRequested == null) {
return null;
}
final url = args['url'] as String?;
final permissionKindIndex = args['permissionKind'] as int?;
final isUserInitiated = args['isUserInitiated'] as bool?;
if (url != null && permissionKindIndex != null && isUserInitiated != null) {
final permissionKind = WebviewPermissionKind.values[permissionKindIndex];
final decision =
await _permissionRequested!(url, permissionKind, isUserInitiated);
switch (decision) {
case WebviewPermissionDecision.allow:
return true;
case WebviewPermissionDecision.deny:
return false;
default:
return null;
}
}
return null;
}
@override
Future<void> dispose() async {
await _creatingCompleter.future;
if (!_isDisposed) {
_isDisposed = true;
await _eventStreamSubscription?.cancel();
await _pluginChannel.invokeMethod('dispose', _textureId);
}
super.dispose();
}
/// Loads the given [url].
Future<void> loadUrl(String url) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('loadUrl', url);
}
/// Loads a document from the given string.
Future<void> loadStringContent(String content) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('loadStringContent', content);
}
/// Reloads the current document.
Future<void> reload() async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('reload');
}
/// Stops all navigations and pending resource fetches.
Future<void> stop() async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('stop');
}
/// Navigates the WebView to the previous page in the navigation history.
Future<void> goBack() async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('goBack');
}
/// Navigates the WebView to the next page in the navigation history.
Future<void> goForward() async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('goForward');
}
/// Adds the provided JavaScript [script] to a list of scripts that should be run after the global
/// object has been created, but before the HTML document has been parsed and before any
/// other script included by the HTML document is run.
///
/// Returns a [ScriptID] on success which can be used for [removeScriptToExecuteOnDocumentCreated].
///
/// see https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.1264.42#addscripttoexecuteondocumentcreated
Future<ScriptID?> addScriptToExecuteOnDocumentCreated(String script) async {
if (_isDisposed) {
return null;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod<String?>(
'addScriptToExecuteOnDocumentCreated', script);
}
/// Removes the script identified by [scriptId] from the list of registered scripts.
///
/// see https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.1264.42#removescripttoexecuteondocumentcreated
Future<void> removeScriptToExecuteOnDocumentCreated(ScriptID scriptId) async {
if (_isDisposed) {
return null;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod(
'removeScriptToExecuteOnDocumentCreated', scriptId);
}
/// Runs the JavaScript [script] in the current top-level document rendered in
/// the WebView and returns its result.
///
/// see https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.1264.42#executescript
Future<dynamic> executeScript(String script) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
final data = await _methodChannel.invokeMethod('executeScript', script);
if (data == null) return null;
return jsonDecode(data as String);
}
/// Posts the given JSON-formatted message to the current document.
Future<void> postWebMessage(String message) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('postWebMessage', message);
}
/// Sets the user agent value.
Future<void> setUserAgent(String userAgent) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('setUserAgent', userAgent);
}
/// Clears browser cookies.
Future<void> clearCookies() async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('clearCookies');
}
/// Clears browser cache.
Future<void> clearCache() async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('clearCache');
}
/// Toggles ignoring cache for each request. If true, cache will not be used.
Future<void> setCacheDisabled(bool disabled) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('setCacheDisabled', disabled);
}
/// Opens the Browser DevTools in a separate window
Future<void> openDevTools() async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('openDevTools');
}
/// Sets the background color to the provided [color].
///
/// Due to a limitation of the underlying WebView implementation,
/// semi-transparent values are not supported.
/// Any non-zero alpha value will be considered as opaque (0xff).
Future<void> setBackgroundColor(Color color) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod(
'setBackgroundColor', color.value.toSigned(32));
}
/// Sets the zoom factor.
Future<void> setZoomFactor(double zoomFactor) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('setZoomFactor', zoomFactor);
}
/// Sets the [WebviewPopupWindowPolicy].
Future<void> setPopupWindowPolicy(
WebviewPopupWindowPolicy popupPolicy) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod(
'setPopupWindowPolicy', popupPolicy.index);
}
/// Suspends the web view.
Future<void> suspend() async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('suspend');
}
/// Resumes the web view.
Future<void> resume() async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('resume');
}
/// Adds a Virtual Host Name Mapping.
///
/// Please refer to
/// [Microsofts](https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2_3#setvirtualhostnametofoldermapping)
/// documentation for more details.
Future<void> addVirtualHostNameMapping(String hostName, String folderPath,
WebviewHostResourceAccessKind accessKind) async {
if (_isDisposed) {
return;
}
return _methodChannel.invokeMethod(
'setVirtualHostNameMapping', [hostName, folderPath, accessKind.index]);
}
/// Removes a Virtual Host Name Mapping.
///
/// Please refer to
/// [Microsofts](https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2_3#clearvirtualhostnametofoldermapping)
/// documentation for more details.
Future<void> removeVirtualHostNameMapping(String hostName) async {
if (_isDisposed) {
return;
}
return _methodChannel.invokeMethod('clearVirtualHostNameMapping', hostName);
}
/// Limits the number of frames per second to the given value.
Future<void> setFpsLimit([int? maxFps = 0]) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('setFpsLimit', maxFps);
}
/// Sends a Pointer (Touch) update
Future<void> _setPointerUpdate(WebviewPointerEventKind kind, int pointer,
Offset position, double size, double pressure) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('setPointerUpdate',
[pointer, kind.index, position.dx, position.dy, size, pressure]);
}
/// Moves the virtual cursor to [position].
Future<void> _setCursorPos(Offset position) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel
.invokeMethod('setCursorPos', [position.dx, position.dy]);
}
/// Indicates whether the specified [button] is currently down.
Future<void> _setPointerButtonState(PointerButton button, bool isDown) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('setPointerButton',
<String, dynamic>{'button': button.index, 'isDown': isDown});
}
/// Sets the horizontal and vertical scroll delta.
Future<void> _setScrollDelta(double dx, double dy) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel.invokeMethod('setScrollDelta', [dx, dy]);
}
/// Sets the surface size to the provided [size].
Future<void> _setSize(Size size, double scaleFactor) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel
.invokeMethod('setSize', [size.width, size.height, scaleFactor]);
}
}
class Webview extends StatefulWidget {
final WebviewController controller;
final PermissionRequestedDelegate? permissionRequested;
final double? width;
final double? height;
/// An optional scale factor. Defaults to [FlutterView.devicePixelRatio] for
/// rendering in native resolution.
/// Setting this to 1.0 will disable high-DPI support.
/// This should only be needed to mimic old behavior before high-DPI support
/// was available.
final double? scaleFactor;
/// The [FilterQuality] used for scaling the texture's contents.
/// Defaults to [FilterQuality.none] as this renders in native resolution
/// unless specifying a [scaleFactor].
final FilterQuality filterQuality;
const Webview(this.controller,
{this.width,
this.height,
this.permissionRequested,
this.scaleFactor,
this.filterQuality = FilterQuality.none});
@override
_WebviewState createState() => _WebviewState();
}
class _WebviewState extends State<Webview> {
final GlobalKey _key = GlobalKey();
final _downButtons = <int, PointerButton>{};
PointerDeviceKind _pointerKind = PointerDeviceKind.unknown;
MouseCursor _cursor = SystemMouseCursors.basic;
WebviewController get _controller => widget.controller;
StreamSubscription? _cursorSubscription;
@override
void initState() {
super.initState();
// TODO: Refactor callback and event handling and
// remove this line
_controller._permissionRequested = widget.permissionRequested;
// Report initial surface size
WidgetsBinding.instance.addPostFrameCallback((_) => _reportSurfaceSize());
_cursorSubscription = _controller._cursor.listen((cursor) {
setState(() {
_cursor = cursor;
});
});
}
@override
Widget build(BuildContext context) {
return (widget.height != null && widget.width != null)
? SizedBox(
key: _key,
width: widget.width,
height: widget.height,
child: _buildInner())
: SizedBox.expand(key: _key, child: _buildInner());
}
Widget _buildInner() {
return NotificationListener<SizeChangedLayoutNotification>(
onNotification: (notification) {
_reportSurfaceSize();
return true;
},
child: SizeChangedLayoutNotifier(
child: _controller.value.isInitialized
? Listener(
onPointerHover: (ev) {
// ev.kind is for whatever reason not set to touch
// even on touch input
if (_pointerKind == PointerDeviceKind.touch) {
// Ignoring hover events on touch for now
return;
}
_controller._setCursorPos(ev.localPosition);
},
onPointerDown: (ev) {
_pointerKind = ev.kind;
if (ev.kind == PointerDeviceKind.touch) {
_controller._setPointerUpdate(
WebviewPointerEventKind.down,
ev.pointer,
ev.localPosition,
ev.size,
ev.pressure);
return;
}
final button = getButton(ev.buttons);
_downButtons[ev.pointer] = button;
_controller._setPointerButtonState(button, true);
},
onPointerUp: (ev) {
_pointerKind = ev.kind;
if (ev.kind == PointerDeviceKind.touch) {
_controller._setPointerUpdate(
WebviewPointerEventKind.up,
ev.pointer,
ev.localPosition,
ev.size,
ev.pressure);
return;
}
final button = _downButtons.remove(ev.pointer);
if (button != null) {
_controller._setPointerButtonState(button, false);
}
},
onPointerCancel: (ev) {
_pointerKind = ev.kind;
final button = _downButtons.remove(ev.pointer);
if (button != null) {
_controller._setPointerButtonState(button, false);
}
},
onPointerMove: (ev) {
_pointerKind = ev.kind;
if (ev.kind == PointerDeviceKind.touch) {
_controller._setPointerUpdate(
WebviewPointerEventKind.update,
ev.pointer,
ev.localPosition,
ev.size,
ev.pressure);
} else {
_controller._setCursorPos(ev.localPosition);
}
},
onPointerSignal: (signal) {
if (signal is PointerScrollEvent) {
_controller._setScrollDelta(
-signal.scrollDelta.dx, -signal.scrollDelta.dy);
}
},
onPointerPanZoomUpdate: (signal) {
_controller._setScrollDelta(
signal.panDelta.dx, signal.panDelta.dy);
},
child: MouseRegion(
cursor: _cursor,
child: Texture(
textureId: _controller._textureId,
filterQuality: widget.filterQuality,
)),
)
: const SizedBox()));
}
void _reportSurfaceSize() async {
final box = _key.currentContext?.findRenderObject() as RenderBox?;
if (box != null) {
await _controller.ready;
unawaited(_controller._setSize(
box.size, widget.scaleFactor ?? window.devicePixelRatio));
}
}
@override
void dispose() {
super.dispose();
_cursorSubscription?.cancel();
}
}
@@ -1,2 +0,0 @@
export 'src/enums.dart';
export 'src/webview.dart';
-23
View File
@@ -1,23 +0,0 @@
name: webview_windows
description: A WebView2-powered webview implementation for the Windows platform.
version: 0.4.0
repository: https://github.com/jnschulze/flutter-webview-windows
homepage: https://jns.io
environment:
sdk: ">=2.13.0 <3.0.0"
flutter: ">=3.3.0"
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
plugin:
platforms:
windows:
pluginClass: WebviewWindowsPlugin
@@ -1 +0,0 @@
void main() {}
@@ -1,98 +0,0 @@
cmake_minimum_required(VERSION 3.15)
set(PROJECT_NAME "webview_windows")
set(WIL_VERSION "1.0.220914.1")
set(WEBVIEW_VERSION "1.0.1210.39")
message(VERBOSE "CMake system version is ${CMAKE_SYSTEM_VERSION} (using SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION})")
project(${PROJECT_NAME} LANGUAGES CXX)
# This value is used when generating builds using this plugin, so it must
# not be changed
set(PLUGIN_NAME "webview_windows_plugin")
set(NUGET_URL https://dist.nuget.org/win-x86-commandline/v5.10.0/nuget.exe)
set(NUGET_SHA256 852b71cc8c8c2d40d09ea49d321ff56fd2397b9d6ea9f96e532530307bbbafd3)
find_program(NUGET nuget)
set(LOCAL_NUGET "${CMAKE_SOURCE_DIR}/../tools/nuget.exe")
if(EXISTS ${LOCAL_NUGET})
set(NUGET ${LOCAL_NUGET})
endif()
if(NOT NUGET)
message(NOTICE "Nuget is not installed.")
set(NUGET ${CMAKE_BINARY_DIR}/nuget.exe)
if (NOT EXISTS ${NUGET})
message(NOTICE "Attempting to download nuget.")
file(DOWNLOAD ${NUGET_URL} ${NUGET})
endif()
file(SHA256 ${NUGET} NUGET_DL_HASH)
if (NOT NUGET_DL_HASH STREQUAL NUGET_SHA256)
message(FATAL_ERROR "Integrity check for ${NUGET} failed.")
endif()
endif()
add_custom_target(${PROJECT_NAME}_DEPENDENCIES_DOWNLOAD ALL)
add_custom_command(
TARGET ${PROJECT_NAME}_DEPENDENCIES_DOWNLOAD PRE_BUILD
COMMAND ${NUGET} install Microsoft.Windows.ImplementationLibrary -Version ${WIL_VERSION} -ExcludeVersion -OutputDirectory ${CMAKE_BINARY_DIR}/packages
COMMAND ${NUGET} install Microsoft.Web.WebView2 -Version ${WEBVIEW_VERSION} -ExcludeVersion -OutputDirectory ${CMAKE_BINARY_DIR}/packages
)
add_library(${PLUGIN_NAME} SHARED
"webview_windows_plugin.cc"
"webview_platform.cc"
"webview.cc"
"webview_host.cc"
"webview_bridge.cc"
"texture_bridge.cc"
"graphics_context.cc"
"util/direct3d11.interop.cc"
"util/rohelper.cc"
"util/string_converter.cc"
)
if(MSVC)
target_compile_options(${PLUGIN_NAME} PRIVATE "/await")
endif()
if(NOT FLUTTER_WEBVIEW_WINDOWS_USE_TEXTURE_FALLBACK)
message(STATUS "Building with D3D texture support.")
target_compile_definitions("${PLUGIN_NAME}" PRIVATE
HAVE_FLUTTER_D3D_TEXTURE
)
target_sources("${PLUGIN_NAME}" PRIVATE
"texture_bridge_gpu.cc"
)
else()
message(STATUS "Building with fallback PixelBuffer texture.")
target_sources("${PLUGIN_NAME}" PRIVATE
"texture_bridge_fallback.cc"
"util/cpuid/cpuinfo.cc"
)
# Enable AVX2 for pixel buffer conversions
if(MSVC)
target_compile_options(${PLUGIN_NAME} PRIVATE "/arch:AVX2" "/await")
endif()
endif()
apply_standard_settings(${PLUGIN_NAME})
target_compile_features(${PLUGIN_NAME} PUBLIC cxx_std_20) # For std::format support
set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden)
target_link_libraries(${PLUGIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Web.WebView2/build/native/Microsoft.Web.WebView2.targets)
target_link_libraries(${PLUGIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.ImplementationLibrary/build/native/Microsoft.Windows.ImplementationLibrary.targets)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include"
)
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
set(webview_windows_bundled_libraries
PARENT_SCOPE
)
@@ -1,150 +0,0 @@
#include "graphics_context.h"
#include "util/d3dutil.h"
#include "util/direct3d11.interop.h"
GraphicsContext::GraphicsContext(rx::RoHelper* rohelper) : rohelper_(rohelper) {
device_ = CreateD3DDevice();
if (!device_) {
return;
}
device_->GetImmediateContext(device_context_.put());
if (FAILED(util::CreateDirect3D11DeviceFromDXGIDevice(
device_.try_as<IDXGIDevice>().get(),
(IInspectable**)device_winrt_.put()))) {
return;
}
valid_ = true;
}
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor>
GraphicsContext::CreateCompositor() {
HSTRING className;
HSTRING_HEADER classNameHeader;
if (FAILED(rohelper_->GetStringReference(
RuntimeClass_Windows_UI_Composition_Compositor, &className,
&classNameHeader))) {
return nullptr;
}
winrt::com_ptr<IActivationFactory> af;
if (FAILED(rohelper_->GetActivationFactory(
className, __uuidof(IActivationFactory), af.put_void()))) {
return nullptr;
}
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor> compositor;
if (FAILED(af->ActivateInstance(
reinterpret_cast<IInspectable**>(compositor.put())))) {
return nullptr;
}
return compositor;
}
winrt::com_ptr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
GraphicsContext::CreateGraphicsCaptureItemFromVisual(
ABI::Windows::UI::Composition::IVisual* visual) const {
HSTRING className;
HSTRING_HEADER classNameHeader;
if (FAILED(rohelper_->GetStringReference(
RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem, &className,
&classNameHeader))) {
return nullptr;
}
ABI::Windows::Graphics::Capture::IGraphicsCaptureItemStatics*
capture_item_statics;
if (FAILED(rohelper_->GetActivationFactory(
className,
__uuidof(
ABI::Windows::Graphics::Capture::IGraphicsCaptureItemStatics),
(void**)&capture_item_statics))) {
return nullptr;
}
winrt::com_ptr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
capture_item;
if (FAILED(
capture_item_statics->CreateFromVisual(visual, capture_item.put()))) {
return nullptr;
}
return capture_item;
}
winrt::com_ptr<ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool>
GraphicsContext::CreateCaptureFramePool(
ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice* device,
ABI::Windows::Graphics::DirectX::DirectXPixelFormat pixelFormat,
INT32 numberOfBuffers, ABI::Windows::Graphics::SizeInt32 size) const {
HSTRING className;
HSTRING_HEADER classNameHeader;
if (FAILED(rohelper_->GetStringReference(
RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool,
&className, &classNameHeader))) {
return nullptr;
}
ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePoolStatics*
capture_frame_pool_statics;
if (FAILED(rohelper_->GetActivationFactory(
className,
__uuidof(ABI::Windows::Graphics::Capture::
IDirect3D11CaptureFramePoolStatics),
(void**)&capture_frame_pool_statics))) {
return nullptr;
}
winrt::com_ptr<ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool>
capture_frame_pool;
if (FAILED(capture_frame_pool_statics->Create(device, pixelFormat,
numberOfBuffers, size,
capture_frame_pool.put()))) {
return nullptr;
}
return capture_frame_pool;
}
winrt::com_ptr<ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool>
GraphicsContext::CreateFreeThreadedCaptureFramePool(
ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice* device,
ABI::Windows::Graphics::DirectX::DirectXPixelFormat pixelFormat,
INT32 numberOfBuffers, ABI::Windows::Graphics::SizeInt32 size) const {
HSTRING className;
HSTRING_HEADER classNameHeader;
if (FAILED(rohelper_->GetStringReference(
RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool,
&className, &classNameHeader))) {
return nullptr;
}
ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePoolStatics2*
capture_frame_pool_statics;
if (FAILED(rohelper_->GetActivationFactory(
className,
__uuidof(ABI::Windows::Graphics::Capture::
IDirect3D11CaptureFramePoolStatics2),
(void**)&capture_frame_pool_statics))) {
return nullptr;
}
winrt::com_ptr<ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool>
capture_frame_pool;
if (FAILED(capture_frame_pool_statics->CreateFreeThreaded(
device, pixelFormat, numberOfBuffers, size,
capture_frame_pool.put()))) {
return nullptr;
}
return capture_frame_pool;
}
@@ -1,49 +0,0 @@
#pragma once
#include <D3d11.h>
#include <windows.graphics.capture.h>
#include <windows.ui.composition.h>
#include <winrt/Windows.Foundation.h>
#include "util/rohelper.h"
class GraphicsContext {
public:
GraphicsContext(rx::RoHelper* rohelper);
inline bool IsValid() const { return valid_; }
ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice* device() const {
return device_winrt_.get();
}
ID3D11Device* d3d_device() const { return device_.get(); }
ID3D11DeviceContext* d3d_device_context() const {
return device_context_.get();
}
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor> CreateCompositor();
winrt::com_ptr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
CreateGraphicsCaptureItemFromVisual(
ABI::Windows::UI::Composition::IVisual* visual) const;
winrt::com_ptr<ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool>
CreateCaptureFramePool(
ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice* device,
ABI::Windows::Graphics::DirectX::DirectXPixelFormat pixelFormat,
INT32 numberOfBuffers, ABI::Windows::Graphics::SizeInt32 size) const;
winrt::com_ptr<ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool>
CreateFreeThreadedCaptureFramePool(
ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice* device,
ABI::Windows::Graphics::DirectX::DirectXPixelFormat pixelFormat,
INT32 numberOfBuffers, ABI::Windows::Graphics::SizeInt32 size) const;
private:
bool valid_ = false;
rx::RoHelper* rohelper_;
winrt::com_ptr<ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>
device_winrt_;
winrt::com_ptr<ID3D11Device> device_{nullptr};
winrt::com_ptr<ID3D11DeviceContext> device_context_{nullptr};
};
@@ -1,23 +0,0 @@
#ifndef FLUTTER_PLUGIN_WEBVIEW_WINDOWS_PLUGIN_H_
#define FLUTTER_PLUGIN_WEBVIEW_WINDOWS_PLUGIN_H_
#include <flutter_plugin_registrar.h>
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)
#else
#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)
#endif
#if defined(__cplusplus)
extern "C" {
#endif
FLUTTER_PLUGIN_EXPORT void WebviewWindowsPluginRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar);
#if defined(__cplusplus)
} // extern "C"
#endif
#endif // FLUTTER_PLUGIN_WEBVIEW_WINDOWS_PLUGIN_H_
@@ -1,176 +0,0 @@
#include "texture_bridge.h"
#include <windows.foundation.h>
#include <algorithm>
#include <atomic>
#include <cassert>
#include <iostream>
#include "util/direct3d11.interop.h"
namespace {
const int kNumBuffers = 1;
} // namespace
TextureBridge::TextureBridge(GraphicsContext* graphics_context,
ABI::Windows::UI::Composition::IVisual* visual)
: graphics_context_(graphics_context) {
capture_item_ =
graphics_context_->CreateGraphicsCaptureItemFromVisual(visual);
assert(capture_item_);
capture_item_->add_Closed(
Microsoft::WRL::Callback<ABI::Windows::Foundation::ITypedEventHandler<
ABI::Windows::Graphics::Capture::GraphicsCaptureItem*,
IInspectable*>>(
[](ABI::Windows::Graphics::Capture::IGraphicsCaptureItem* item,
IInspectable* args) -> HRESULT {
std::cerr << "Capture item was closed." << std::endl;
return S_OK;
})
.Get(),
&on_closed_token_);
}
TextureBridge::~TextureBridge() {
const std::lock_guard<std::mutex> lock(mutex_);
StopInternal();
if (capture_item_) {
capture_item_->remove_Closed(on_closed_token_);
}
}
bool TextureBridge::Start() {
const std::lock_guard<std::mutex> lock(mutex_);
if (is_running_ || !capture_item_) {
return false;
}
ABI::Windows::Graphics::SizeInt32 size;
capture_item_->get_Size(&size);
frame_pool_ = graphics_context_->CreateCaptureFramePool(
graphics_context_->device(),
static_cast<ABI::Windows::Graphics::DirectX::DirectXPixelFormat>(
kPixelFormat),
kNumBuffers, size);
assert(frame_pool_);
frame_pool_->add_FrameArrived(
Microsoft::WRL::Callback<ABI::Windows::Foundation::ITypedEventHandler<
ABI::Windows::Graphics::Capture::Direct3D11CaptureFramePool*,
IInspectable*>>(
[this](ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool*
pool,
IInspectable* args) -> HRESULT {
OnFrameArrived();
return S_OK;
})
.Get(),
&on_frame_arrived_token_);
if (FAILED(frame_pool_->CreateCaptureSession(capture_item_.get(),
capture_session_.put()))) {
std::cerr << "Creating capture session failed." << std::endl;
return false;
}
if (SUCCEEDED(capture_session_->StartCapture())) {
is_running_ = true;
return true;
}
return false;
}
void TextureBridge::Stop() {
const std::lock_guard<std::mutex> lock(mutex_);
StopInternal();
}
void TextureBridge::StopInternal() {
if (is_running_) {
is_running_ = false;
frame_pool_->remove_FrameArrived(on_frame_arrived_token_);
auto closable =
capture_session_.try_as<ABI::Windows::Foundation::IClosable>();
assert(closable);
closable->Close();
capture_session_ = nullptr;
}
}
void TextureBridge::OnFrameArrived() {
const std::lock_guard<std::mutex> lock(mutex_);
if (!is_running_) {
return;
}
bool has_frame = false;
winrt::com_ptr<ABI::Windows::Graphics::Capture::IDirect3D11CaptureFrame>
frame;
auto hr = frame_pool_->TryGetNextFrame(frame.put());
if (SUCCEEDED(hr) && frame) {
winrt::com_ptr<
ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface>
frame_surface;
if (SUCCEEDED(frame->get_Surface(frame_surface.put()))) {
last_frame_ =
util::TryGetDXGIInterfaceFromObject<ID3D11Texture2D>(frame_surface);
has_frame = !ShouldDropFrame();
}
}
if (needs_update_) {
ABI::Windows::Graphics::SizeInt32 size;
capture_item_->get_Size(&size);
frame_pool_->Recreate(
graphics_context_->device(),
static_cast<ABI::Windows::Graphics::DirectX::DirectXPixelFormat>(
kPixelFormat),
kNumBuffers, size);
needs_update_ = false;
}
if (has_frame && frame_available_) {
frame_available_();
}
}
bool TextureBridge::ShouldDropFrame() {
if (!frame_duration_.has_value()) {
return false;
}
auto now = std::chrono::high_resolution_clock::now();
bool should_drop_frame = false;
if (last_frame_timestamp_.has_value()) {
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_frame_timestamp_.value());
should_drop_frame = diff < frame_duration_.value();
}
if (!should_drop_frame) {
last_frame_timestamp_ = now;
}
return should_drop_frame;
}
void TextureBridge::NotifySurfaceSizeChanged() {
const std::lock_guard<std::mutex> lock(mutex_);
needs_update_ = true;
}
void TextureBridge::SetFpsLimit(std::optional<int> max_fps) {
const std::lock_guard<std::mutex> lock(mutex_);
auto value = max_fps.value_or(0);
if (value != 0) {
frame_duration_ = FrameDuration(1000.0 / value);
} else {
frame_duration_.reset();
last_frame_timestamp_.reset();
}
}
@@ -1,74 +0,0 @@
#pragma once
#include <windows.graphics.capture.h>
#include <wrl.h>
#include <chrono>
#include <cstdint>
#include <functional>
#include <mutex>
#include <optional>
#include "graphics_context.h"
typedef struct {
size_t width;
size_t height;
} Size;
class TextureBridge {
public:
typedef std::function<void()> FrameAvailableCallback;
typedef std::function<void(Size size)> SurfaceSizeChangedCallback;
typedef std::chrono::duration<double, std::milli> FrameDuration;
TextureBridge(GraphicsContext* graphics_context,
ABI::Windows::UI::Composition::IVisual* visual);
virtual ~TextureBridge();
bool Start();
void Stop();
void SetOnFrameAvailable(FrameAvailableCallback callback) {
frame_available_ = std::move(callback);
}
void SetOnSurfaceSizeChanged(SurfaceSizeChangedCallback callback) {
surface_size_changed_ = std::move(callback);
}
void NotifySurfaceSizeChanged();
void SetFpsLimit(std::optional<int> max_fps);
protected:
bool is_running_ = false;
const GraphicsContext* graphics_context_;
std::mutex mutex_;
std::optional<FrameDuration> frame_duration_ = std::nullopt;
FrameAvailableCallback frame_available_;
SurfaceSizeChangedCallback surface_size_changed_;
std::atomic<bool> needs_update_ = false;
winrt::com_ptr<ID3D11Texture2D> last_frame_;
std::optional<std::chrono::high_resolution_clock::time_point>
last_frame_timestamp_;
winrt::com_ptr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
capture_item_;
winrt::com_ptr<ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool>
frame_pool_;
winrt::com_ptr<ABI::Windows::Graphics::Capture::IGraphicsCaptureSession>
capture_session_;
EventRegistrationToken on_closed_token_ = {};
EventRegistrationToken on_frame_arrived_token_ = {};
virtual void StopInternal();
void OnFrameArrived();
bool ShouldDropFrame();
// corresponds to DXGI_FORMAT_B8G8R8A8_UNORM
static constexpr auto kPixelFormat = ABI::Windows::Graphics::DirectX::
DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized;
};
@@ -1,135 +0,0 @@
#include "texture_bridge_fallback.h"
#include <iostream>
#include "util/direct3d11.interop.h"
#include "util/swizzle.h"
TextureBridgeFallback::TextureBridgeFallback(
GraphicsContext* graphics_context,
ABI::Windows::UI::Composition::IVisual* visual)
: TextureBridge(graphics_context, visual) {}
TextureBridgeFallback::~TextureBridgeFallback() {
const std::lock_guard<std::mutex> lock(buffer_mutex_);
}
void TextureBridgeFallback::ProcessFrame(
winrt::com_ptr<ID3D11Texture2D> src_texture) {
D3D11_TEXTURE2D_DESC desc;
src_texture->GetDesc(&desc);
const auto width = desc.Width;
const auto height = desc.Height;
bool is_exact_size;
EnsureStagingTexture(width, height, is_exact_size);
auto device_context = graphics_context_->d3d_device_context();
auto staging_texture = staging_texture_.get();
if (is_exact_size) {
device_context->CopyResource(staging_texture, src_texture.get());
} else {
D3D11_BOX client_box;
client_box.top = 0;
client_box.left = 0;
client_box.right = width;
client_box.bottom = height;
client_box.front = 0;
client_box.back = 1;
device_context->CopySubresourceRegion(staging_texture, 0, 0, 0, 0,
src_texture.get(), 0, &client_box);
}
D3D11_MAPPED_SUBRESOURCE mappedResource;
if (!SUCCEEDED(device_context->Map(staging_texture, 0, D3D11_MAP_READ, 0,
&mappedResource))) {
return;
}
{
const std::lock_guard<std::mutex> lock(buffer_mutex_);
if (!pixel_buffer_ || pixel_buffer_->width != width ||
pixel_buffer_->height != height) {
if (!pixel_buffer_) {
pixel_buffer_ = std::make_unique<FlutterDesktopPixelBuffer>();
pixel_buffer_->release_context = &buffer_mutex_;
// Gets invoked after the FlutterDesktopPixelBuffer's
// backing buffer has been uploaded.
pixel_buffer_->release_callback = [](void* opaque) {
auto mutex = reinterpret_cast<std::mutex*>(opaque);
// Gets locked just before |CopyPixelBuffer| returns.
mutex->unlock();
};
}
pixel_buffer_->width = width;
pixel_buffer_->height = height;
const auto size = width * height * 4;
backing_pixel_buffer_.reset(new uint8_t[size]);
pixel_buffer_->buffer = backing_pixel_buffer_.get();
}
const auto src_pitch_in_pixels = mappedResource.RowPitch / 4;
RGBA_to_BGRA(reinterpret_cast<uint32_t*>(backing_pixel_buffer_.get()),
static_cast<const uint32_t*>(mappedResource.pData), height,
src_pitch_in_pixels, width);
}
device_context->Unmap(staging_texture, 0);
}
void TextureBridgeFallback::EnsureStagingTexture(uint32_t width,
uint32_t height,
bool& is_exact_size) {
// Only recreate an existing texture if it's too small.
if (!staging_texture_ || staging_texture_size_.width < width ||
staging_texture_size_.height < height) {
D3D11_TEXTURE2D_DESC dstDesc = {};
dstDesc.ArraySize = 1;
dstDesc.MipLevels = 1;
dstDesc.BindFlags = 0;
dstDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
dstDesc.Format = static_cast<DXGI_FORMAT>(kPixelFormat);
dstDesc.Width = width;
dstDesc.Height = height;
dstDesc.MiscFlags = 0;
dstDesc.SampleDesc.Count = 1;
dstDesc.SampleDesc.Quality = 0;
dstDesc.Usage = D3D11_USAGE_STAGING;
staging_texture_ = nullptr;
if (!SUCCEEDED(graphics_context_->d3d_device()->CreateTexture2D(
&dstDesc, nullptr, staging_texture_.put()))) {
std::cerr << "Creating dst texture failed" << std::endl;
return;
}
staging_texture_size_ = {width, height};
}
is_exact_size = staging_texture_size_.width == width &&
staging_texture_size_.height == height;
}
const FlutterDesktopPixelBuffer* TextureBridgeFallback::CopyPixelBuffer(
size_t width, size_t height) {
const std::lock_guard<std::mutex> lock(mutex_);
if (!is_running_) {
return nullptr;
}
if (last_frame_) {
ProcessFrame(last_frame_);
}
auto buffer = pixel_buffer_.get();
// Only lock the mutex if the buffer is not null
// (to ensure the release callback gets called)
if (buffer) {
// Gets unlocked in the FlutterDesktopPixelBuffer's release callback.
buffer_mutex_.lock();
}
return buffer;
}
@@ -1,27 +0,0 @@
#pragma once
#include <flutter/texture_registrar.h>
#include <mutex>
#include "texture_bridge.h"
class TextureBridgeFallback : public TextureBridge {
public:
TextureBridgeFallback(GraphicsContext* graphics_context,
ABI::Windows::UI::Composition::IVisual* visual);
~TextureBridgeFallback() override;
const FlutterDesktopPixelBuffer* CopyPixelBuffer(size_t width, size_t height);
private:
Size staging_texture_size_ = {0, 0};
winrt::com_ptr<ID3D11Texture2D> staging_texture_{nullptr};
std::mutex buffer_mutex_;
std::unique_ptr<uint8_t> backing_pixel_buffer_;
std::unique_ptr<FlutterDesktopPixelBuffer> pixel_buffer_;
void ProcessFrame(winrt::com_ptr<ID3D11Texture2D> src_texture);
void EnsureStagingTexture(uint32_t width, uint32_t height,
bool& is_exact_size);
};
@@ -1,99 +0,0 @@
#include "texture_bridge_gpu.h"
#include <iostream>
#include "util/direct3d11.interop.h"
TextureBridgeGpu::TextureBridgeGpu(
GraphicsContext* graphics_context,
ABI::Windows::UI::Composition::IVisual* visual)
: TextureBridge(graphics_context, visual) {
surface_descriptor_.struct_size = sizeof(FlutterDesktopGpuSurfaceDescriptor);
surface_descriptor_.format =
kFlutterDesktopPixelFormatNone; // no format required for DXGI surfaces
}
void TextureBridgeGpu::ProcessFrame(
winrt::com_ptr<ID3D11Texture2D> src_texture) {
D3D11_TEXTURE2D_DESC desc;
src_texture->GetDesc(&desc);
const auto width = desc.Width;
const auto height = desc.Height;
EnsureSurface(width, height);
auto device_context = graphics_context_->d3d_device_context();
device_context->CopyResource(surface_.get(), src_texture.get());
device_context->Flush();
}
void TextureBridgeGpu::EnsureSurface(uint32_t width, uint32_t height) {
if (!surface_ || surface_size_.width != width ||
surface_size_.height != height) {
D3D11_TEXTURE2D_DESC dstDesc = {};
dstDesc.ArraySize = 1;
dstDesc.MipLevels = 1;
dstDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
dstDesc.CPUAccessFlags = 0;
dstDesc.Format = static_cast<DXGI_FORMAT>(kPixelFormat);
dstDesc.Width = width;
dstDesc.Height = height;
dstDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
dstDesc.SampleDesc.Count = 1;
dstDesc.SampleDesc.Quality = 0;
dstDesc.Usage = D3D11_USAGE_DEFAULT;
surface_ = nullptr;
if (!SUCCEEDED(graphics_context_->d3d_device()->CreateTexture2D(
&dstDesc, nullptr, surface_.put()))) {
std::cerr << "Creating intermediate texture failed" << std::endl;
return;
}
HANDLE shared_handle;
surface_.try_as(dxgi_surface_);
assert(dxgi_surface_);
dxgi_surface_->GetSharedHandle(&shared_handle);
surface_descriptor_.handle = shared_handle;
surface_descriptor_.width = surface_descriptor_.visible_width = width;
surface_descriptor_.height = surface_descriptor_.visible_height = height;
surface_descriptor_.release_context = surface_.get();
surface_descriptor_.release_callback = [](void* release_context) {
auto texture = reinterpret_cast<ID3D11Texture2D*>(release_context);
texture->Release();
};
surface_size_ = {width, height};
}
}
const FlutterDesktopGpuSurfaceDescriptor*
TextureBridgeGpu::GetSurfaceDescriptor(size_t width, size_t height) {
const std::lock_guard<std::mutex> lock(mutex_);
if (!is_running_) {
return nullptr;
}
if (last_frame_) {
ProcessFrame(last_frame_);
}
if (surface_) {
// Gets released in the SurfaceDescriptor's release callback.
surface_->AddRef();
}
return &surface_descriptor_;
}
void TextureBridgeGpu::StopInternal() {
TextureBridge::StopInternal();
// For some reason, the destination surface needs to be recreated upon
// resuming. Force |EnsureSurface| to create a new one by resetting it here.
surface_ = nullptr;
}
@@ -1,26 +0,0 @@
#pragma once
#include <flutter/texture_registrar.h>
#include "texture_bridge.h"
class TextureBridgeGpu : public TextureBridge {
public:
TextureBridgeGpu(GraphicsContext* graphics_context,
ABI::Windows::UI::Composition::IVisual* visual);
const FlutterDesktopGpuSurfaceDescriptor* GetSurfaceDescriptor(size_t width,
size_t height);
protected:
void StopInternal() override;
private:
FlutterDesktopGpuSurfaceDescriptor surface_descriptor_ = {};
Size surface_size_ = {0, 0};
winrt::com_ptr<ID3D11Texture2D> surface_{nullptr};
winrt::com_ptr<IDXGIResource> dxgi_surface_;
void ProcessFrame(winrt::com_ptr<ID3D11Texture2D> src_texture);
void EnsureSurface(uint32_t width, uint32_t height);
};
@@ -1,20 +0,0 @@
#pragma once
#include <windows.ui.composition.interop.h>
namespace util {
winrt::com_ptr<ABI::Windows::UI::Composition::Desktop::IDesktopWindowTarget>
TryCreateDesktopWindowTarget(
const winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor>&
compositor,
HWND window) {
namespace abi = ABI::Windows::UI::Composition::Desktop;
auto interop = compositor.try_as<abi::ICompositorDesktopInterop>();
winrt::com_ptr<abi::IDesktopWindowTarget> target;
interop->CreateDesktopWindowTarget(window, true, target.put());
return target;
}
} // namespace util
@@ -1,80 +0,0 @@
#include "cpuinfo.h"
#include "detail/cpuinfo_impl.h"
#if defined(_MSC_VER) && (defined(__x86_64__) || defined(_M_X64))
#include "detail/init_msvc_x86.h"
#else
#include "detail/init_unknown.hpp"
#endif
namespace cpuid {
cpuinfo::cpuinfo() : impl_(new impl) { init_cpuinfo(*impl_); }
cpuinfo::~cpuinfo() {}
// x86 member functions
bool cpuinfo::has_fpu() const { return impl_->m_has_fpu; }
bool cpuinfo::has_mmx() const { return impl_->m_has_mmx; }
bool cpuinfo::has_sse() const { return impl_->m_has_sse; }
bool cpuinfo::has_sse2() const { return impl_->m_has_sse2; }
bool cpuinfo::has_sse3() const { return impl_->m_has_sse3; }
bool cpuinfo::has_ssse3() const { return impl_->m_has_ssse3; }
bool cpuinfo::has_sse4_1() const { return impl_->m_has_sse4_1; }
bool cpuinfo::has_sse4_2() const { return impl_->m_has_sse4_2; }
bool cpuinfo::has_pclmulqdq() const { return impl_->m_has_pclmulqdq; }
bool cpuinfo::has_avx() const { return impl_->m_has_avx; }
bool cpuinfo::has_avx2() const { return impl_->m_has_avx2; }
bool cpuinfo::has_avx512_f() const { return impl_->m_has_avx512_f; }
bool cpuinfo::has_avx512_dq() const { return impl_->m_has_avx512_dq; }
bool cpuinfo::has_avx512_ifma() const { return impl_->m_has_avx512_ifma; }
bool cpuinfo::has_avx512_pf() const { return impl_->m_has_avx512_pf; }
bool cpuinfo::has_avx512_er() const { return impl_->m_has_avx512_er; }
bool cpuinfo::has_avx512_cd() const { return impl_->m_has_avx512_cd; }
bool cpuinfo::has_avx512_bw() const { return impl_->m_has_avx512_bw; }
bool cpuinfo::has_avx512_vl() const { return impl_->m_has_avx512_vl; }
bool cpuinfo::has_avx512_vbmi() const { return impl_->m_has_avx512_vbmi; }
bool cpuinfo::has_avx512_vbmi2() const { return impl_->m_has_avx512_vbmi2; }
bool cpuinfo::has_avx512_vnni() const { return impl_->m_has_avx512_vnni; }
bool cpuinfo::has_avx512_bitalg() const { return impl_->m_has_avx512_bitalg; }
bool cpuinfo::has_avx512_vpopcntdq() const {
return impl_->m_has_avx512_vpopcntdq;
}
bool cpuinfo::has_avx512_4vnniw() const { return impl_->m_has_avx512_4vnniw; }
bool cpuinfo::has_avx512_4fmaps() const { return impl_->m_has_avx512_4fmaps; }
bool cpuinfo::has_avx512_vp2intersect() const {
return impl_->m_has_avx512_vp2intersect;
}
bool cpuinfo::has_f16c() const { return impl_->m_has_f16c; }
// ARM member functions
bool cpuinfo::has_neon() const { return impl_->m_has_neon; }
} // namespace cpuid
@@ -1,105 +0,0 @@
#pragma once
#include <memory>
namespace cpuid {
class cpuinfo {
public:
struct impl;
cpuinfo();
~cpuinfo();
// Has X87 FPU
bool has_fpu() const;
// Return true if the CPU supports MMX
bool has_mmx() const;
// Return true if the CPU supports SSE
bool has_sse() const;
// Return true if the CPU supports SSE2
bool has_sse2() const;
// Return true if the CPU supports SSE3
bool has_sse3() const;
// Return true if the CPU supports SSSE3
bool has_ssse3() const;
// Return true if the CPU supports SSE 4.1
bool has_sse4_1() const;
// Return true if the CPU supports SSE 4.2
bool has_sse4_2() const;
// Return true if the CPU supports pclmulqdq
bool has_pclmulqdq() const;
// Return true if the CPU supports AVX
bool has_avx() const;
// Return true if the CPU supports AVX2
bool has_avx2() const;
// Return true if the CPU supports AVX512F
bool has_avx512_f() const;
// Return true if the CPU supports AVX512DQ
bool has_avx512_dq() const;
// Return true if the CPU supports AVX512_IFMA
bool has_avx512_ifma() const;
// Return true if the CPU supports AVX512PF
bool has_avx512_pf() const;
// Return true if the CPU supports AVX512ER
bool has_avx512_er() const;
// Return true if the CPU supports AVX512CD
bool has_avx512_cd() const;
// Return true if the CPU supports AVX512BW
bool has_avx512_bw() const;
// Return true if the CPU supports AVX512VL
bool has_avx512_vl() const;
// Return true if the CPU supports AVX512_VBMI
bool has_avx512_vbmi() const;
// Return true if the CPU supports AVX512_VBMI2
bool has_avx512_vbmi2() const;
// Return true if the CPU supports AVX512_VNNI
bool has_avx512_vnni() const;
// Return true if the CPU supports AVX512_BITALG
bool has_avx512_bitalg() const;
// Return true if the CPU supports AVX512_VPOPCNTDQ
bool has_avx512_vpopcntdq() const;
// Return true if the CPU supports AVX512_4VNNIW
bool has_avx512_4vnniw() const;
// Return true if the CPU supports AVX512_4FMAPS
bool has_avx512_4fmaps() const;
// Return true if the CPU supports AVX512_VP2INTERSECT
bool has_avx512_vp2intersect() const;
// Return true if the CPU supports F16C
bool has_f16c() const;
// Return true if the CPU supports NEON
bool has_neon() const;
private:
// Private implementation
std::unique_ptr<impl> impl_;
};
} // namespace cpuid
@@ -1,69 +0,0 @@
#pragma once
#include "../cpuinfo.h"
namespace cpuid {
struct cpuinfo::impl {
impl()
: m_has_fpu(false),
m_has_mmx(false),
m_has_sse(false),
m_has_sse2(false),
m_has_sse3(false),
m_has_ssse3(false),
m_has_sse4_1(false),
m_has_sse4_2(false),
m_has_pclmulqdq(false),
m_has_avx(false),
m_has_avx2(false),
m_has_avx512_f(false),
m_has_avx512_dq(false),
m_has_avx512_ifma(false),
m_has_avx512_pf(false),
m_has_avx512_er(false),
m_has_avx512_cd(false),
m_has_avx512_bw(false),
m_has_avx512_vl(false),
m_has_avx512_vbmi(false),
m_has_avx512_vbmi2(false),
m_has_avx512_vnni(false),
m_has_avx512_bitalg(false),
m_has_avx512_vpopcntdq(false),
m_has_avx512_4vnniw(false),
m_has_avx512_4fmaps(false),
m_has_avx512_vp2intersect(false),
m_has_f16c(false),
m_has_neon(false) {}
bool m_has_fpu;
bool m_has_mmx;
bool m_has_sse;
bool m_has_sse2;
bool m_has_sse3;
bool m_has_ssse3;
bool m_has_sse4_1;
bool m_has_sse4_2;
bool m_has_pclmulqdq;
bool m_has_avx;
bool m_has_avx2;
bool m_has_avx512_f;
bool m_has_avx512_dq;
bool m_has_avx512_ifma;
bool m_has_avx512_pf;
bool m_has_avx512_er;
bool m_has_avx512_cd;
bool m_has_avx512_bw;
bool m_has_avx512_vl;
bool m_has_avx512_vbmi;
bool m_has_avx512_vbmi2;
bool m_has_avx512_vnni;
bool m_has_avx512_bitalg;
bool m_has_avx512_vpopcntdq;
bool m_has_avx512_4vnniw;
bool m_has_avx512_4fmaps;
bool m_has_avx512_vp2intersect;
bool m_has_f16c;
bool m_has_neon;
};
} // namespace cpuid
@@ -1,43 +0,0 @@
#pragma once
#include <cstdint>
#include "cpuinfo_impl.h"
namespace cpuid {
void extract_x86_flags(cpuinfo::impl& info, uint32_t ecx, uint32_t edx) {
info.m_has_fpu = (edx & (1 << 0)) != 0;
info.m_has_mmx = (edx & (1 << 23)) != 0;
info.m_has_sse = (edx & (1 << 25)) != 0;
info.m_has_sse2 = (edx & (1 << 26)) != 0;
info.m_has_sse3 = (ecx & (1 << 0)) != 0;
info.m_has_ssse3 = (ecx & (1 << 9)) != 0;
info.m_has_sse4_1 = (ecx & (1 << 19)) != 0;
info.m_has_sse4_2 = (ecx & (1 << 20)) != 0;
info.m_has_pclmulqdq = (ecx & (1 << 1)) != 0;
info.m_has_avx = (ecx & (1 << 28)) != 0;
info.m_has_f16c = (ecx & (1 << 29)) != 0;
}
void extract_x86_extended_flags(cpuinfo::impl& info, uint32_t ebx, uint32_t ecx,
uint32_t edx) {
info.m_has_avx2 = (ebx & (1 << 5)) != 0;
info.m_has_avx512_f = (ebx & (1 << 16)) != 0;
info.m_has_avx512_dq = (ebx & (1 << 17)) != 0;
info.m_has_avx512_ifma = (ebx & (1 << 21)) != 0;
info.m_has_avx512_pf = (ebx & (1 << 26)) != 0;
info.m_has_avx512_er = (ebx & (1 << 27)) != 0;
info.m_has_avx512_cd = (ebx & (1 << 28)) != 0;
info.m_has_avx512_bw = (ebx & (1 << 30)) != 0;
info.m_has_avx512_vl = (ebx & (1 << 31)) != 0;
info.m_has_avx512_vbmi = (ecx & (1 << 1)) != 0;
info.m_has_avx512_vbmi2 = (ecx & (1 << 6)) != 0;
info.m_has_avx512_vnni = (ecx & (1 << 11)) != 0;
info.m_has_avx512_bitalg = (ecx & (1 << 12)) != 0;
info.m_has_avx512_vpopcntdq = (ecx & (1 << 14)) != 0;
info.m_has_avx512_4vnniw = (edx & (1 << 2)) != 0;
info.m_has_avx512_4fmaps = (edx & (1 << 3)) != 0;
info.m_has_avx512_vp2intersect = (edx & (1 << 8)) != 0;
}
} // namespace cpuid
@@ -1,36 +0,0 @@
#pragma once
#include <intrin.h>
#include "cpuinfo_impl.h"
#include "extract_x86_flags.h"
namespace cpuid {
void init_cpuinfo(cpuinfo::impl& info) {
int registers[4];
// The register information per input can be extracted from here:
// http://en.wikipedia.org/wiki/CPUID
//
// CPUID should be called with EAX=0 first, as this will return the
// maximum supported EAX input value for future calls
__cpuid(registers, 0);
uint32_t maximum_eax = registers[0];
// Set registers for basic flag extraction, eax=1
// All CPUs should support index=1
if (maximum_eax >= 1U) {
__cpuid(registers, 1);
extract_x86_flags(info, registers[2], registers[3]);
}
// Set registers for extended flags extraction, eax=7 and ecx=0
// This operation is not supported on older CPUs, so it should be skipped
// to avoid incorrect results
if (maximum_eax >= 7U) {
__cpuidex(registers, 7, 0);
extract_x86_extended_flags(info, registers[1], registers[2], registers[3]);
}
}
} // namespace cpuid
@@ -1,9 +0,0 @@
#pragma once
#include "cpuinfo_impl.h"
namespace cpuid {
void init_cpuinfo(cpuinfo::impl& info) { (void)info; }
} // namespace cpuid
@@ -1,31 +0,0 @@
#pragma once
#include <D3d11.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.System.h>
inline auto CreateD3DDevice(D3D_DRIVER_TYPE const type,
winrt::com_ptr<ID3D11Device>& device) {
WINRT_ASSERT(!device);
UINT flags =
D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
//#ifdef _DEBUG
// flags |= D3D11_CREATE_DEVICE_DEBUG;
//#endif
return D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
D3D11_SDK_VERSION, device.put(), nullptr, nullptr);
}
inline auto CreateD3DDevice() {
winrt::com_ptr<ID3D11Device> device;
HRESULT hr = CreateD3DDevice(D3D_DRIVER_TYPE_HARDWARE, device);
if (DXGI_ERROR_UNSUPPORTED == hr) {
CreateD3DDevice(D3D_DRIVER_TYPE_WARP, device);
}
return device;
}
@@ -1,43 +0,0 @@
#include "direct3d11.interop.h"
namespace util {
namespace {
typedef HRESULT(WINAPI* CreateDirect3D11DeviceFromDXGIDeviceFn)(IDXGIDevice*,
LPVOID*);
struct D3DFuncs {
CreateDirect3D11DeviceFromDXGIDeviceFn CreateDirect3D11DeviceFromDXGIDevice =
nullptr;
D3DFuncs() {
auto handle = GetModuleHandle(L"d3d11.dll");
if (!handle) {
return;
}
CreateDirect3D11DeviceFromDXGIDevice =
reinterpret_cast<CreateDirect3D11DeviceFromDXGIDeviceFn>(
GetProcAddress(handle, "CreateDirect3D11DeviceFromDXGIDevice"));
}
static const D3DFuncs& instance() {
static D3DFuncs funcs;
return funcs;
}
};
} // namespace
HRESULT CreateDirect3D11DeviceFromDXGIDevice(IDXGIDevice* dxgiDevice,
IInspectable** graphicsDevice) {
auto ptr = D3DFuncs::instance().CreateDirect3D11DeviceFromDXGIDevice;
if (ptr) {
return ptr(dxgiDevice, reinterpret_cast<LPVOID*>(graphicsDevice));
}
return E_NOTIMPL;
}
} // namespace util
@@ -1,49 +0,0 @@
#pragma once
#include <inspectable.h>
#include <windows.foundation.h>
#include <winrt/windows.graphics.directx.direct3d11.h>
#include "dxgi.h"
namespace Windows {
namespace Graphics {
namespace DirectX {
namespace Direct3D11 {
struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
IDirect3DDxgiInterfaceAccess : ::IUnknown {
virtual HRESULT __stdcall GetInterface(GUID const& id, void** object) = 0;
};
} // namespace Direct3D11
} // namespace DirectX
} // namespace Graphics
} // namespace Windows
namespace util {
HRESULT CreateDirect3D11DeviceFromDXGIDevice(IDXGIDevice* dxgiDevice,
IInspectable** graphicsDevice);
template <typename T>
auto GetDXGIInterfaceFromObject(
winrt::Windows::Foundation::IInspectable const& object) {
auto access = object.as<
Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>();
winrt::com_ptr<T> result;
winrt::check_hresult(
access->GetInterface(winrt::guid_of<T>(), result.put_void()));
return result;
}
template <typename T>
auto TryGetDXGIInterfaceFromObject(const winrt::com_ptr<IInspectable>& object) {
auto access = object.try_as<
Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>();
winrt::com_ptr<T> result;
access->GetInterface(winrt::guid_of<T>(), result.put_void());
return result;
}
} // namespace util
@@ -1,244 +0,0 @@
// Based on ANGLE's RoHelper (CompositorNativeWindow11.{cpp,h})
// - https://github.com/google/angle/blob/main/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.h
// - https://github.com/google/angle/blob/main/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.cpp
// - https://gist.github.com/clarkezone/43e984fb9bdcd2cfcd9a4f41c208a02f
//
// Copyright 2018 The ANGLE Project Authors.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of TransGaming Inc., Google Inc., 3DLabs Inc.
// Ltd., nor the names of their contributors may be used to endorse
// or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include "rohelper.h"
#include <windows.foundation.metadata.h>
#include <wrl.h>
namespace rx {
template <typename T>
bool AssignProcAddress(HMODULE comBaseModule, const char* name, T*& outProc) {
outProc = reinterpret_cast<T*>(GetProcAddress(comBaseModule, name));
return *outProc != nullptr;
}
RoHelper::RoHelper(RO_INIT_TYPE init_type)
: mFpWindowsCreateStringReference(nullptr),
mFpGetActivationFactory(nullptr),
mFpWindowsCompareStringOrdinal(nullptr),
mFpCreateDispatcherQueueController(nullptr),
mFpWindowsDeleteString(nullptr),
mFpRoInitialize(nullptr),
mFpRoUninitialize(nullptr),
mWinRtAvailable(false),
mComBaseModule(nullptr),
mCoreMessagingModule(nullptr) {
#ifdef WINUWP
mFpWindowsCreateStringReference = &::WindowsCreateStringReference;
mFpRoInitialize = &::RoInitialize;
mFpRoUninitialize = &::RoUninitialize;
mFpWindowsDeleteString = &::WindowsDeleteString;
mFpGetActivationFactory = &::RoGetActivationFactory;
mFpWindowsCompareStringOrdinal = &::WindowsCompareStringOrdinal;
mFpCreateDispatcherQueueController = &::CreateDispatcherQueueController;
mWinRtAvailable = true;
#else
mComBaseModule = LoadLibraryA("ComBase.dll");
if (mComBaseModule == nullptr) {
return;
}
if (!AssignProcAddress(mComBaseModule, "WindowsCreateStringReference",
mFpWindowsCreateStringReference)) {
return;
}
if (!AssignProcAddress(mComBaseModule, "RoGetActivationFactory",
mFpGetActivationFactory)) {
return;
}
if (!AssignProcAddress(mComBaseModule, "WindowsCompareStringOrdinal",
mFpWindowsCompareStringOrdinal)) {
return;
}
if (!AssignProcAddress(mComBaseModule, "WindowsDeleteString",
mFpWindowsDeleteString)) {
return;
}
if (!AssignProcAddress(mComBaseModule, "RoInitialize", mFpRoInitialize)) {
return;
}
if (!AssignProcAddress(mComBaseModule, "RoUninitialize", mFpRoUninitialize)) {
return;
}
mCoreMessagingModule = LoadLibraryA("coremessaging.dll");
if (mCoreMessagingModule == nullptr) {
return;
}
if (!AssignProcAddress(mCoreMessagingModule,
"CreateDispatcherQueueController",
mFpCreateDispatcherQueueController)) {
return;
}
auto result = RoInitialize(init_type);
if (SUCCEEDED(result) || result == S_FALSE || result == RPC_E_CHANGED_MODE) {
mWinRtAvailable = true;
}
#endif
}
RoHelper::~RoHelper() {
#ifndef WINUWP
if (mWinRtAvailable) {
RoUninitialize();
}
if (mCoreMessagingModule != nullptr) {
FreeLibrary(mCoreMessagingModule);
mCoreMessagingModule = nullptr;
}
if (mComBaseModule != nullptr) {
FreeLibrary(mComBaseModule);
mComBaseModule = nullptr;
}
#endif
}
bool RoHelper::WinRtAvailable() const { return mWinRtAvailable; }
bool RoHelper::SupportedWindowsRelease() {
if (!mWinRtAvailable) {
return false;
}
HSTRING className, contractName;
HSTRING_HEADER classNameHeader, contractNameHeader;
boolean isSupported = false;
HRESULT hr = GetStringReference(
RuntimeClass_Windows_Foundation_Metadata_ApiInformation, &className,
&classNameHeader);
if (FAILED(hr)) {
return !!isSupported;
}
Microsoft::WRL::ComPtr<
ABI::Windows::Foundation::Metadata::IApiInformationStatics>
api;
hr = GetActivationFactory(
className,
__uuidof(ABI::Windows::Foundation::Metadata::IApiInformationStatics),
&api);
if (FAILED(hr)) {
return !!isSupported;
}
hr = GetStringReference(L"Windows.Foundation.UniversalApiContract",
&contractName, &contractNameHeader);
if (FAILED(hr)) {
return !!isSupported;
}
api->IsApiContractPresentByMajor(contractName, 6, &isSupported);
return !!isSupported;
}
HRESULT RoHelper::GetStringReference(PCWSTR source, HSTRING* act,
HSTRING_HEADER* header) {
if (!mWinRtAvailable) {
return E_FAIL;
}
const wchar_t* str = static_cast<const wchar_t*>(source);
unsigned int length;
HRESULT hr = SizeTToUInt32(::wcslen(str), &length);
if (FAILED(hr)) {
return hr;
}
return mFpWindowsCreateStringReference(source, length, header, act);
}
HRESULT RoHelper::GetActivationFactory(const HSTRING act,
const IID& interfaceId, void** fac) {
if (!mWinRtAvailable) {
return E_FAIL;
}
auto hr = mFpGetActivationFactory(act, interfaceId, fac);
return hr;
}
HRESULT RoHelper::WindowsCompareStringOrdinal(HSTRING one, HSTRING two,
int* result) {
if (!mWinRtAvailable) {
return E_FAIL;
}
return mFpWindowsCompareStringOrdinal(one, two, result);
}
HRESULT RoHelper::CreateDispatcherQueueController(
DispatcherQueueOptions options,
ABI::Windows::System::IDispatcherQueueController**
dispatcherQueueController) {
if (!mWinRtAvailable) {
return E_FAIL;
}
return mFpCreateDispatcherQueueController(options, dispatcherQueueController);
}
HRESULT RoHelper::WindowsDeleteString(HSTRING one) {
if (!mWinRtAvailable) {
return E_FAIL;
}
return mFpWindowsDeleteString(one);
}
HRESULT RoHelper::RoInitialize(RO_INIT_TYPE type) {
return mFpRoInitialize(type);
}
void RoHelper::RoUninitialize() { mFpRoUninitialize(); }
} // namespace rx
@@ -1,97 +0,0 @@
// Based on ANGLE's RoHelper (CompositorNativeWindow11.{cpp,h})
// - https://github.com/google/angle/blob/main/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.h
// - https://github.com/google/angle/blob/main/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.cpp
// - https://gist.github.com/clarkezone/43e984fb9bdcd2cfcd9a4f41c208a02f
//
// Copyright 2018 The ANGLE Project Authors.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of TransGaming Inc., Google Inc., 3DLabs Inc.
// Ltd., nor the names of their contributors may be used to endorse
// or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <dispatcherqueue.h>
#include <roapi.h>
#include <windows.ui.composition.interop.h>
namespace rx {
class RoHelper {
public:
RoHelper(RO_INIT_TYPE init_type);
~RoHelper();
bool WinRtAvailable() const;
bool SupportedWindowsRelease();
HRESULT GetStringReference(PCWSTR source, HSTRING* act,
HSTRING_HEADER* header);
HRESULT GetActivationFactory(const HSTRING act, const IID& interfaceId,
void** fac);
HRESULT WindowsCompareStringOrdinal(HSTRING one, HSTRING two, int* result);
HRESULT CreateDispatcherQueueController(
DispatcherQueueOptions options,
ABI::Windows::System::IDispatcherQueueController**
dispatcherQueueController);
HRESULT WindowsDeleteString(HSTRING one);
HRESULT RoInitialize(RO_INIT_TYPE type);
void RoUninitialize();
private:
using WindowsCreateStringReference_ = HRESULT __stdcall(PCWSTR, UINT32,
HSTRING_HEADER*,
HSTRING*);
using GetActivationFactory_ = HRESULT __stdcall(HSTRING, REFIID, void**);
using WindowsCompareStringOrginal_ = HRESULT __stdcall(HSTRING, HSTRING,
int*);
using WindowsDeleteString_ = HRESULT __stdcall(HSTRING);
using CreateDispatcherQueueController_ =
HRESULT __stdcall(DispatcherQueueOptions,
ABI::Windows::System::IDispatcherQueueController**);
using RoInitialize_ = HRESULT __stdcall(RO_INIT_TYPE);
using RoUninitialize_ = void __stdcall();
WindowsCreateStringReference_* mFpWindowsCreateStringReference;
GetActivationFactory_* mFpGetActivationFactory;
WindowsCompareStringOrginal_* mFpWindowsCompareStringOrdinal;
CreateDispatcherQueueController_* mFpCreateDispatcherQueueController;
WindowsDeleteString_* mFpWindowsDeleteString;
RoInitialize_* mFpRoInitialize;
RoUninitialize_* mFpRoUninitialize;
bool mWinRtAvailable;
HMODULE mComBaseModule;
HMODULE mCoreMessagingModule;
};
} // namespace rx
@@ -1,54 +0,0 @@
#include "string_converter.h"
#include <windows.h>
namespace util {
std::string Utf8FromUtf16(std::wstring_view utf16_string) {
if (utf16_string.empty()) {
return std::string();
}
auto src_length = static_cast<int>(utf16_string.size());
int target_length =
::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(),
src_length, nullptr, 0, nullptr, nullptr);
std::string utf8_string;
if (target_length <= 0 || target_length > utf8_string.max_size()) {
return utf8_string;
}
utf8_string.resize(target_length);
int converted_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(), src_length,
utf8_string.data(), target_length, nullptr, nullptr);
if (converted_length == 0) {
return std::string();
}
return utf8_string;
}
std::wstring Utf16FromUtf8(std::string_view utf8_string) {
if (utf8_string.empty()) {
return std::wstring();
}
auto src_length = static_cast<int>(utf8_string.size());
int target_length =
::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(),
src_length, nullptr, 0);
std::wstring utf16_string;
if (target_length <= 0 || target_length > utf16_string.max_size()) {
return utf16_string;
}
utf16_string.resize(target_length);
int converted_length =
::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(),
src_length, utf16_string.data(), target_length);
if (converted_length == 0) {
return std::wstring();
}
return utf16_string;
}
} // namespace util
@@ -1,8 +0,0 @@
#pragma once
#include <string>
namespace util {
std::string Utf8FromUtf16(std::wstring_view utf16_string);
std::wstring Utf16FromUtf8(std::string_view utf8_string);
} // namespace util
@@ -1,192 +0,0 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/*
* see skia/src/opts/SkSwizzler_opts.h
*/
#pragma once
#include "cpuid/cpuinfo.h"
/**
* SK_CPU_SSE_LEVEL
*
* If defined, SK_CPU_SSE_LEVEL should be set to the highest supported level.
* On non-intel CPU this should be undefined.
*/
#define SK_CPU_SSE_LEVEL_SSE1 10
#define SK_CPU_SSE_LEVEL_SSE2 20
#define SK_CPU_SSE_LEVEL_SSE3 30
#define SK_CPU_SSE_LEVEL_SSSE3 31
#define SK_CPU_SSE_LEVEL_SSE41 41
#define SK_CPU_SSE_LEVEL_SSE42 42
#define SK_CPU_SSE_LEVEL_AVX 51
#define SK_CPU_SSE_LEVEL_AVX2 52
#define SK_CPU_SSE_LEVEL_SKX 60
// Are we in GCC/Clang?
#ifndef SK_CPU_SSE_LEVEL
// These checks must be done in descending order to ensure we set the highest
// available SSE level.
#if defined(__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512CD__) && \
defined(__AVX512BW__) && defined(__AVX512VL__)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SKX
#elif defined(__AVX2__)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX2
#elif defined(__AVX__)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX
#elif defined(__SSE4_2__)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE42
#elif defined(__SSE4_1__)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE41
#elif defined(__SSSE3__)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSSE3
#elif defined(__SSE3__)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE3
#elif defined(__SSE2__)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2
#endif
#endif
// Are we in VisualStudio?
#ifndef SK_CPU_SSE_LEVEL
// These checks must be done in descending order to ensure we set the highest
// available SSE level. 64-bit intel guarantees at least SSE2 support.
#if defined(__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512CD__) && \
defined(__AVX512BW__) && defined(__AVX512VL__)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SKX
#elif defined(__AVX2__)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX2
#elif defined(__AVX__)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX
#elif defined(_M_X64) || defined(_M_AMD64)
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2
#elif defined(_M_IX86_FP)
#if _M_IX86_FP >= 2
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2
#elif _M_IX86_FP == 1
#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE1
#endif
#endif
#endif
inline void RGBA_to_BGRA_portable(uint32_t* dst, const uint32_t* src,
int height, int src_stride, int dst_stride) {
auto width = std::min<int>(src_stride, dst_stride);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
uint8_t a = (src[x] >> 24) & 0xFF, b = (src[x] >> 16) & 0xFF,
g = (src[x] >> 8) & 0xFF, r = (src[x] >> 0) & 0xFF;
dst[x] = (uint32_t)a << 24 | (uint32_t)r << 16 | (uint32_t)g << 8 |
(uint32_t)b << 0;
}
src += src_stride;
dst += dst_stride;
}
}
#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SKX
inline void RGBA_to_BGRA_SKX(uint32_t* dst, const uint32_t* src, int height,
int src_stride, int dst_stride) {
const uint8_t mask[64] = {2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14,
13, 12, 15, 2, 1, 0, 3, 6, 5, 4, 7, 10, 9,
8, 11, 14, 13, 12, 15, 2, 1, 0, 3, 6, 5, 4,
7, 10, 9, 8, 11, 14, 13, 12, 15, 2, 1, 0, 3,
6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15};
const __m512i swapRB = _mm512_loadu_si512(mask);
auto width = std::min<int>(src_stride, dst_stride);
for (int y = 0; y < height; y++) {
auto cw = width;
auto rptr = src;
auto dptr = dst;
while (cw >= 16) {
__m512i rgba = _mm512_loadu_si512((const __m512i*)rptr);
__m512i bgra = _mm512_shuffle_epi8(rgba, swapRB);
_mm512_storeu_si512((__m512i*)dptr, bgra);
rptr += 16;
dptr += 16;
cw -= 16;
}
for (auto x = 0; x < cw; x++) {
uint8_t a = (rptr[x] >> 24) & 0xFF, b = (rptr[x] >> 16) & 0xFF,
g = (rptr[x] >> 8) & 0xFF, r = (rptr[x] >> 0) & 0xFF;
dptr[x] = (uint32_t)a << 24 | (uint32_t)r << 16 | (uint32_t)g << 8 |
(uint32_t)b << 0;
}
src += src_stride;
dst += dst_stride;
}
}
#endif
#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2
inline void RGBA_to_BGRA_AVX2(uint32_t* dst, const uint32_t* src, int height,
int src_stride, int dst_stride) {
const __m256i swapRB =
_mm256_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15, 2,
1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15);
auto width = std::min<int>(src_stride, dst_stride);
for (int y = 0; y < height; y++) {
auto cw = width;
auto rptr = src;
auto dptr = dst;
while (cw >= 8) {
__m256i rgba = _mm256_loadu_si256((const __m256i*)rptr);
__m256i bgra = _mm256_shuffle_epi8(rgba, swapRB);
_mm256_storeu_si256((__m256i*)dptr, bgra);
rptr += 8;
dptr += 8;
cw -= 8;
}
for (auto x = 0; x < cw; x++) {
uint8_t a = (rptr[x] >> 24) & 0xFF, b = (rptr[x] >> 16) & 0xFF,
g = (rptr[x] >> 8) & 0xFF, r = (rptr[x] >> 0) & 0xFF;
dptr[x] = (uint32_t)a << 24 | (uint32_t)r << 16 | (uint32_t)g << 8 |
(uint32_t)b << 0;
}
src += src_stride;
dst += dst_stride;
}
}
#endif
inline void RGBA_to_BGRA(uint32_t* dst, const uint32_t* src, int height,
int src_stride, int dst_stride) {
static cpuid::cpuinfo info;
#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SKX
if (info.has_avx512_f() && info.has_avx512_dq() && info.has_avx512_cd() &&
info.has_avx512_bw() && info.has_avx512_vl()) {
return RGBA_to_BGRA_SKX(dst, src, height, src_stride, dst_stride);
}
#endif
#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2
if (info.has_avx2()) {
return RGBA_to_BGRA_AVX2(dst, src, height, src_stride, dst_stride);
}
#endif
RGBA_to_BGRA_portable(dst, src, height, src_stride, dst_stride);
}
-802
View File
@@ -1,802 +0,0 @@
#include "webview.h"
#include <wrl.h>
#include <format>
#include <iostream>
#include "util/composition.desktop.interop.h"
#include "util/string_converter.h"
#include "webview_host.h"
using namespace Microsoft::WRL;
namespace {
inline void ConvertColor(COREWEBVIEW2_COLOR& webview_color, int32_t color) {
webview_color.B = color & 0xFF;
webview_color.G = (color >> 8) & 0xFF;
webview_color.R = (color >> 16) & 0xFF;
webview_color.A = (color >> 24) & 0xFF;
}
inline WebviewPermissionKind CW2PermissionKindToPermissionKind(
COREWEBVIEW2_PERMISSION_KIND kind) {
using k = COREWEBVIEW2_PERMISSION_KIND;
switch (kind) {
case k::COREWEBVIEW2_PERMISSION_KIND_MICROPHONE:
return WebviewPermissionKind::Microphone;
case k::COREWEBVIEW2_PERMISSION_KIND_CAMERA:
return WebviewPermissionKind::Camera;
case k::COREWEBVIEW2_PERMISSION_KIND_GEOLOCATION:
return WebviewPermissionKind::GeoLocation;
case k::COREWEBVIEW2_PERMISSION_KIND_NOTIFICATIONS:
return WebviewPermissionKind::Notifications;
case k::COREWEBVIEW2_PERMISSION_KIND_OTHER_SENSORS:
return WebviewPermissionKind::OtherSensors;
case k::COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ:
return WebviewPermissionKind::ClipboardRead;
default:
return WebviewPermissionKind::Unknown;
}
}
inline COREWEBVIEW2_PERMISSION_STATE WebViewPermissionStateToCW2PermissionState(
WebviewPermissionState state) {
using s = COREWEBVIEW2_PERMISSION_STATE;
switch (state) {
case WebviewPermissionState::Allow:
return s::COREWEBVIEW2_PERMISSION_STATE_ALLOW;
case WebviewPermissionState::Deny:
return s::COREWEBVIEW2_PERMISSION_STATE_DENY;
default:
return s::COREWEBVIEW2_PERMISSION_STATE_DEFAULT;
}
}
} // namespace
Webview::Webview(
wil::com_ptr<ICoreWebView2CompositionController> composition_controller,
WebviewHost* host, HWND hwnd, bool owns_window, bool offscreen_only)
: composition_controller_(std::move(composition_controller)),
host_(host),
hwnd_(hwnd),
owns_window_(owns_window) {
webview_controller_ =
composition_controller_.try_query<ICoreWebView2Controller3>();
if (!webview_controller_ ||
FAILED(webview_controller_->get_CoreWebView2(webview_.put()))) {
return;
}
webview_controller_->put_BoundsMode(COREWEBVIEW2_BOUNDS_MODE_USE_RAW_PIXELS);
webview_controller_->put_ShouldDetectMonitorScaleChanges(FALSE);
webview_controller_->put_RasterizationScale(1.0);
wil::com_ptr<ICoreWebView2Settings> settings;
if (SUCCEEDED(webview_->get_Settings(settings.put()))) {
settings2_ = settings.try_query<ICoreWebView2Settings2>();
auto settings3 = settings.try_query<ICoreWebView2Settings3>();
settings->put_IsStatusBarEnabled(FALSE);
settings->put_AreDevToolsEnabled(FALSE);
settings->put_AreDefaultContextMenusEnabled(FALSE);
if (settings3) {
settings3->put_AreBrowserAcceleratorKeysEnabled(FALSE);
}
}
EnableSecurityUpdates();
RegisterEventHandlers();
is_valid_ = CreateSurface(host->compositor(), hwnd, offscreen_only);
}
Webview::~Webview() {
if (owns_window_) {
DestroyWindow(hwnd_);
}
}
bool Webview::CreateSurface(
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor> compositor,
HWND hwnd, bool offscreen_only) {
winrt::com_ptr<ABI::Windows::UI::Composition::IContainerVisual> root;
if (FAILED(compositor->CreateContainerVisual(root.put()))) {
return false;
}
surface_ = root.try_as<ABI::Windows::UI::Composition::IVisual>();
assert(surface_);
// initial size. doesn't matter as we resize the surface anyway.
surface_->put_Size({1280, 720});
surface_->put_IsVisible(true);
// Create on-screen window for debugging purposes
if (!offscreen_only) {
window_target_ = util::TryCreateDesktopWindowTarget(compositor, hwnd);
auto composition_target =
window_target_
.try_as<ABI::Windows::UI::Composition::ICompositionTarget>();
if (composition_target) {
composition_target->put_Root(surface_.get());
}
}
winrt::com_ptr<ABI::Windows::UI::Composition::IVisual> webview_visual;
compositor->CreateContainerVisual(
reinterpret_cast<ABI::Windows::UI::Composition::IContainerVisual**>(
webview_visual.put()));
auto webview_visual2 =
webview_visual.try_as<ABI::Windows::UI::Composition::IVisual2>();
if (webview_visual2) {
webview_visual2->put_RelativeSizeAdjustment({1.0f, 1.0f});
}
winrt::com_ptr<ABI::Windows::UI::Composition::IVisualCollection> children;
root->get_Children(children.put());
children->InsertAtTop(webview_visual.get());
composition_controller_->put_RootVisualTarget(webview_visual2.get());
webview_controller_->put_IsVisible(true);
return true;
}
void Webview::EnableSecurityUpdates() {
if (SUCCEEDED(webview_->CallDevToolsProtocolMethod(L"Security.enable", L"{}",
nullptr)) &&
SUCCEEDED(webview_->GetDevToolsProtocolEventReceiver(
L"Security.securityStateChanged",
&devtools_protocol_event_receiver_))) {
devtools_protocol_event_receiver_->add_DevToolsProtocolEventReceived(
Callback<ICoreWebView2DevToolsProtocolEventReceivedEventHandler>(
[this](ICoreWebView2* sender,
ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args)
-> HRESULT {
if (devtools_protocol_event_callback_) {
wil::unique_cotaskmem_string json_args;
if (args->get_ParameterObjectAsJson(&json_args) == S_OK) {
std::string json = util::Utf8FromUtf16(json_args.get());
devtools_protocol_event_callback_(json.c_str());
}
}
return S_OK;
})
.Get(),
&event_registrations_.devtools_protocol_event_token_);
}
}
void Webview::RegisterEventHandlers() {
if (!webview_) {
return;
}
webview_->add_ContentLoading(
Callback<ICoreWebView2ContentLoadingEventHandler>(
[this](ICoreWebView2* sender, IUnknown* args) -> HRESULT {
if (loading_state_changed_callback_) {
loading_state_changed_callback_(WebviewLoadingState::Loading);
}
return S_OK;
})
.Get(),
&event_registrations_.content_loading_token_);
webview_->add_NavigationCompleted(
Callback<ICoreWebView2NavigationCompletedEventHandler>(
[this](ICoreWebView2* sender,
ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT {
BOOL is_success;
args->get_IsSuccess(&is_success);
if (!is_success && on_load_error_callback_) {
COREWEBVIEW2_WEB_ERROR_STATUS web_error_status;
args->get_WebErrorStatus(&web_error_status);
on_load_error_callback_(web_error_status);
}
if (loading_state_changed_callback_) {
loading_state_changed_callback_(
WebviewLoadingState::NavigationCompleted);
}
return S_OK;
})
.Get(),
&event_registrations_.navigation_completed_token_);
webview_->add_HistoryChanged(
Callback<ICoreWebView2HistoryChangedEventHandler>(
[this](ICoreWebView2* sender, IUnknown* args) -> HRESULT {
if (history_changed_callback_) {
BOOL can_go_back;
BOOL can_go_forward;
sender->get_CanGoBack(&can_go_back);
sender->get_CanGoForward(&can_go_forward);
history_changed_callback_({can_go_back, can_go_forward});
}
return S_OK;
})
.Get(),
&event_registrations_.history_changed_token_);
webview_->add_SourceChanged(
Callback<ICoreWebView2SourceChangedEventHandler>(
[this](ICoreWebView2* sender, IUnknown* args) -> HRESULT {
LPWSTR wurl;
if (url_changed_callback_ && webview_->get_Source(&wurl) == S_OK) {
std::string url = util::Utf8FromUtf16(wurl);
url_changed_callback_(url);
}
return S_OK;
})
.Get(),
&event_registrations_.source_changed_token_);
webview_->add_DocumentTitleChanged(
Callback<ICoreWebView2DocumentTitleChangedEventHandler>(
[this](ICoreWebView2* sender, IUnknown* args) -> HRESULT {
LPWSTR wtitle;
if (document_title_changed_callback_ &&
webview_->get_DocumentTitle(&wtitle) == S_OK) {
std::string title = util::Utf8FromUtf16(wtitle);
document_title_changed_callback_(title);
}
return S_OK;
})
.Get(),
&event_registrations_.document_title_changed_token_);
composition_controller_->add_CursorChanged(
Callback<ICoreWebView2CursorChangedEventHandler>(
[this](ICoreWebView2CompositionController* sender,
IUnknown* args) -> HRESULT {
HCURSOR cursor;
if (cursor_changed_callback_ &&
sender->get_Cursor(&cursor) == S_OK) {
cursor_changed_callback_(cursor);
}
return S_OK;
})
.Get(),
&event_registrations_.cursor_changed_token_);
webview_controller_->add_GotFocus(
Callback<ICoreWebView2FocusChangedEventHandler>(
[this](ICoreWebView2Controller* sender, IUnknown* args) -> HRESULT {
if (focus_changed_callback_) {
focus_changed_callback_(true);
}
return S_OK;
})
.Get(),
&event_registrations_.got_focus_token_);
webview_controller_->add_LostFocus(
Callback<ICoreWebView2FocusChangedEventHandler>(
[this](ICoreWebView2Controller* sender, IUnknown* args) -> HRESULT {
if (focus_changed_callback_) {
focus_changed_callback_(false);
}
return S_OK;
})
.Get(),
&event_registrations_.lost_focus_token_);
webview_->add_WebMessageReceived(
Callback<ICoreWebView2WebMessageReceivedEventHandler>(
[this](ICoreWebView2* sender,
ICoreWebView2WebMessageReceivedEventArgs* args) -> HRESULT {
wil::unique_cotaskmem_string wmessage;
if (web_message_received_callback_ &&
args->get_WebMessageAsJson(&wmessage) == S_OK) {
const std::string message = util::Utf8FromUtf16(wmessage.get());
web_message_received_callback_(message);
}
return S_OK;
})
.Get(),
&event_registrations_.web_message_received_token_);
webview_->add_PermissionRequested(
Callback<ICoreWebView2PermissionRequestedEventHandler>(
[this](ICoreWebView2* sender,
ICoreWebView2PermissionRequestedEventArgs* args) -> HRESULT {
if (!permission_requested_callback_) {
return S_OK;
}
wil::unique_cotaskmem_string wuri;
COREWEBVIEW2_PERMISSION_KIND kind =
COREWEBVIEW2_PERMISSION_KIND_UNKNOWN_PERMISSION;
BOOL is_user_initiated = false;
if (args->get_Uri(&wuri) == S_OK &&
args->get_PermissionKind(&kind) == S_OK &&
args->get_IsUserInitiated(&is_user_initiated) == S_OK) {
wil::com_ptr<ICoreWebView2Deferral> deferral;
args->GetDeferral(deferral.put());
const std::string uri = util::Utf8FromUtf16(wuri.get());
permission_requested_callback_(
uri, CW2PermissionKindToPermissionKind(kind),
is_user_initiated == TRUE,
[deferral = std::move(deferral),
args = std::move(args)](WebviewPermissionState state) {
args->put_State(
WebViewPermissionStateToCW2PermissionState(state));
deferral->Complete();
});
}
return S_OK;
})
.Get(),
&event_registrations_.permission_requested_token_);
webview_->add_NewWindowRequested(
Callback<ICoreWebView2NewWindowRequestedEventHandler>(
[this](ICoreWebView2* sender,
ICoreWebView2NewWindowRequestedEventArgs* args) -> HRESULT {
switch (popup_window_policy_) {
case WebviewPopupWindowPolicy::Deny:
args->put_Handled(TRUE);
break;
case WebviewPopupWindowPolicy::ShowInSameWindow:
args->put_NewWindow(webview_.get());
args->put_Handled(TRUE);
break;
}
return S_OK;
})
.Get(),
&event_registrations_.new_windows_requested_token_);
webview_->add_ContainsFullScreenElementChanged(
Callback<ICoreWebView2ContainsFullScreenElementChangedEventHandler>(
[this](ICoreWebView2* sender, IUnknown* args) -> HRESULT {
BOOL flag = FALSE;
if (contains_fullscreen_element_changed_callback_ &&
SUCCEEDED(sender->get_ContainsFullScreenElement(&flag))) {
contains_fullscreen_element_changed_callback_(flag);
}
return S_OK;
})
.Get(),
&event_registrations_.contains_fullscreen_element_changed_token_);
}
void Webview::SetSurfaceSize(size_t width, size_t height, float scale_factor) {
if (!IsValid()) {
return;
}
if (surface_ && width > 0 && height > 0) {
scale_factor_ = scale_factor;
auto scaled_width = width * scale_factor;
auto scaled_height = height * scale_factor;
RECT bounds;
bounds.left = 0;
bounds.top = 0;
bounds.right = static_cast<LONG>(scaled_width);
bounds.bottom = static_cast<LONG>(scaled_height);
surface_->put_Size({scaled_width, scaled_height});
webview_controller_->put_RasterizationScale(scale_factor);
if (webview_controller_->put_Bounds(bounds) != S_OK) {
std::cerr << "Setting webview bounds failed." << std::endl;
}
if (surface_size_changed_callback_) {
surface_size_changed_callback_(width, height);
}
}
}
bool Webview::OpenDevTools() {
if (!IsValid()) {
return false;
}
webview_->OpenDevToolsWindow();
return true;
}
bool Webview::ClearCookies() {
if (!IsValid()) {
return false;
}
return webview_->CallDevToolsProtocolMethod(L"Network.clearBrowserCookies",
L"{}", nullptr) == S_OK;
}
bool Webview::ClearCache() {
if (!IsValid()) {
return false;
}
return webview_->CallDevToolsProtocolMethod(L"Network.clearBrowserCache",
L"{}", nullptr) == S_OK;
}
bool Webview::SetCacheDisabled(bool disabled) {
if (!IsValid()) {
return false;
}
std::string json = std::format("{{\"disableCache\":{}}}", disabled);
return webview_->CallDevToolsProtocolMethod(L"Network.setCacheDisabled",
util::Utf16FromUtf8(json).c_str(),
nullptr) == S_OK;
}
void Webview::SetPopupWindowPolicy(WebviewPopupWindowPolicy policy) {
popup_window_policy_ = policy;
}
bool Webview::SetUserAgent(const std::string& user_agent) {
if (settings2_) {
return settings2_->put_UserAgent(util::Utf16FromUtf8(user_agent).c_str()) ==
S_OK;
}
return false;
}
bool Webview::SetBackgroundColor(int32_t color) {
if (!IsValid()) {
return false;
}
COREWEBVIEW2_COLOR webview_color;
ConvertColor(webview_color, color);
// Semi-transparent backgrounds are not supported.
// Valid alpha values are 0 or 255.
if (webview_color.A > 0) {
webview_color.A = 0xFF;
}
return webview_controller_->put_DefaultBackgroundColor(webview_color) == S_OK;
}
bool Webview::SetZoomFactor(double factor) {
if (!IsValid()) {
return false;
}
return webview_controller_->put_ZoomFactor(factor) == S_OK;
}
void Webview::SetCursorPos(double x, double y) {
if (!IsValid()) {
return;
}
POINT point;
point.x = static_cast<LONG>(x * scale_factor_);
point.y = static_cast<LONG>(y * scale_factor_);
last_cursor_pos_ = point;
// https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.774.44
composition_controller_->SendMouseInput(
COREWEBVIEW2_MOUSE_EVENT_KIND::COREWEBVIEW2_MOUSE_EVENT_KIND_MOVE,
virtual_keys_.state(), 0, point);
}
void Webview::SetPointerUpdate(int32_t pointer,
WebviewPointerEventKind eventKind, double x,
double y, double size, double pressure) {
if (!IsValid()) {
return;
}
COREWEBVIEW2_POINTER_EVENT_KIND event =
COREWEBVIEW2_POINTER_EVENT_KIND_UPDATE;
UINT32 pointerFlags = POINTER_FLAG_NONE;
switch (eventKind) {
case WebviewPointerEventKind::Activate:
event = COREWEBVIEW2_POINTER_EVENT_KIND_ACTIVATE;
break;
case WebviewPointerEventKind::Down:
event = COREWEBVIEW2_POINTER_EVENT_KIND_DOWN;
pointerFlags =
POINTER_FLAG_DOWN | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT;
break;
case WebviewPointerEventKind::Enter:
event = COREWEBVIEW2_POINTER_EVENT_KIND_ENTER;
break;
case WebviewPointerEventKind::Leave:
event = COREWEBVIEW2_POINTER_EVENT_KIND_LEAVE;
break;
case WebviewPointerEventKind::Up:
event = COREWEBVIEW2_POINTER_EVENT_KIND_UP;
pointerFlags = POINTER_FLAG_UP;
break;
case WebviewPointerEventKind::Update:
event = COREWEBVIEW2_POINTER_EVENT_KIND_UPDATE;
pointerFlags =
POINTER_FLAG_UPDATE | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT;
break;
}
POINT point;
point.x = static_cast<LONG>(x * scale_factor_);
point.y = static_cast<LONG>(y * scale_factor_);
RECT rect;
rect.left = point.x - 2;
rect.right = point.x + 2;
rect.top = point.y - 2;
rect.bottom = point.y + 2;
host_->CreateWebViewPointerInfo(
[this, pointer, event, pointerFlags, point, rect, pressure](
wil::com_ptr<ICoreWebView2PointerInfo> pointerInfo,
std::unique_ptr<WebviewCreationError> error) {
if (pointerInfo) {
ICoreWebView2PointerInfo* pInfo = pointerInfo.get();
pInfo->put_PointerId(pointer);
pInfo->put_PointerKind(PT_TOUCH);
pInfo->put_PointerFlags(pointerFlags);
pInfo->put_TouchFlags(TOUCH_FLAG_NONE);
pInfo->put_TouchMask(TOUCH_MASK_CONTACTAREA | TOUCH_MASK_PRESSURE);
pInfo->put_TouchPressure(
std::clamp((UINT32)(pressure == 0.0 ? 1024 : 1024 * pressure),
(UINT32)0, (UINT32)1024));
pInfo->put_PixelLocationRaw(point);
pInfo->put_TouchContactRaw(rect);
composition_controller_->SendPointerInput(event, pInfo);
}
});
}
void Webview::SetPointerButtonState(WebviewPointerButton button, bool is_down) {
if (!IsValid()) {
return;
}
COREWEBVIEW2_MOUSE_EVENT_KIND kind;
switch (button) {
case WebviewPointerButton::Primary:
virtual_keys_.set_isLeftButtonDown(is_down);
kind = is_down ? COREWEBVIEW2_MOUSE_EVENT_KIND_LEFT_BUTTON_DOWN
: COREWEBVIEW2_MOUSE_EVENT_KIND_LEFT_BUTTON_UP;
break;
case WebviewPointerButton::Secondary:
virtual_keys_.set_isRightButtonDown(is_down);
kind = is_down ? COREWEBVIEW2_MOUSE_EVENT_KIND_RIGHT_BUTTON_DOWN
: COREWEBVIEW2_MOUSE_EVENT_KIND_RIGHT_BUTTON_UP;
break;
case WebviewPointerButton::Tertiary:
virtual_keys_.set_isMiddleButtonDown(is_down);
kind = is_down ? COREWEBVIEW2_MOUSE_EVENT_KIND_MIDDLE_BUTTON_DOWN
: COREWEBVIEW2_MOUSE_EVENT_KIND_MIDDLE_BUTTON_UP;
break;
default:
kind = static_cast<COREWEBVIEW2_MOUSE_EVENT_KIND>(0);
}
composition_controller_->SendMouseInput(kind, virtual_keys_.state(), 0,
last_cursor_pos_);
}
void Webview::SendScroll(double delta, bool horizontal) {
// delta * 6 gives me a multiple of WHEEL_DELTA (120)
constexpr auto kScrollMultiplier = 6;
auto offset = static_cast<short>(delta * kScrollMultiplier);
POINT point;
point.x = 0;
point.y = 0;
if (horizontal) {
composition_controller_->SendMouseInput(
COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL, virtual_keys_.state(),
offset, point);
} else {
composition_controller_->SendMouseInput(COREWEBVIEW2_MOUSE_EVENT_KIND_WHEEL,
virtual_keys_.state(), offset,
point);
}
}
void Webview::SetScrollDelta(double delta_x, double delta_y) {
if (!IsValid()) {
return;
}
if (delta_x != 0.0) {
SendScroll(delta_x, true);
}
if (delta_y != 0.0) {
SendScroll(delta_y, false);
}
}
void Webview::LoadUrl(const std::string& url) {
if (IsValid()) {
webview_->Navigate(util::Utf16FromUtf8(url).c_str());
}
}
void Webview::LoadStringContent(const std::string& content) {
if (IsValid()) {
webview_->NavigateToString(util::Utf16FromUtf8(content).c_str());
}
}
bool Webview::Stop() {
if (!IsValid()) {
return false;
}
return SUCCEEDED(webview_->CallDevToolsProtocolMethod(L"Page.stopLoading",
L"{}", nullptr));
}
bool Webview::Reload() {
if (!IsValid()) {
return false;
}
return SUCCEEDED(webview_->Reload());
}
bool Webview::GoBack() {
if (!IsValid()) {
return false;
}
return SUCCEEDED(webview_->GoBack());
}
bool Webview::GoForward() {
if (!IsValid()) {
return false;
}
return SUCCEEDED(webview_->GoForward());
}
void Webview::AddScriptToExecuteOnDocumentCreated(
const std::string& script,
AddScriptToExecuteOnDocumentCreatedCallback callback) {
if (IsValid()) {
if (SUCCEEDED(webview_->AddScriptToExecuteOnDocumentCreated(
util::Utf16FromUtf8(script).c_str(),
Callback<
ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler>(
[callback](HRESULT result, LPCWSTR wsid) -> HRESULT {
std::string sid = util::Utf8FromUtf16(wsid);
callback(SUCCEEDED(result), sid);
return S_OK;
})
.Get()))) {
return;
}
}
callback(false, std::string());
}
void Webview::RemoveScriptToExecuteOnDocumentCreated(
const std::string& script_id) {
if (IsValid()) {
webview_->RemoveScriptToExecuteOnDocumentCreated(
util::Utf16FromUtf8(script_id).c_str());
}
}
void Webview::ExecuteScript(const std::string& script,
ScriptExecutedCallback callback) {
if (IsValid()) {
if (SUCCEEDED(webview_->ExecuteScript(
util::Utf16FromUtf8(script).c_str(),
Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
[callback](HRESULT result, LPCWSTR json_result_object) {
callback(SUCCEEDED(result),
util::Utf8FromUtf16(json_result_object));
return S_OK;
})
.Get()))) {
return;
}
}
callback(false, std::string());
}
bool Webview::PostWebMessage(const std::string& json) {
if (!IsValid()) {
return false;
}
return webview_->PostWebMessageAsJson(util::Utf16FromUtf8(json).c_str()) ==
S_OK;
}
bool Webview::Suspend() {
if (!IsValid()) {
return false;
}
wil::com_ptr<ICoreWebView2_3> webview;
webview = webview_.query<ICoreWebView2_3>();
if (!webview) {
return false;
}
webview_controller_->put_IsVisible(false);
return webview->TrySuspend(
Callback<ICoreWebView2TrySuspendCompletedHandler>(
[](HRESULT error_code, BOOL is_successful) -> HRESULT {
return S_OK;
})
.Get()) == S_OK;
}
bool Webview::Resume() {
if (!IsValid()) {
return false;
}
wil::com_ptr<ICoreWebView2_3> webview;
webview = webview_.query<ICoreWebView2_3>();
if (!webview) {
return false;
}
return webview->Resume() == S_OK &&
webview_controller_->put_IsVisible(true) == S_OK;
}
bool Webview::SetVirtualHostNameMapping(
const std::string& hostName, const std::string& path,
WebviewHostResourceAccessKind accessKind) {
if (!IsValid()) {
return false;
}
wil::com_ptr<ICoreWebView2_3> webview;
webview = webview_.query<ICoreWebView2_3>();
if (!webview) {
return false;
}
COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND accessKindIntValue =
COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY;
switch (accessKind) {
case WebviewHostResourceAccessKind::Allow:
accessKindIntValue = COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_ALLOW;
break;
case WebviewHostResourceAccessKind::DenyCors:
accessKindIntValue = COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY_CORS;
break;
case WebviewHostResourceAccessKind::Deny:
accessKindIntValue = COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY;
break;
}
return webview->SetVirtualHostNameToFolderMapping(
util::Utf16FromUtf8(hostName).c_str(), util::Utf16FromUtf8(path).c_str(),
accessKindIntValue);
}
bool Webview::ClearVirtualHostNameMapping(const std::string& hostName) {
if (!IsValid()) {
return false;
}
wil::com_ptr<ICoreWebView2_3> webview;
webview = webview_.query<ICoreWebView2_3>();
if (!webview) {
return false;
}
return webview->ClearVirtualHostNameToFolderMapping(
util::Utf16FromUtf8(hostName).c_str());
}
-259
View File
@@ -1,259 +0,0 @@
#pragma once
#include <WebView2.h>
#include <wil/com.h>
#include <windows.ui.composition.desktop.h>
#include <windows.ui.composition.h>
#include <winrt/base.h>
#include <functional>
class WebviewHost;
enum class WebviewLoadingState { None, Loading, NavigationCompleted };
enum class WebviewPointerButton { None, Primary, Secondary, Tertiary };
enum class WebviewPointerEventKind { Activate, Down, Enter, Leave, Up, Update };
enum class WebviewPermissionKind {
Unknown,
Microphone,
Camera,
GeoLocation,
Notifications,
OtherSensors,
ClipboardRead
};
enum class WebviewPermissionState { Default, Allow, Deny };
enum class WebviewPopupWindowPolicy { Allow, Deny, ShowInSameWindow };
enum class WebviewHostResourceAccessKind { Deny, Allow, DenyCors };
struct WebviewHistoryChanged {
BOOL can_go_back;
BOOL can_go_forward;
};
struct VirtualKeyState {
public:
inline void set_isLeftButtonDown(bool is_down) {
set(COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS::
COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_LEFT_BUTTON,
is_down);
}
inline void set_isRightButtonDown(bool is_down) {
set(COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS::
COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_RIGHT_BUTTON,
is_down);
}
inline void set_isMiddleButtonDown(bool is_down) {
set(COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS::
COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_MIDDLE_BUTTON,
is_down);
}
inline COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS state() const { return state_; }
private:
COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS state_ =
COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS::
COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_NONE;
inline void set(COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS key, bool flag) {
if (flag) {
state_ |= key;
} else {
state_ &= ~key;
}
}
};
struct EventRegistrations {
EventRegistrationToken source_changed_token_{};
EventRegistrationToken content_loading_token_{};
EventRegistrationToken navigation_completed_token_{};
EventRegistrationToken history_changed_token_{};
EventRegistrationToken document_title_changed_token_{};
EventRegistrationToken cursor_changed_token_{};
EventRegistrationToken got_focus_token_{};
EventRegistrationToken lost_focus_token_{};
EventRegistrationToken web_message_received_token_{};
EventRegistrationToken permission_requested_token_{};
EventRegistrationToken devtools_protocol_event_token_{};
EventRegistrationToken new_windows_requested_token_{};
EventRegistrationToken contains_fullscreen_element_changed_token_{};
};
class Webview {
public:
friend class WebviewHost;
typedef std::function<void(const std::string&)> UrlChangedCallback;
typedef std::function<void(WebviewLoadingState)> LoadingStateChangedCallback;
typedef std::function<void(COREWEBVIEW2_WEB_ERROR_STATUS)>
OnLoadErrorCallback;
typedef std::function<void(WebviewHistoryChanged)> HistoryChangedCallback;
typedef std::function<void(const std::string&)> DevtoolsProtocolEventCallback;
typedef std::function<void(const std::string&)> DocumentTitleChangedCallback;
typedef std::function<void(size_t width, size_t height)>
SurfaceSizeChangedCallback;
typedef std::function<void(const HCURSOR)> CursorChangedCallback;
typedef std::function<void(bool)> FocusChangedCallback;
typedef std::function<void(bool, const std::string&)>
AddScriptToExecuteOnDocumentCreatedCallback;
typedef std::function<void(bool, const std::string&)> ScriptExecutedCallback;
typedef std::function<void(const std::string&)> WebMessageReceivedCallback;
typedef std::function<void(WebviewPermissionState state)>
WebviewPermissionRequestedCompleter;
typedef std::function<void(const std::string& url, WebviewPermissionKind kind,
bool is_user_initiated,
WebviewPermissionRequestedCompleter completer)>
PermissionRequestedCallback;
typedef std::function<void(bool contains_fullscreen_element)>
ContainsFullScreenElementChangedCallback;
~Webview();
ABI::Windows::UI::Composition::IVisual* const surface() {
return surface_.get();
}
bool IsValid() { return is_valid_; }
void SetSurfaceSize(size_t width, size_t height, float scale_factor);
void SetCursorPos(double x, double y);
void SetPointerUpdate(int32_t pointer, WebviewPointerEventKind eventKind,
double x, double y, double size, double pressure);
void SetPointerButtonState(WebviewPointerButton button, bool isDown);
void SetScrollDelta(double delta_x, double delta_y);
void LoadUrl(const std::string& url);
void LoadStringContent(const std::string& content);
bool Stop();
bool Reload();
bool GoBack();
bool GoForward();
void AddScriptToExecuteOnDocumentCreated(
const std::string& script,
AddScriptToExecuteOnDocumentCreatedCallback callback);
void RemoveScriptToExecuteOnDocumentCreated(const std::string& script_id);
void ExecuteScript(const std::string& script,
ScriptExecutedCallback callback);
bool PostWebMessage(const std::string& json);
bool ClearCookies();
bool ClearCache();
bool SetCacheDisabled(bool disabled);
void SetPopupWindowPolicy(WebviewPopupWindowPolicy policy);
bool SetUserAgent(const std::string& user_agent);
bool OpenDevTools();
bool SetBackgroundColor(int32_t color);
bool SetZoomFactor(double factor);
bool Suspend();
bool Resume();
bool SetVirtualHostNameMapping(const std::string& hostName,
const std::string& path,
WebviewHostResourceAccessKind accessKind);
bool ClearVirtualHostNameMapping(const std::string& hostName);
void OnUrlChanged(UrlChangedCallback callback) {
url_changed_callback_ = std::move(callback);
}
void OnLoadError(OnLoadErrorCallback callback) {
on_load_error_callback_ = std::move(callback);
}
void OnLoadingStateChanged(LoadingStateChangedCallback callback) {
loading_state_changed_callback_ = std::move(callback);
}
void OnHistoryChanged(HistoryChangedCallback callback) {
history_changed_callback_ = std::move(callback);
}
void OnSurfaceSizeChanged(SurfaceSizeChangedCallback callback) {
surface_size_changed_callback_ = std::move(callback);
}
void OnDocumentTitleChanged(DocumentTitleChangedCallback callback) {
document_title_changed_callback_ = std::move(callback);
}
void OnCursorChanged(CursorChangedCallback callback) {
cursor_changed_callback_ = std::move(callback);
}
void OnFocusChanged(FocusChangedCallback callback) {
focus_changed_callback_ = std::move(callback);
}
void OnWebMessageReceived(WebMessageReceivedCallback callback) {
web_message_received_callback_ = std::move(callback);
}
void OnPermissionRequested(PermissionRequestedCallback callback) {
permission_requested_callback_ = std::move(callback);
}
void OnDevtoolsProtocolEvent(DevtoolsProtocolEventCallback callback) {
devtools_protocol_event_callback_ = std::move(callback);
}
void OnContainsFullScreenElementChanged(
ContainsFullScreenElementChangedCallback callback) {
contains_fullscreen_element_changed_callback_ = std::move(callback);
}
private:
HWND hwnd_;
bool owns_window_;
bool is_valid_ = false;
float scale_factor_ = 1.0;
wil::com_ptr<ICoreWebView2CompositionController> composition_controller_;
wil::com_ptr<ICoreWebView2Controller3> webview_controller_;
wil::com_ptr<ICoreWebView2> webview_;
wil::com_ptr<ICoreWebView2DevToolsProtocolEventReceiver>
devtools_protocol_event_receiver_;
wil::com_ptr<ICoreWebView2Settings2> settings2_;
POINT last_cursor_pos_ = {0, 0};
VirtualKeyState virtual_keys_;
WebviewPopupWindowPolicy popup_window_policy_ =
WebviewPopupWindowPolicy::Allow;
winrt::com_ptr<ABI::Windows::UI::Composition::IVisual> surface_;
winrt::com_ptr<ABI::Windows::UI::Composition::Desktop::IDesktopWindowTarget>
window_target_;
WebviewHost* host_;
EventRegistrations event_registrations_{};
UrlChangedCallback url_changed_callback_;
LoadingStateChangedCallback loading_state_changed_callback_;
OnLoadErrorCallback on_load_error_callback_;
HistoryChangedCallback history_changed_callback_;
DocumentTitleChangedCallback document_title_changed_callback_;
SurfaceSizeChangedCallback surface_size_changed_callback_;
CursorChangedCallback cursor_changed_callback_;
FocusChangedCallback focus_changed_callback_;
WebMessageReceivedCallback web_message_received_callback_;
PermissionRequestedCallback permission_requested_callback_;
DevtoolsProtocolEventCallback devtools_protocol_event_callback_;
ContainsFullScreenElementChangedCallback
contains_fullscreen_element_changed_callback_;
Webview(
wil::com_ptr<ICoreWebView2CompositionController> composition_controller,
WebviewHost* host, HWND hwnd, bool owns_window, bool offscreen_only);
bool CreateSurface(
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor> compositor,
HWND hwnd, bool offscreen_only);
void RegisterEventHandlers();
void EnableSecurityUpdates();
void SendScroll(double offset, bool horizontal);
};
@@ -1,692 +0,0 @@
#include "webview_bridge.h"
#include <flutter/event_stream_handler_functions.h>
#include <flutter/method_result_functions.h>
#include <format>
#ifdef HAVE_FLUTTER_D3D_TEXTURE
#include "texture_bridge_gpu.h"
#else
#include "texture_bridge_fallback.h"
#endif
namespace {
constexpr auto kErrorInvalidArgs = "invalidArguments";
constexpr auto kMethodLoadUrl = "loadUrl";
constexpr auto kMethodLoadStringContent = "loadStringContent";
constexpr auto kMethodReload = "reload";
constexpr auto kMethodStop = "stop";
constexpr auto kMethodGoBack = "goBack";
constexpr auto kMethodGoForward = "goForward";
constexpr auto kMethodAddScriptToExecuteOnDocumentCreated =
"addScriptToExecuteOnDocumentCreated";
constexpr auto kMethodRemoveScriptToExecuteOnDocumentCreated =
"removeScriptToExecuteOnDocumentCreated";
constexpr auto kMethodExecuteScript = "executeScript";
constexpr auto kMethodPostWebMessage = "postWebMessage";
constexpr auto kMethodSetSize = "setSize";
constexpr auto kMethodSetCursorPos = "setCursorPos";
constexpr auto kMethodSetPointerUpdate = "setPointerUpdate";
constexpr auto kMethodSetPointerButton = "setPointerButton";
constexpr auto kMethodSetScrollDelta = "setScrollDelta";
constexpr auto kMethodSetUserAgent = "setUserAgent";
constexpr auto kMethodSetBackgroundColor = "setBackgroundColor";
constexpr auto kMethodSetZoomFactor = "setZoomFactor";
constexpr auto kMethodOpenDevTools = "openDevTools";
constexpr auto kMethodSuspend = "suspend";
constexpr auto kMethodResume = "resume";
constexpr auto kMethodSetVirtualHostNameMapping = "setVirtualHostNameMapping";
constexpr auto kMethodClearVirtualHostNameMapping =
"clearVirtualHostNameMapping";
constexpr auto kMethodClearCookies = "clearCookies";
constexpr auto kMethodClearCache = "clearCache";
constexpr auto kMethodSetCacheDisabled = "setCacheDisabled";
constexpr auto kMethodSetPopupWindowPolicy = "setPopupWindowPolicy";
constexpr auto kMethodSetFpsLimit = "setFpsLimit";
constexpr auto kEventType = "type";
constexpr auto kEventValue = "value";
constexpr auto kErrorNotSupported = "not_supported";
constexpr auto kScriptFailed = "script_failed";
constexpr auto kMethodFailed = "method_failed";
static const std::optional<std::pair<double, double>> GetPointFromArgs(
const flutter::EncodableValue* args) {
const flutter::EncodableList* list =
std::get_if<flutter::EncodableList>(args);
if (!list || list->size() != 2) {
return std::nullopt;
}
const auto x = std::get_if<double>(&(*list)[0]);
const auto y = std::get_if<double>(&(*list)[1]);
if (!x || !y) {
return std::nullopt;
}
return std::make_pair(*x, *y);
}
static const std::optional<std::tuple<double, double, double>>
GetPointAndScaleFactorFromArgs(const flutter::EncodableValue* args) {
const flutter::EncodableList* list =
std::get_if<flutter::EncodableList>(args);
if (!list || list->size() != 3) {
return std::nullopt;
}
const auto x = std::get_if<double>(&(*list)[0]);
const auto y = std::get_if<double>(&(*list)[1]);
const auto z = std::get_if<double>(&(*list)[2]);
if (!x || !y || !z) {
return std::nullopt;
}
return std::make_tuple(*x, *y, *z);
}
static const std::string& GetCursorName(const HCURSOR cursor) {
// The cursor names correspond to the Flutter Engine names:
// in shell/platform/windows/flutter_window_win32.cc
static const std::string kDefaultCursorName = "basic";
static const std::pair<std::string, const wchar_t*> mappings[] = {
{"allScroll", IDC_SIZEALL},
{kDefaultCursorName, IDC_ARROW},
{"click", IDC_HAND},
{"forbidden", IDC_NO},
{"help", IDC_HELP},
{"move", IDC_SIZEALL},
{"none", nullptr},
{"noDrop", IDC_NO},
{"precise", IDC_CROSS},
{"progress", IDC_APPSTARTING},
{"text", IDC_IBEAM},
{"resizeColumn", IDC_SIZEWE},
{"resizeDown", IDC_SIZENS},
{"resizeDownLeft", IDC_SIZENESW},
{"resizeDownRight", IDC_SIZENWSE},
{"resizeLeft", IDC_SIZEWE},
{"resizeLeftRight", IDC_SIZEWE},
{"resizeRight", IDC_SIZEWE},
{"resizeRow", IDC_SIZENS},
{"resizeUp", IDC_SIZENS},
{"resizeUpDown", IDC_SIZENS},
{"resizeUpLeft", IDC_SIZENWSE},
{"resizeUpRight", IDC_SIZENESW},
{"resizeUpLeftDownRight", IDC_SIZENWSE},
{"resizeUpRightDownLeft", IDC_SIZENESW},
{"wait", IDC_WAIT},
};
static std::map<HCURSOR, std::string> cursors;
static bool initialized = false;
if (!initialized) {
initialized = true;
for (const auto& pair : mappings) {
HCURSOR cursor_handle = LoadCursor(nullptr, pair.second);
if (cursor_handle) {
cursors[cursor_handle] = pair.first;
}
}
}
const auto it = cursors.find(cursor);
if (it != cursors.end()) {
return it->second;
}
return kDefaultCursorName;
}
} // namespace
WebviewBridge::WebviewBridge(flutter::BinaryMessenger* messenger,
flutter::TextureRegistrar* texture_registrar,
GraphicsContext* graphics_context,
std::unique_ptr<Webview> webview)
: webview_(std::move(webview)), texture_registrar_(texture_registrar) {
#ifdef HAVE_FLUTTER_D3D_TEXTURE
texture_bridge_ =
std::make_unique<TextureBridgeGpu>(graphics_context, webview_->surface());
flutter_texture_ =
std::make_unique<flutter::TextureVariant>(flutter::GpuSurfaceTexture(
kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle,
[bridge = static_cast<TextureBridgeGpu*>(texture_bridge_.get())](
size_t width,
size_t height) -> const FlutterDesktopGpuSurfaceDescriptor* {
return bridge->GetSurfaceDescriptor(width, height);
}));
#else
texture_bridge_ = std::make_unique<TextureBridgeFallback>(
graphics_context, webview_->surface());
flutter_texture_ =
std::make_unique<flutter::TextureVariant>(flutter::PixelBufferTexture(
[bridge = static_cast<TextureBridgeFallback*>(texture_bridge_.get())](
size_t width, size_t height) -> const FlutterDesktopPixelBuffer* {
return bridge->CopyPixelBuffer(width, height);
}));
#endif
texture_id_ = texture_registrar->RegisterTexture(flutter_texture_.get());
texture_bridge_->SetOnFrameAvailable(
[this]() { texture_registrar_->MarkTextureFrameAvailable(texture_id_); });
// texture_bridge_->SetOnSurfaceSizeChanged([this](Size size) {
// webview_->SetSurfaceSize(size.width, size.height);
//});
const auto method_channel_name =
std::format("io.jns.webview.win/{}", texture_id_);
method_channel_ =
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
messenger, method_channel_name,
&flutter::StandardMethodCodec::GetInstance());
method_channel_->SetMethodCallHandler([this](const auto& call, auto result) {
HandleMethodCall(call, std::move(result));
});
const auto event_channel_name =
std::format("io.jns.webview.win/{}/events", texture_id_);
event_channel_ =
std::make_unique<flutter::EventChannel<flutter::EncodableValue>>(
messenger, event_channel_name,
&flutter::StandardMethodCodec::GetInstance());
auto handler = std::make_unique<
flutter::StreamHandlerFunctions<flutter::EncodableValue>>(
[this](const flutter::EncodableValue* arguments,
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>>&&
events) {
event_sink_ = std::move(events);
RegisterEventHandlers();
return nullptr;
},
[this](const flutter::EncodableValue* arguments) {
event_sink_ = nullptr;
return nullptr;
});
event_channel_->SetStreamHandler(std::move(handler));
}
WebviewBridge::~WebviewBridge() {
method_channel_->SetMethodCallHandler(nullptr);
texture_registrar_->UnregisterTexture(texture_id_);
}
void WebviewBridge::RegisterEventHandlers() {
webview_->OnUrlChanged([this](const std::string& url) {
const auto event = flutter::EncodableValue(flutter::EncodableMap{
{flutter::EncodableValue(kEventType),
flutter::EncodableValue("urlChanged")},
{flutter::EncodableValue(kEventValue), flutter::EncodableValue(url)},
});
EmitEvent(event);
});
webview_->OnLoadError([this](COREWEBVIEW2_WEB_ERROR_STATUS web_status) {
const auto event = flutter::EncodableValue(flutter::EncodableMap{
{flutter::EncodableValue(kEventType),
flutter::EncodableValue("onLoadError")},
{flutter::EncodableValue(kEventValue),
flutter::EncodableValue(static_cast<int>(web_status))},
});
EmitEvent(event);
});
webview_->OnLoadingStateChanged([this](WebviewLoadingState state) {
const auto event = flutter::EncodableValue(flutter::EncodableMap{
{flutter::EncodableValue(kEventType),
flutter::EncodableValue("loadingStateChanged")},
{flutter::EncodableValue(kEventValue),
flutter::EncodableValue(static_cast<int>(state))},
});
EmitEvent(event);
});
webview_->OnHistoryChanged([this](WebviewHistoryChanged historyChanged) {
const auto event = flutter::EncodableValue(flutter::EncodableMap{
{flutter::EncodableValue(kEventType),
flutter::EncodableValue("historyChanged")},
{flutter::EncodableValue(kEventValue),
flutter::EncodableValue(flutter::EncodableMap{
{flutter::EncodableValue("canGoBack"),
flutter::EncodableValue(
static_cast<bool>(historyChanged.can_go_back))},
{flutter::EncodableValue("canGoForward"),
flutter::EncodableValue(
static_cast<bool>(historyChanged.can_go_forward))},
})},
});
EmitEvent(event);
});
webview_->OnDevtoolsProtocolEvent([this](const std::string& json) {
const auto event = flutter::EncodableValue(flutter::EncodableMap{
{flutter::EncodableValue(kEventType),
flutter::EncodableValue("securityStateChanged")},
{flutter::EncodableValue(kEventValue), flutter::EncodableValue(json)}});
EmitEvent(event);
});
webview_->OnDocumentTitleChanged([this](const std::string& title) {
const auto event = flutter::EncodableValue(flutter::EncodableMap{
{flutter::EncodableValue(kEventType),
flutter::EncodableValue("titleChanged")},
{flutter::EncodableValue(kEventValue), flutter::EncodableValue(title)},
});
EmitEvent(event);
});
webview_->OnSurfaceSizeChanged([this](size_t width, size_t height) {
texture_bridge_->NotifySurfaceSizeChanged();
});
webview_->OnCursorChanged([this](const HCURSOR cursor) {
const auto& name = GetCursorName(cursor);
const auto event = flutter::EncodableValue(
flutter::EncodableMap{{flutter::EncodableValue(kEventType),
flutter::EncodableValue("cursorChanged")},
{flutter::EncodableValue(kEventValue), name}});
EmitEvent(event);
});
webview_->OnWebMessageReceived([this](const std::string& message) {
const auto event = flutter::EncodableValue(
flutter::EncodableMap{{flutter::EncodableValue(kEventType),
flutter::EncodableValue("webMessageReceived")},
{flutter::EncodableValue(kEventValue), message}});
EmitEvent(event);
});
webview_->OnPermissionRequested(
[this](const std::string& url, WebviewPermissionKind kind,
bool is_user_initiated,
Webview::WebviewPermissionRequestedCompleter completer) {
OnPermissionRequested(url, kind, is_user_initiated, completer);
});
webview_->OnContainsFullScreenElementChanged(
[this](bool contains_fullscreen_element) {
const auto event = flutter::EncodableValue(flutter::EncodableMap{
{flutter::EncodableValue(kEventType),
flutter::EncodableValue("containsFullScreenElementChanged")},
{flutter::EncodableValue(kEventValue),
contains_fullscreen_element}});
EmitEvent(event);
});
}
void WebviewBridge::OnPermissionRequested(
const std::string& url, WebviewPermissionKind permissionKind,
bool isUserInitiated,
Webview::WebviewPermissionRequestedCompleter completer) {
auto args = std::make_unique<flutter::EncodableValue>(flutter::EncodableMap{
{"url", url},
{"isUserInitiated", isUserInitiated},
{"permissionKind", static_cast<int>(permissionKind)}});
method_channel_->InvokeMethod(
"permissionRequested", std::move(args),
std::make_unique<flutter::MethodResultFunctions<flutter::EncodableValue>>(
[completer](const flutter::EncodableValue* result) {
auto allow = std::get_if<bool>(result);
if (allow != nullptr) {
return completer(*allow ? WebviewPermissionState::Allow
: WebviewPermissionState::Deny);
}
completer(WebviewPermissionState::Default);
},
[completer](const std::string& error_code,
const std::string& error_message,
const flutter::EncodableValue* error_details) {
completer(WebviewPermissionState::Default);
},
[completer]() { completer(WebviewPermissionState::Default); }));
}
void WebviewBridge::HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
const auto& method_name = method_call.method_name();
// setCursorPos: [double x, double y]
if (method_name.compare(kMethodSetCursorPos) == 0) {
const auto point = GetPointFromArgs(method_call.arguments());
if (point) {
webview_->SetCursorPos(point->first, point->second);
return result->Success();
}
return result->Error(kErrorInvalidArgs);
}
// setPointerUpdate:
// [int pointer, int event, double x, double y, double size, double pressure]
if (method_name.compare(kMethodSetPointerUpdate) == 0) {
const flutter::EncodableList* list =
std::get_if<flutter::EncodableList>(method_call.arguments());
if (!list || list->size() != 6) {
return result->Error(kErrorInvalidArgs);
}
const auto pointer = std::get_if<int32_t>(&(*list)[0]);
const auto event = std::get_if<int32_t>(&(*list)[1]);
const auto x = std::get_if<double>(&(*list)[2]);
const auto y = std::get_if<double>(&(*list)[3]);
const auto size = std::get_if<double>(&(*list)[4]);
const auto pressure = std::get_if<double>(&(*list)[5]);
if (pointer && event && x && y && size && pressure) {
webview_->SetPointerUpdate(*pointer,
static_cast<WebviewPointerEventKind>(*event),
*x, *y, *size, *pressure);
return result->Success();
}
return result->Error(kErrorInvalidArgs);
}
// setScrollDelta: [double dx, double dy]
if (method_name.compare(kMethodSetScrollDelta) == 0) {
const auto delta = GetPointFromArgs(method_call.arguments());
if (delta) {
webview_->SetScrollDelta(delta->first, delta->second);
return result->Success();
}
return result->Error(kErrorInvalidArgs);
}
// setPointerButton: {"button": int, "isDown": bool}
if (method_name.compare(kMethodSetPointerButton) == 0) {
const auto& map = std::get<flutter::EncodableMap>(*method_call.arguments());
const auto button = map.find(flutter::EncodableValue("button"));
const auto isDown = map.find(flutter::EncodableValue("isDown"));
if (button != map.end() && isDown != map.end()) {
const auto buttonValue = std::get_if<int32_t>(&button->second);
const auto isDownValue = std::get_if<bool>(&isDown->second);
if (buttonValue && isDownValue) {
webview_->SetPointerButtonState(
static_cast<WebviewPointerButton>(*buttonValue), *isDownValue);
return result->Success();
}
}
return result->Error(kErrorInvalidArgs);
}
// setSize: [double width, double height, double scale_factor]
if (method_name.compare(kMethodSetSize) == 0) {
auto size = GetPointAndScaleFactorFromArgs(method_call.arguments());
if (size) {
const auto [width, height, scale_factor] = size.value();
webview_->SetSurfaceSize(static_cast<size_t>(width),
static_cast<size_t>(height),
static_cast<float>(scale_factor));
texture_bridge_->Start();
return result->Success();
}
return result->Error(kErrorInvalidArgs);
}
// loadUrl: string
if (method_name.compare(kMethodLoadUrl) == 0) {
if (const auto url = std::get_if<std::string>(method_call.arguments())) {
webview_->LoadUrl(*url);
return result->Success();
}
return result->Error(kErrorInvalidArgs);
}
// loadStringContent: string
if (method_name.compare(kMethodLoadStringContent) == 0) {
if (const auto content =
std::get_if<std::string>(method_call.arguments())) {
webview_->LoadStringContent(*content);
return result->Success();
}
return result->Error(kErrorInvalidArgs);
}
// reload
if (method_name.compare(kMethodReload) == 0) {
if (webview_->Reload()) {
return result->Success();
}
return result->Error(kMethodFailed);
}
// stop
if (method_name.compare(kMethodStop) == 0) {
if (webview_->Stop()) {
return result->Success();
}
return result->Error(kMethodFailed);
}
// goBack
if (method_name.compare(kMethodGoBack) == 0) {
if (webview_->GoBack()) {
return result->Success();
}
return result->Error(kMethodFailed);
}
// goForward
if (method_name.compare(kMethodGoForward) == 0) {
if (webview_->GoForward()) {
return result->Success();
}
return result->Error(kMethodFailed);
}
// suspend
if (method_name.compare(kMethodSuspend) == 0) {
texture_bridge_->Stop();
webview_->Suspend();
return result->Success();
}
// resume
if (method_name.compare(kMethodResume) == 0) {
webview_->Resume();
texture_bridge_->Start();
return result->Success();
}
// setVirtualHostNameMapping [string hostName, string path, int accessKind]
if (method_name.compare(kMethodSetVirtualHostNameMapping) == 0) {
const flutter::EncodableList* list =
std::get_if<flutter::EncodableList>(method_call.arguments());
if (!list || list->size() != 3) {
return result->Error(kErrorInvalidArgs);
}
const auto hostName = std::get_if<std::string>(&(*list)[0]);
const auto path = std::get_if<std::string>(&(*list)[1]);
const auto accessKind = std::get_if<int32_t>(&(*list)[2]);
if (hostName && path && accessKind) {
webview_->SetVirtualHostNameMapping(
*hostName, *path,
static_cast<WebviewHostResourceAccessKind>(*accessKind));
return result->Success();
}
return result->Error(kErrorInvalidArgs);
}
// clearVirtualHostNameMapping: string
if (method_name.compare(kMethodClearVirtualHostNameMapping) == 0) {
if (const auto hostName =
std::get_if<std::string>(method_call.arguments())) {
if (webview_->ClearVirtualHostNameMapping(*hostName)) {
return result->Success();
}
}
return result->Error(kErrorInvalidArgs);
}
if (method_name.compare(kMethodAddScriptToExecuteOnDocumentCreated) == 0) {
if (const auto script = std::get_if<std::string>(method_call.arguments())) {
std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>
shared_result = std::move(result);
webview_->AddScriptToExecuteOnDocumentCreated(
*script, [shared_result](bool success, const std::string& script_id) {
if (success) {
shared_result->Success(script_id);
} else {
shared_result->Error(kScriptFailed, "Executing script failed.");
}
});
return;
}
return result->Error(kErrorInvalidArgs);
}
if (method_name.compare(kMethodRemoveScriptToExecuteOnDocumentCreated) == 0) {
if (const auto script_id =
std::get_if<std::string>(method_call.arguments())) {
std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>
shared_result = std::move(result);
webview_->RemoveScriptToExecuteOnDocumentCreated(*script_id);
shared_result->Success();
return;
}
return result->Error(kErrorInvalidArgs);
}
// executeScript: string
if (method_name.compare(kMethodExecuteScript) == 0) {
if (const auto script = std::get_if<std::string>(method_call.arguments())) {
std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>
shared_result = std::move(result);
webview_->ExecuteScript(
*script,
[shared_result](bool success, const std::string& json_result) {
if (success) {
shared_result->Success(json_result);
} else {
shared_result->Error(kScriptFailed, "Executing script failed.");
}
});
return;
}
return result->Error(kErrorInvalidArgs);
}
// postWebMessage: string
if (method_name.compare(kMethodPostWebMessage) == 0) {
if (const auto message =
std::get_if<std::string>(method_call.arguments())) {
if (webview_->PostWebMessage(*message)) {
return result->Success();
}
return result->Error(kErrorNotSupported, "Posting the message failed.");
}
return result->Error(kErrorInvalidArgs);
}
// setUserAgent: string
if (method_name.compare(kMethodSetUserAgent) == 0) {
if (const auto user_agent =
std::get_if<std::string>(method_call.arguments())) {
if (webview_->SetUserAgent(*user_agent)) {
return result->Success();
}
return result->Error(kErrorNotSupported,
"Setting the user agent failed.");
}
return result->Error(kErrorInvalidArgs);
}
// setBackgroundColor: int
if (method_name.compare(kMethodSetBackgroundColor) == 0) {
if (const auto color = std::get_if<int32_t>(method_call.arguments())) {
if (webview_->SetBackgroundColor(*color)) {
return result->Success();
}
return result->Error(kErrorNotSupported,
"Setting the background color failed.");
}
return result->Error(kErrorInvalidArgs);
}
// setZoomFactor: double
if (method_name.compare(kMethodSetZoomFactor) == 0) {
if (const auto factor = std::get_if<double>(method_call.arguments())) {
if (webview_->SetZoomFactor(*factor)) {
return result->Success();
}
return result->Error(kErrorNotSupported,
"Setting the zoom factor failed.");
}
return result->Error(kErrorInvalidArgs);
}
// openDevTools
if (method_name.compare(kMethodOpenDevTools) == 0) {
if (webview_->OpenDevTools()) {
return result->Success();
}
return result->Error(kMethodFailed);
}
// clearCookies
if (method_name.compare(kMethodClearCookies) == 0) {
if (webview_->ClearCookies()) {
return result->Success();
}
return result->Error(kMethodFailed);
}
// clearCache
if (method_name.compare(kMethodClearCache) == 0) {
if (webview_->ClearCache()) {
return result->Success();
}
return result->Error(kMethodFailed);
}
// setCacheDisabled: bool
if (method_name.compare(kMethodSetCacheDisabled) == 0) {
if (const auto disabled = std::get_if<bool>(method_call.arguments())) {
if (webview_->SetCacheDisabled(*disabled)) {
return result->Success();
}
}
return result->Error(kErrorInvalidArgs);
}
// setPopupWindowPolicy: int
if (method_name.compare(kMethodSetPopupWindowPolicy) == 0) {
if (const auto index = std::get_if<int32_t>(method_call.arguments())) {
switch (*index) {
case 1:
webview_->SetPopupWindowPolicy(WebviewPopupWindowPolicy::Deny);
break;
case 2:
webview_->SetPopupWindowPolicy(
WebviewPopupWindowPolicy::ShowInSameWindow);
break;
default:
webview_->SetPopupWindowPolicy(WebviewPopupWindowPolicy::Allow);
break;
}
return result->Success();
}
return result->Error(kErrorInvalidArgs);
}
if (method_name.compare(kMethodSetFpsLimit) == 0) {
if (const auto value = std::get_if<int32_t>(method_call.arguments())) {
texture_bridge_->SetFpsLimit(*value == 0 ? std::nullopt
: std::make_optional(*value));
return result->Success();
}
}
result->NotImplemented();
}
@@ -1,55 +0,0 @@
#pragma once
#include <flutter/event_channel.h>
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>
#include <flutter/texture_registrar.h>
#include <memory>
#include "graphics_context.h"
#include "texture_bridge.h"
#include "webview.h"
class WebviewBridge {
public:
WebviewBridge(flutter::BinaryMessenger* messenger,
flutter::TextureRegistrar* texture_registrar,
GraphicsContext* graphics_context,
std::unique_ptr<Webview> webview);
~WebviewBridge();
TextureBridge* texture_bridge() const { return texture_bridge_.get(); }
int64_t texture_id() const { return texture_id_; }
private:
std::unique_ptr<flutter::TextureVariant> flutter_texture_;
std::unique_ptr<TextureBridge> texture_bridge_;
std::unique_ptr<Webview> webview_;
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> event_sink_;
std::unique_ptr<flutter::EventChannel<flutter::EncodableValue>>
event_channel_;
std::unique_ptr<flutter::MethodChannel<flutter::EncodableValue>>
method_channel_;
flutter::TextureRegistrar* texture_registrar_;
int64_t texture_id_;
void HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
void RegisterEventHandlers();
template <typename T>
void EmitEvent(const T& value) {
if (event_sink_) {
event_sink_->Success(value);
}
}
void OnPermissionRequested(
const std::string& url, WebviewPermissionKind permissionKind,
bool is_user_initiated,
Webview::WebviewPermissionRequestedCompleter completer);
};
@@ -1,117 +0,0 @@
#include "webview_host.h"
#include <wrl.h>
#include <future>
#include <iostream>
#include "util/rohelper.h"
using namespace Microsoft::WRL;
// static
std::unique_ptr<WebviewHost> WebviewHost::Create(
WebviewPlatform* platform, std::optional<std::wstring> user_data_directory,
std::optional<std::wstring> browser_exe_path,
std::optional<std::string> arguments) {
wil::com_ptr<CoreWebView2EnvironmentOptions> opts;
if (arguments.has_value()) {
opts = Microsoft::WRL::Make<CoreWebView2EnvironmentOptions>();
std::wstring warguments(arguments.value().begin(), arguments.value().end());
opts->put_AdditionalBrowserArguments(warguments.c_str());
}
std::promise<HRESULT> result_promise;
wil::com_ptr<ICoreWebView2Environment> env;
auto result = CreateCoreWebView2EnvironmentWithOptions(
browser_exe_path.has_value() ? browser_exe_path->c_str() : nullptr,
user_data_directory.has_value() ? user_data_directory->c_str() : nullptr, opts.get(),
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[&promise = result_promise, &ptr = env](
HRESULT r, ICoreWebView2Environment* env) -> HRESULT {
promise.set_value(r);
ptr.swap(env);
return S_OK;
})
.Get());
if (SUCCEEDED(result)) {
result = result_promise.get_future().get();
if ((SUCCEEDED(result) || result == RPC_E_CHANGED_MODE) && env) {
auto webview_env3 = env.try_query<ICoreWebView2Environment3>();
if (webview_env3) {
return std::unique_ptr<WebviewHost>(
new WebviewHost(platform, std::move(webview_env3)));
}
}
}
return {};
}
WebviewHost::WebviewHost(WebviewPlatform* platform,
wil::com_ptr<ICoreWebView2Environment3> webview_env)
: webview_env_(webview_env) {
compositor_ = platform->graphics_context()->CreateCompositor();
}
void WebviewHost::CreateWebview(HWND hwnd, bool offscreen_only,
bool owns_window,
WebviewCreationCallback callback) {
CreateWebViewCompositionController(
hwnd, [=, self = this](
wil::com_ptr<ICoreWebView2CompositionController> controller,
std::unique_ptr<WebviewCreationError> error) {
if (controller) {
std::unique_ptr<Webview> webview(new Webview(
std::move(controller), self, hwnd, owns_window, offscreen_only));
callback(std::move(webview), nullptr);
} else {
callback(nullptr, std::move(error));
}
});
}
void WebviewHost::CreateWebViewPointerInfo(PointerInfoCreationCallback callback) {
ICoreWebView2PointerInfo *pointer;
auto hr = webview_env_->CreateCoreWebView2PointerInfo(&pointer);
if (FAILED(hr)) {
callback(nullptr, WebviewCreationError::create(hr, "CreateWebViewPointerInfo failed."));
} else if (SUCCEEDED(hr)) {
callback(std::move(wil::com_ptr<ICoreWebView2PointerInfo>(pointer)), nullptr);
}
}
void WebviewHost::CreateWebViewCompositionController(
HWND hwnd, CompositionControllerCreationCallback callback) {
auto hr = webview_env_->CreateCoreWebView2CompositionController(
hwnd,
Callback<
ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler>(
[callback](HRESULT hr,
ICoreWebView2CompositionController* compositionController)
-> HRESULT {
if (SUCCEEDED(hr)) {
callback(
std::move(wil::com_ptr<ICoreWebView2CompositionController>(
compositionController)),
nullptr);
} else {
callback(nullptr, WebviewCreationError::create(
hr,
"CreateCoreWebView2CompositionController "
"completion handler failed."));
}
return S_OK;
})
.Get());
if (FAILED(hr)) {
callback(nullptr,
WebviewCreationError::create(
hr, "CreateCoreWebView2CompositionController failed."));
}
}
@@ -1,63 +0,0 @@
#pragma once
#include <WebView2.h>
#include <WebView2EnvironmentOptions.h>
#include <wil/com.h>
#include <functional>
#include "graphics_context.h"
#include "webview.h"
#include "webview_platform.h"
#include "windows.ui.composition.h"
struct WebviewCreationError {
HRESULT hr;
std::string message;
explicit WebviewCreationError(HRESULT hr, std::string message)
: hr(hr), message(message) {}
static std::unique_ptr<WebviewCreationError> create(
HRESULT hr, const std::string message) {
return std::make_unique<WebviewCreationError>(hr, message);
}
};
class WebviewHost {
public:
typedef std::function<void(std::unique_ptr<Webview>,
std::unique_ptr<WebviewCreationError>)>
WebviewCreationCallback;
typedef std::function<void(wil::com_ptr<ICoreWebView2CompositionController>,
std::unique_ptr<WebviewCreationError>)>
CompositionControllerCreationCallback;
typedef std::function<void(wil::com_ptr<ICoreWebView2PointerInfo>,
std::unique_ptr<WebviewCreationError>)>
PointerInfoCreationCallback;
static std::unique_ptr<WebviewHost> Create(
WebviewPlatform* platform,
std::optional<std::wstring> user_data_directory = std::nullopt,
std::optional<std::wstring> browser_exe_path = std::nullopt,
std::optional<std::string> arguments = std::nullopt);
void CreateWebview(HWND hwnd, bool offscreen_only, bool owns_window,
WebviewCreationCallback callback);
void CreateWebViewPointerInfo(PointerInfoCreationCallback cb);
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor> compositor()
const {
return compositor_;
}
private:
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor> compositor_;
wil::com_ptr<ICoreWebView2Environment3> webview_env_;
WebviewHost(WebviewPlatform* platform,
wil::com_ptr<ICoreWebView2Environment3> webview_env);
void CreateWebViewCompositionController(
HWND hwnd, CompositionControllerCreationCallback cb);
};
@@ -1,77 +0,0 @@
#include "webview_platform.h"
#include <DispatcherQueue.h>
#include <shlobj.h>
#include <windows.graphics.capture.h>
#include <filesystem>
#include <iostream>
WebviewPlatform::WebviewPlatform()
: rohelper_(std::make_unique<rx::RoHelper>(RO_INIT_SINGLETHREADED)) {
if (rohelper_->WinRtAvailable()) {
DispatcherQueueOptions options{sizeof(DispatcherQueueOptions),
DQTYPE_THREAD_CURRENT, DQTAT_COM_STA};
if (FAILED(rohelper_->CreateDispatcherQueueController(
options, dispatcher_queue_controller_.put()))) {
std::cerr << "Creating DispatcherQueueController failed." << std::endl;
return;
}
if (!IsGraphicsCaptureSessionSupported()) {
std::cerr << "Windows::Graphics::Capture::GraphicsCaptureSession is not "
"supported."
<< std::endl;
return;
}
graphics_context_ = std::make_unique<GraphicsContext>(rohelper_.get());
valid_ = graphics_context_->IsValid();
}
}
bool WebviewPlatform::IsGraphicsCaptureSessionSupported() {
HSTRING className;
HSTRING_HEADER classNameHeader;
if (FAILED(rohelper_->GetStringReference(
RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureSession,
&className, &classNameHeader))) {
return false;
}
ABI::Windows::Graphics::Capture::IGraphicsCaptureSessionStatics*
capture_session_statics;
if (FAILED(rohelper_->GetActivationFactory(
className,
__uuidof(
ABI::Windows::Graphics::Capture::IGraphicsCaptureSessionStatics),
(void**)&capture_session_statics))) {
return false;
}
boolean is_supported = false;
if (FAILED(capture_session_statics->IsSupported(&is_supported))) {
return false;
}
return !!is_supported;
}
std::optional<std::wstring> WebviewPlatform::GetDefaultDataDirectory() {
PWSTR path_tmp;
if (!SUCCEEDED(
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path_tmp))) {
return std::nullopt;
}
auto path = std::filesystem::path(path_tmp);
CoTaskMemFree(path_tmp);
wchar_t filename[MAX_PATH];
GetModuleFileName(nullptr, filename, MAX_PATH);
path /= "flutter_webview_windows";
path /= std::filesystem::path(filename).stem();
return path.wstring();
}
@@ -1,30 +0,0 @@
#pragma once
#include <winrt/base.h>
#include <memory>
#include <optional>
#include <string>
#include "graphics_context.h"
#include "util/rohelper.h"
class WebviewPlatform {
public:
WebviewPlatform();
bool IsSupported() { return valid_; }
std::optional<std::wstring> GetDefaultDataDirectory();
bool IsGraphicsCaptureSessionSupported();
GraphicsContext* graphics_context() const {
return graphics_context_.get();
};
rx::RoHelper* rohelper() const { return rohelper_.get(); }
private:
std::unique_ptr<rx::RoHelper> rohelper_;
winrt::com_ptr<ABI::Windows::System::IDispatcherQueueController>
dispatcher_queue_controller_;
std::unique_ptr<GraphicsContext> graphics_context_;
bool valid_ = false;
};
@@ -1,246 +0,0 @@
#include "include/webview_windows/webview_windows_plugin.h"
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>
#include <windows.h>
#include <memory>
#include <string>
#include <unordered_map>
#include "webview_bridge.h"
#include "webview_host.h"
#include "webview_platform.h"
#include "util/string_converter.h"
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d11.lib")
namespace {
constexpr auto kMethodInitialize = "initialize";
constexpr auto kMethodDispose = "dispose";
constexpr auto kMethodInitializeEnvironment = "initializeEnvironment";
constexpr auto kMethodGetWebViewVersion = "getWebViewVersion";
constexpr auto kErrorCodeInvalidId = "invalid_id";
constexpr auto kErrorCodeEnvironmentCreationFailed =
"environment_creation_failed";
constexpr auto kErrorCodeEnvironmentAlreadyInitialized =
"environment_already_initialized";
constexpr auto kErrorCodeWebviewCreationFailed = "webview_creation_failed";
constexpr auto kErrorUnsupportedPlatform = "unsupported_platform";
template <typename T>
std::optional<T> GetOptionalValue(const flutter::EncodableMap& map,
const std::string& key) {
const auto it = map.find(flutter::EncodableValue(key));
if (it != map.end()) {
const auto val = std::get_if<T>(&it->second);
if (val) {
return *val;
}
}
return std::nullopt;
}
class WebviewWindowsPlugin : public flutter::Plugin {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar);
WebviewWindowsPlugin(flutter::TextureRegistrar* textures,
flutter::BinaryMessenger* messenger);
virtual ~WebviewWindowsPlugin();
private:
std::unique_ptr<WebviewPlatform> platform_;
std::unique_ptr<WebviewHost> webview_host_;
std::unordered_map<int64_t, std::unique_ptr<WebviewBridge>> instances_;
WNDCLASS window_class_ = {};
flutter::TextureRegistrar* textures_;
flutter::BinaryMessenger* messenger_;
bool InitPlatform();
void CreateWebviewInstance(
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>);
// Called when a method is called on this plugin's channel from Dart.
void HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
};
// static
void WebviewWindowsPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarWindows* registrar) {
auto channel =
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "io.jns.webview.win",
&flutter::StandardMethodCodec::GetInstance());
auto plugin = std::make_unique<WebviewWindowsPlugin>(
registrar->texture_registrar(), registrar->messenger());
channel->SetMethodCallHandler(
[plugin_pointer = plugin.get()](const auto& call, auto result) {
plugin_pointer->HandleMethodCall(call, std::move(result));
});
registrar->AddPlugin(std::move(plugin));
}
WebviewWindowsPlugin::WebviewWindowsPlugin(flutter::TextureRegistrar* textures,
flutter::BinaryMessenger* messenger)
: textures_(textures), messenger_(messenger) {
window_class_.lpszClassName = L"FlutterWebviewMessage";
window_class_.lpfnWndProc = &DefWindowProc;
RegisterClass(&window_class_);
}
WebviewWindowsPlugin::~WebviewWindowsPlugin() {
instances_.clear();
UnregisterClass(window_class_.lpszClassName, nullptr);
}
void WebviewWindowsPlugin::HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
if (method_call.method_name().compare(kMethodInitializeEnvironment) == 0) {
if (webview_host_) {
return result->Error(kErrorCodeEnvironmentAlreadyInitialized,
"The webview environment is already initialized");
}
if (!InitPlatform()) {
return result->Error(kErrorUnsupportedPlatform,
"The platform is not supported");
}
const auto& map = std::get<flutter::EncodableMap>(*method_call.arguments());
std::optional<std::wstring> browser_exe_wpath = std::nullopt;
std::optional<std::string> browser_exe_path =
GetOptionalValue<std::string>(map, "browserExePath");
if (browser_exe_path) {
browser_exe_wpath = util::Utf16FromUtf8(*browser_exe_path);
}
std::optional<std::wstring> user_data_wpath = std::nullopt;
std::optional<std::string> user_data_path =
GetOptionalValue<std::string>(map, "userDataPath");
if (user_data_path) {
user_data_wpath = util::Utf16FromUtf8(*user_data_path);
} else {
user_data_wpath = platform_->GetDefaultDataDirectory();
}
std::optional<std::string> additional_args =
GetOptionalValue<std::string>(map, "additionalArguments");
webview_host_ = std::move(WebviewHost::Create(
platform_.get(), user_data_wpath, browser_exe_wpath, additional_args));
if (!webview_host_) {
return result->Error(kErrorCodeEnvironmentCreationFailed);
}
return result->Success();
}
if (method_call.method_name().compare(kMethodGetWebViewVersion) == 0) {
LPWSTR version_info = nullptr;
auto hr = GetAvailableCoreWebView2BrowserVersionString(nullptr, &version_info);
if (SUCCEEDED(hr) && version_info != nullptr) {
return result->Success(flutter::EncodableValue(util::Utf8FromUtf16(version_info)));
} else {
return result->Success();
}
}
if (method_call.method_name().compare(kMethodInitialize) == 0) {
return CreateWebviewInstance(std::move(result));
}
if (method_call.method_name().compare(kMethodDispose) == 0) {
if (const auto texture_id = std::get_if<int64_t>(method_call.arguments())) {
const auto it = instances_.find(*texture_id);
if (it != instances_.end()) {
instances_.erase(it);
return result->Success();
}
}
return result->Error(kErrorCodeInvalidId);
} else {
result->NotImplemented();
}
}
void WebviewWindowsPlugin::CreateWebviewInstance(
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
if (!InitPlatform()) {
return result->Error(kErrorUnsupportedPlatform,
"The platform is not supported");
}
if (!webview_host_) {
webview_host_ = std::move(WebviewHost::Create(
platform_.get(), platform_->GetDefaultDataDirectory()));
if (!webview_host_) {
return result->Error(kErrorCodeEnvironmentCreationFailed);
}
}
auto hwnd = CreateWindowEx(0, window_class_.lpszClassName, L"", 0, CW_DEFAULT,
CW_DEFAULT, 0, 0, HWND_MESSAGE, nullptr,
window_class_.hInstance, nullptr);
std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>
shared_result = std::move(result);
webview_host_->CreateWebview(
hwnd, true, true,
[shared_result, this](std::unique_ptr<Webview> webview,
std::unique_ptr<WebviewCreationError> error) {
if (!webview) {
if (error) {
return shared_result->Error(
kErrorCodeWebviewCreationFailed,
std::format(
"Creating the webview failed: {} (HRESULT: {:#010x})",
error->message, error->hr));
}
return shared_result->Error(kErrorCodeWebviewCreationFailed,
"Creating the webview failed.");
}
auto bridge = std::make_unique<WebviewBridge>(
messenger_, textures_, platform_->graphics_context(),
std::move(webview));
auto texture_id = bridge->texture_id();
instances_[texture_id] = std::move(bridge);
auto response = flutter::EncodableValue(flutter::EncodableMap{
{flutter::EncodableValue("textureId"),
flutter::EncodableValue(texture_id)},
});
shared_result->Success(response);
});
}
bool WebviewWindowsPlugin::InitPlatform() {
if (!platform_) {
platform_ = std::make_unique<WebviewPlatform>();
}
return platform_->IsSupported();
}
} // namespace
void WebviewWindowsPluginRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar) {
WebviewWindowsPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarManager::GetInstance()
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
}
Binary file not shown.
-17
View File
@@ -1,17 +0,0 @@
flutter/ephemeral/
# Visual Studio user-specific files.
*.suo
*.user
*.userosscache
*.sln.docstates
# Visual Studio build-related files.
x64/
x86/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
-108
View File
@@ -1,108 +0,0 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.14)
project(floating_mini_games LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "floating_mini_games")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(VERSION 3.14...3.25)
# Define build configuration option.
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTICONFIG)
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
CACHE STRING "" FORCE)
else()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
endif()
# Define settings for the Profile build mode.
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
# Use Unicode for all projects.
add_definitions(-DUNICODE -D_UNICODE)
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_17)
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
target_compile_options(${TARGET} PRIVATE /EHsc)
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
endfunction()
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build; see runner/CMakeLists.txt.
add_subdirectory("runner")
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# Support files are copied into place next to the executable, so that it can
# run in place. This is done instead of making a separate bundle (as on Linux)
# so that building and running from within Visual Studio will work.
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# Make the "install" step default, as it's required to run.
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Copy the native assets provided by the build.dart from all packages.
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/")
install(DIRECTORY "${NATIVE_ASSETS_DIR}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
CONFIGURATIONS Profile;Release
COMPONENT Runtime)
-109
View File
@@ -1,109 +0,0 @@
# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.14)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# Set fallback configurations for older versions of the flutter tool.
if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
set(FLUTTER_TARGET_PLATFORM "windows-x64")
endif()
# === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"flutter_export.h"
"flutter_windows.h"
"flutter_messenger.h"
"flutter_plugin_registrar.h"
"flutter_texture_registrar.h"
)
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
add_dependencies(flutter flutter_assemble)
# === Wrapper ===
list(APPEND CPP_WRAPPER_SOURCES_CORE
"core_implementations.cc"
"standard_codec.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
"plugin_registrar.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_APP
"flutter_engine.cc"
"flutter_view_controller.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
# Wrapper sources needed for a plugin.
add_library(flutter_wrapper_plugin STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
)
apply_standard_settings(flutter_wrapper_plugin)
set_target_properties(flutter_wrapper_plugin PROPERTIES
POSITION_INDEPENDENT_CODE ON)
set_target_properties(flutter_wrapper_plugin PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
target_include_directories(flutter_wrapper_plugin PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_plugin flutter_assemble)
# Wrapper sources needed for the runner.
add_library(flutter_wrapper_app STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_APP}
)
apply_standard_settings(flutter_wrapper_app)
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
target_include_directories(flutter_wrapper_app PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_app flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
${PHONY_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
${FLUTTER_TARGET_PLATFORM} $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
)
@@ -1,20 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
#include <webview_windows/webview_windows_plugin.h>
#include <window_manager/window_manager_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
WebviewWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WebviewWindowsPlugin"));
WindowManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
}
@@ -1,15 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter/plugin_registry.h>
// Registers Flutter plugins.
void RegisterPlugins(flutter::PluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_
@@ -1,26 +0,0 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
screen_retriever_windows
webview_windows
window_manager
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)
-40
View File
@@ -1,40 +0,0 @@
cmake_minimum_required(VERSION 3.14)
project(runner LANGUAGES CXX)
# Define the application target. To change its name, change BINARY_NAME in the
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
# work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp"
"main.cpp"
"utils.cpp"
"win32_window.cpp"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
"Runner.rc"
"runner.exe.manifest"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add preprocessor definitions for the build version.
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
# Disable Windows macros that collide with C++ standard library functions.
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
# Add dependency libraries and include directories. Add any application-specific
# dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
-119
View File
@@ -1,119 +0,0 @@
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include \"\"winres.h\"\"\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
IDI_APP_ICON ICON "resources\\app_icon.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
#else
#define VERSION_AS_NUMBER 1,0,0,0
#endif
#if defined(FLUTTER_VERSION)
#define VERSION_AS_STRING FLUTTER_VERSION
#else
#define VERSION_AS_STRING "1.0.0"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_AS_NUMBER
PRODUCTVERSION VERSION_AS_NUMBER
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "Bansonic" "\0"
VALUE "FileDescription", "浮动小游戏" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "floating_mini_games" "\0"
VALUE "LegalCopyright", "Copyright (C) 2026 Bansonic. All rights reserved." "\0"
VALUE "OriginalFilename", "浮动小游戏.exe" "\0"
VALUE "ProductName", "浮动小游戏" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
-71
View File
@@ -1,71 +0,0 @@
#include "flutter_window.h"
#include <optional>
#include "flutter/generated_plugin_registrant.h"
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
: project_(project) {}
FlutterWindow::~FlutterWindow() {}
bool FlutterWindow::OnCreate() {
if (!Win32Window::OnCreate()) {
return false;
}
RECT frame = GetClientArea();
// The size here must match the window dimensions to avoid unnecessary surface
// creation / destruction in the startup path.
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
frame.right - frame.left, frame.bottom - frame.top, project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false;
}
RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
flutter_controller_->engine()->SetNextFrameCallback([&]() {
this->Show();
});
// Flutter can complete the first frame before the "show window" callback is
// registered. The following call ensures a frame is pending to ensure the
// window is shown. It is a no-op if the first frame hasn't completed yet.
flutter_controller_->ForceRedraw();
return true;
}
void FlutterWindow::OnDestroy() {
if (flutter_controller_) {
flutter_controller_ = nullptr;
}
Win32Window::OnDestroy();
}
LRESULT
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
// Give Flutter, including plugins, an opportunity to handle window messages.
if (flutter_controller_) {
std::optional<LRESULT> result =
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
lparam);
if (result) {
return *result;
}
}
switch (message) {
case WM_FONTCHANGE:
flutter_controller_->engine()->ReloadSystemFonts();
break;
}
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
}
-33
View File
@@ -1,33 +0,0 @@
#ifndef RUNNER_FLUTTER_WINDOW_H_
#define RUNNER_FLUTTER_WINDOW_H_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <memory>
#include "win32_window.h"
// A window that does nothing but host a Flutter view.
class FlutterWindow : public Win32Window {
public:
// Creates a new FlutterWindow hosting a Flutter view running |project|.
explicit FlutterWindow(const flutter::DartProject& project);
virtual ~FlutterWindow();
protected:
// Win32Window:
bool OnCreate() override;
void OnDestroy() override;
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
LPARAM const lparam) noexcept override;
private:
// The project to run.
flutter::DartProject project_;
// The Flutter instance hosted by this window.
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
};
#endif // RUNNER_FLUTTER_WINDOW_H_
-36
View File
@@ -1,36 +0,0 @@
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>
#include "flutter_window.h"
#include "utils.h"
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
_In_ wchar_t* command_line, _In_ int show_command) {
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
CreateAndAttachConsole();
}
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
flutter::DartProject project(L"data");
std::vector<std::string> command_line_arguments = GetCommandLineArguments();
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.Create(L"\u6d6e\u52a8\u5c0f\u6e38\u620f", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);
::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
::CoUninitialize();
return EXIT_SUCCESS;
}
-16
View File
@@ -1,16 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Runner.rc
//
#define IDI_APP_ICON 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Binary file not shown.

Before

Width:  |  Height:  |  Size: 423 KiB

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>
-65
View File
@@ -1,65 +0,0 @@
#include "utils.h"
#include <flutter_windows.h>
#include <io.h>
#include <stdio.h>
#include <windows.h>
#include <iostream>
void CreateAndAttachConsole() {
if (::AllocConsole()) {
FILE *unused;
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
_dup2(_fileno(stdout), 2);
}
std::ios::sync_with_stdio();
FlutterDesktopResyncOutputStreams();
}
}
std::vector<std::string> GetCommandLineArguments() {
// Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
int argc;
wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
if (argv == nullptr) {
return std::vector<std::string>();
}
std::vector<std::string> command_line_arguments;
// Skip the first argument as it's the binary name.
for (int i = 1; i < argc; i++) {
command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
}
::LocalFree(argv);
return command_line_arguments;
}
std::string Utf8FromUtf16(const wchar_t* utf16_string) {
if (utf16_string == nullptr) {
return std::string();
}
unsigned int target_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, nullptr, 0, nullptr, nullptr)
-1; // remove the trailing null character
int input_length = (int)wcslen(utf16_string);
std::string utf8_string;
if (target_length == 0 || target_length > utf8_string.max_size()) {
return utf8_string;
}
utf8_string.resize(target_length);
int converted_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
input_length, utf8_string.data(), target_length, nullptr, nullptr);
if (converted_length == 0) {
return std::string();
}
return utf8_string;
}
-19
View File
@@ -1,19 +0,0 @@
#ifndef RUNNER_UTILS_H_
#define RUNNER_UTILS_H_
#include <string>
#include <vector>
// Creates a console for the process, and redirects stdout and stderr to
// it for both the runner and the Flutter library.
void CreateAndAttachConsole();
// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
// encoded in UTF-8. Returns an empty std::string on failure.
std::string Utf8FromUtf16(const wchar_t* utf16_string);
// Gets the command line arguments passed in as a std::vector<std::string>,
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
std::vector<std::string> GetCommandLineArguments();
#endif // RUNNER_UTILS_H_
-288
View File
@@ -1,288 +0,0 @@
#include "win32_window.h"
#include <dwmapi.h>
#include <flutter_windows.h>
#include "resource.h"
namespace {
/// Window attribute that enables dark mode window decorations.
///
/// Redefined in case the developer's machine has a Windows SDK older than
/// version 10.0.22000.0.
/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
/// Registry key for app theme preference.
///
/// A value of 0 indicates apps should use dark mode. A non-zero or missing
/// value indicates apps should use light mode.
constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
// The number of Win32Window objects that currently exist.
static int g_active_window_count = 0;
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
// Scale helper to convert logical scaler values to physical using passed in
// scale factor
int Scale(int source, double scale_factor) {
return static_cast<int>(source * scale_factor);
}
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
// This API is only needed for PerMonitor V1 awareness mode.
void EnableFullDpiSupportIfAvailable(HWND hwnd) {
HMODULE user32_module = LoadLibraryA("User32.dll");
if (!user32_module) {
return;
}
auto enable_non_client_dpi_scaling =
reinterpret_cast<EnableNonClientDpiScaling*>(
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
if (enable_non_client_dpi_scaling != nullptr) {
enable_non_client_dpi_scaling(hwnd);
}
FreeLibrary(user32_module);
}
} // namespace
// Manages the Win32Window's window class registration.
class WindowClassRegistrar {
public:
~WindowClassRegistrar() = default;
// Returns the singleton registrar instance.
static WindowClassRegistrar* GetInstance() {
if (!instance_) {
instance_ = new WindowClassRegistrar();
}
return instance_;
}
// Returns the name of the window class, registering the class if it hasn't
// previously been registered.
const wchar_t* GetWindowClass();
// Unregisters the window class. Should only be called if there are no
// instances of the window.
void UnregisterWindowClass();
private:
WindowClassRegistrar() = default;
static WindowClassRegistrar* instance_;
bool class_registered_ = false;
};
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
const wchar_t* WindowClassRegistrar::GetWindowClass() {
if (!class_registered_) {
WNDCLASS window_class{};
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.lpszClassName = kWindowClassName;
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc;
RegisterClass(&window_class);
class_registered_ = true;
}
return kWindowClassName;
}
void WindowClassRegistrar::UnregisterWindowClass() {
UnregisterClass(kWindowClassName, nullptr);
class_registered_ = false;
}
Win32Window::Win32Window() {
++g_active_window_count;
}
Win32Window::~Win32Window() {
--g_active_window_count;
Destroy();
}
bool Win32Window::Create(const std::wstring& title,
const Point& origin,
const Size& size) {
Destroy();
const wchar_t* window_class =
WindowClassRegistrar::GetInstance()->GetWindowClass();
const POINT target_point = {static_cast<LONG>(origin.x),
static_cast<LONG>(origin.y)};
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
double scale_factor = dpi / 96.0;
HWND window = CreateWindow(
window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
nullptr, nullptr, GetModuleHandle(nullptr), this);
if (!window) {
return false;
}
UpdateTheme(window);
return OnCreate();
}
bool Win32Window::Show() {
return ShowWindow(window_handle_, SW_SHOWNORMAL);
}
// static
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
if (message == WM_NCCREATE) {
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
EnableFullDpiSupportIfAvailable(window);
that->window_handle_ = window;
} else if (Win32Window* that = GetThisFromHandle(window)) {
return that->MessageHandler(window, message, wparam, lparam);
}
return DefWindowProc(window, message, wparam, lparam);
}
LRESULT
Win32Window::MessageHandler(HWND hwnd,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
switch (message) {
case WM_DESTROY:
window_handle_ = nullptr;
Destroy();
if (quit_on_close_) {
PostQuitMessage(0);
}
return 0;
case WM_DPICHANGED: {
auto newRectSize = reinterpret_cast<RECT*>(lparam);
LONG newWidth = newRectSize->right - newRectSize->left;
LONG newHeight = newRectSize->bottom - newRectSize->top;
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
case WM_SIZE: {
RECT rect = GetClientArea();
if (child_content_ != nullptr) {
// Size and position the child window.
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, TRUE);
}
return 0;
}
case WM_ACTIVATE:
if (child_content_ != nullptr) {
SetFocus(child_content_);
}
return 0;
case WM_DWMCOLORIZATIONCOLORCHANGED:
UpdateTheme(hwnd);
return 0;
}
return DefWindowProc(window_handle_, message, wparam, lparam);
}
void Win32Window::Destroy() {
OnDestroy();
if (window_handle_) {
DestroyWindow(window_handle_);
window_handle_ = nullptr;
}
if (g_active_window_count == 0) {
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
}
}
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
return reinterpret_cast<Win32Window*>(
GetWindowLongPtr(window, GWLP_USERDATA));
}
void Win32Window::SetChildContent(HWND content) {
child_content_ = content;
SetParent(content, window_handle_);
RECT frame = GetClientArea();
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
frame.bottom - frame.top, true);
SetFocus(child_content_);
}
RECT Win32Window::GetClientArea() {
RECT frame;
GetClientRect(window_handle_, &frame);
return frame;
}
HWND Win32Window::GetHandle() {
return window_handle_;
}
void Win32Window::SetQuitOnClose(bool quit_on_close) {
quit_on_close_ = quit_on_close;
}
bool Win32Window::OnCreate() {
// No-op; provided for subclasses.
return true;
}
void Win32Window::OnDestroy() {
// No-op; provided for subclasses.
}
void Win32Window::UpdateTheme(HWND const window) {
DWORD light_mode;
DWORD light_mode_size = sizeof(light_mode);
LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
kGetPreferredBrightnessRegValue,
RRF_RT_REG_DWORD, nullptr, &light_mode,
&light_mode_size);
if (result == ERROR_SUCCESS) {
BOOL enable_dark_mode = light_mode == 0;
DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
&enable_dark_mode, sizeof(enable_dark_mode));
}
}
-102
View File
@@ -1,102 +0,0 @@
#ifndef RUNNER_WIN32_WINDOW_H_
#define RUNNER_WIN32_WINDOW_H_
#include <windows.h>
#include <functional>
#include <memory>
#include <string>
// A class abstraction for a high DPI-aware Win32 Window. Intended to be
// inherited from by classes that wish to specialize with custom
// rendering and input handling
class Win32Window {
public:
struct Point {
unsigned int x;
unsigned int y;
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
};
struct Size {
unsigned int width;
unsigned int height;
Size(unsigned int width, unsigned int height)
: width(width), height(height) {}
};
Win32Window();
virtual ~Win32Window();
// Creates a win32 window with |title| that is positioned and sized using
// |origin| and |size|. New windows are created on the default monitor. Window
// sizes are specified to the OS in physical pixels, hence to ensure a
// consistent size this function will scale the inputted width and height as
// as appropriate for the default monitor. The window is invisible until
// |Show| is called. Returns true if the window was created successfully.
bool Create(const std::wstring& title, const Point& origin, const Size& size);
// Show the current window. Returns true if the window was successfully shown.
bool Show();
// Release OS resources associated with window.
void Destroy();
// Inserts |content| into the window tree.
void SetChildContent(HWND content);
// Returns the backing Window handle to enable clients to set icon and other
// window properties. Returns nullptr if the window has been destroyed.
HWND GetHandle();
// If true, closing this window will quit the application.
void SetQuitOnClose(bool quit_on_close);
// Return a RECT representing the bounds of the current client area.
RECT GetClientArea();
protected:
// Processes and route salient window messages for mouse handling,
// size change and DPI. Delegates handling of these to member overloads that
// inheriting classes can handle.
virtual LRESULT MessageHandler(HWND window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Called when CreateAndShow is called, allowing subclass window-related
// setup. Subclasses should return false if setup fails.
virtual bool OnCreate();
// Called when Destroy is called.
virtual void OnDestroy();
private:
friend class WindowClassRegistrar;
// OS callback called by message pump. Handles the WM_NCCREATE message which
// is passed when the non-client area is being created and enables automatic
// non-client DPI scaling so that the non-client area automatically
// responds to changes in DPI. All other messages are handled by
// MessageHandler.
static LRESULT CALLBACK WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Retrieves a class instance pointer for |window|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
// Update the window frame's theme to match the system theme.
static void UpdateTheme(HWND const window);
bool quit_on_close_ = false;
// window handle for top level window.
HWND window_handle_ = nullptr;
// window handle for hosted content.
HWND child_content_ = nullptr;
};
#endif // RUNNER_WIN32_WINDOW_H_