windows: added inappwebview widget support, added some other methods
This commit is contained in:
parent
11659f22e1
commit
914316e6d3
@ -71,41 +71,6 @@ PointerInterceptor myDrawer({required BuildContext context}) {
|
||||
},
|
||||
)
|
||||
];
|
||||
} else if (defaultTargetPlatform == TargetPlatform.macOS ||
|
||||
defaultTargetPlatform == TargetPlatform.windows ||
|
||||
defaultTargetPlatform == TargetPlatform.linux) {
|
||||
children = [
|
||||
// ListTile(
|
||||
// title: Text('InAppWebView'),
|
||||
// onTap: () {
|
||||
// Navigator.pushReplacementNamed(context, '/');
|
||||
// },
|
||||
// ),
|
||||
// ListTile(
|
||||
// title: Text('InAppBrowser'),
|
||||
// onTap: () {
|
||||
// Navigator.pushReplacementNamed(context, '/InAppBrowser');
|
||||
// },
|
||||
// ),
|
||||
ListTile(
|
||||
title: Text('InAppBrowser'),
|
||||
onTap: () {
|
||||
Navigator.pushReplacementNamed(context, '/');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text('WebAuthenticationSession'),
|
||||
onTap: () {
|
||||
Navigator.pushReplacementNamed(context, '/WebAuthenticationSession');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text('HeadlessInAppWebView'),
|
||||
onTap: () {
|
||||
Navigator.pushReplacementNamed(context, '/HeadlessInAppWebView');
|
||||
},
|
||||
),
|
||||
];
|
||||
} else if (defaultTargetPlatform == TargetPlatform.macOS) {
|
||||
children = [
|
||||
// ListTile(
|
||||
@ -142,22 +107,16 @@ PointerInterceptor myDrawer({required BuildContext context}) {
|
||||
} else if (defaultTargetPlatform == TargetPlatform.windows ||
|
||||
defaultTargetPlatform == TargetPlatform.linux) {
|
||||
children = [
|
||||
// ListTile(
|
||||
// title: Text('InAppWebView'),
|
||||
// onTap: () {
|
||||
// Navigator.pushReplacementNamed(context, '/');
|
||||
// },
|
||||
// ),
|
||||
// ListTile(
|
||||
// title: Text('InAppBrowser'),
|
||||
// onTap: () {
|
||||
// Navigator.pushReplacementNamed(context, '/InAppBrowser');
|
||||
// },
|
||||
// ),
|
||||
ListTile(
|
||||
title: Text('InAppWebView'),
|
||||
onTap: () {
|
||||
Navigator.pushReplacementNamed(context, '/');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text('InAppBrowser'),
|
||||
onTap: () {
|
||||
Navigator.pushReplacementNamed(context, '/');
|
||||
Navigator.pushReplacementNamed(context, '/InAppBrowser');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
@ -219,12 +178,11 @@ class _MyAppState extends State<MyApp> {
|
||||
'/WebAuthenticationSession': (context) =>
|
||||
WebAuthenticationSessionExampleScreen(),
|
||||
});
|
||||
} else if (defaultTargetPlatform == TargetPlatform.windows ||
|
||||
} else if (defaultTargetPlatform == TargetPlatform.windows ||
|
||||
defaultTargetPlatform == TargetPlatform.linux) {
|
||||
return MaterialApp(initialRoute: '/', routes: {
|
||||
// '/': (context) => InAppWebViewExampleScreen(),
|
||||
// '/InAppBrowser': (context) => InAppBrowserExampleScreen(),
|
||||
'/': (context) => InAppBrowserExampleScreen(),
|
||||
'/': (context) => InAppWebViewExampleScreen(),
|
||||
'/InAppBrowser': (context) => InAppBrowserExampleScreen(),
|
||||
'/HeadlessInAppWebView': (context) =>
|
||||
HeadlessInAppWebViewExampleScreen(),
|
||||
});
|
||||
|
@ -372,6 +372,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- MacOS
|
||||
///- Windows
|
||||
///{@endtemplate}
|
||||
Future<void> show() {
|
||||
throw UnimplementedError('show is not implemented on the current platform');
|
||||
@ -384,6 +385,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- MacOS
|
||||
///- Windows
|
||||
///{@endtemplate}
|
||||
Future<void> hide() {
|
||||
throw UnimplementedError('hide is not implemented on the current platform');
|
||||
@ -396,6 +398,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- MacOS
|
||||
///- Windows
|
||||
///{@endtemplate}
|
||||
Future<void> close() {
|
||||
throw UnimplementedError(
|
||||
@ -409,6 +412,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- MacOS
|
||||
///- Windows
|
||||
///{@endtemplate}
|
||||
Future<bool> isHidden() {
|
||||
throw UnimplementedError(
|
||||
@ -964,6 +968,7 @@ abstract class PlatformInAppBrowserEvents {
|
||||
///- Android native WebView ([Official API - WebChromeClient.onReceivedTitle](https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTitle(android.webkit.WebView,%20java.lang.String)))
|
||||
///- iOS
|
||||
///- MacOS
|
||||
///- Windows ([Official API - IWebView2WebView.add_DocumentTitleChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_documenttitlechanged))
|
||||
void onTitleChanged(String? title) {}
|
||||
|
||||
///Event fired to respond to the results of an over-scroll operation.
|
||||
|
@ -134,7 +134,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface
|
||||
///- iOS ([Official API - WKWebView.url](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url))
|
||||
///- MacOS ([Official API - WKWebView.url](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url))
|
||||
///- Web
|
||||
///- Windows ([Official API - IWebView2WebView.get_Source](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#get_source))
|
||||
///- Windows ([Official API - IWebView2WebView.get_Source](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_source))
|
||||
///{@endtemplate}
|
||||
Future<WebUri?> getUrl() {
|
||||
throw UnimplementedError(
|
||||
@ -151,6 +151,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface
|
||||
///- iOS ([Official API - WKWebView.title](https://developer.apple.com/documentation/webkit/wkwebview/1415015-title))
|
||||
///- MacOS ([Official API - WKWebView.title](https://developer.apple.com/documentation/webkit/wkwebview/1415015-title))
|
||||
///- Web
|
||||
///- Windows ([Official API - IWebView2WebView.get_DocumentTitle](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_documenttitle))
|
||||
///{@endtemplate}
|
||||
Future<String?> getTitle() {
|
||||
throw UnimplementedError(
|
||||
@ -345,6 +346,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface
|
||||
///- iOS ([Official API - WKWebView.reload](https://developer.apple.com/documentation/webkit/wkwebview/1414969-reload))
|
||||
///- MacOS ([Official API - WKWebView.reload](https://developer.apple.com/documentation/webkit/wkwebview/1414969-reload))
|
||||
///- Web ([Official API - Location.reload](https://developer.mozilla.org/en-US/docs/Web/API/Location/reload))
|
||||
///- Windows ([Official API - IWebView2WebView.Reload](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#reload))
|
||||
///{@endtemplate}
|
||||
Future<void> reload() {
|
||||
throw UnimplementedError(
|
||||
@ -361,6 +363,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface
|
||||
///- iOS ([Official API - WKWebView.goBack](https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback))
|
||||
///- MacOS ([Official API - WKWebView.goBack](https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback))
|
||||
///- Web ([Official API - History.back](https://developer.mozilla.org/en-US/docs/Web/API/History/back))
|
||||
///- Windows ([Official API - IWebView2WebView.GoBack](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#goback))
|
||||
///{@endtemplate}
|
||||
Future<void> goBack() {
|
||||
throw UnimplementedError(
|
||||
@ -390,6 +393,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface
|
||||
///- iOS ([Official API - WKWebView.goForward](https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward))
|
||||
///- MacOS ([Official API - WKWebView.goForward](https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward))
|
||||
///- Web ([Official API - History.forward](https://developer.mozilla.org/en-US/docs/Web/API/History/forward))
|
||||
///- Windows ([Official API - IWebView2WebView.GoForward](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#goforward))
|
||||
///{@endtemplate}
|
||||
Future<void> goForward() {
|
||||
throw UnimplementedError(
|
||||
@ -492,7 +496,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface
|
||||
///Those changes remain visible to all scripts, regardless of which content world you specify.
|
||||
///For more information about content worlds, see [ContentWorld].
|
||||
///Available on iOS 14.0+ and MacOS 11.0+.
|
||||
///**NOTE**: not used on Web.
|
||||
///**NOTE**: not used on Web and on Windows platforms.
|
||||
///
|
||||
///**NOTE**: This method shouldn't be called in the [PlatformWebViewCreationParams.onWebViewCreated] or [PlatformWebViewCreationParams.onLoadStart] events,
|
||||
///because, in these events, the `WebView` is not ready to handle it yet.
|
||||
@ -506,6 +510,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface
|
||||
///- iOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript))
|
||||
///- MacOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript))
|
||||
///- Web ([Official API - Window.eval](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval?retiredLocale=it))
|
||||
///- Windows ([Official API - IWebView2WebView.ExecuteScript](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#executescript))
|
||||
///{@endtemplate}
|
||||
Future<dynamic> evaluateJavascript(
|
||||
{required String source, ContentWorld? contentWorld}) {
|
||||
|
@ -192,6 +192,7 @@ class PlatformInAppWebViewWidgetCreationParams
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- Web
|
||||
///- Windows
|
||||
///{@endtemplate}
|
||||
abstract class PlatformInAppWebViewWidget extends PlatformInterface
|
||||
implements Disposable {
|
||||
|
@ -152,7 +152,7 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController {
|
||||
|
||||
final setCookieCompleter = Completer<void>();
|
||||
final headlessWebView =
|
||||
MacOSHeadlessInAppWebView(MacOSHeadlessInAppWebViewCreationParams(
|
||||
WindowsHeadlessInAppWebView(WindowsHeadlessInAppWebViewCreationParams(
|
||||
initialUrlRequest: URLRequest(url: url),
|
||||
onLoadStop: (controller, url) async {
|
||||
await controller.evaluateJavascript(
|
||||
@ -234,7 +234,7 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController {
|
||||
|
||||
final pageLoaded = Completer<void>();
|
||||
final headlessWebView =
|
||||
MacOSHeadlessInAppWebView(MacOSHeadlessInAppWebViewCreationParams(
|
||||
WindowsHeadlessInAppWebView(WindowsHeadlessInAppWebViewCreationParams(
|
||||
initialUrlRequest: URLRequest(url: url),
|
||||
onLoadStop: (controller, url) async {
|
||||
pageLoaded.complete();
|
||||
|
@ -0,0 +1,414 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.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;
|
||||
|
||||
/// Pointer button type
|
||||
// Order must match InAppWebViewPointerEventKind (see in_app_webview.h)
|
||||
enum PointerButton { none, primary, secondary, tertiary }
|
||||
|
||||
/// Pointer Event kind
|
||||
// Order must match InAppWebViewPointerEventKind (see in_app_webview.h)
|
||||
enum InAppWebViewPointerEventKind { activate, down, enter, leave, up, update }
|
||||
|
||||
/// 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 = 'com.pichillilorenzo/flutter_inappwebview';
|
||||
const MethodChannel _pluginChannel = MethodChannel(_pluginChannelPrefix);
|
||||
|
||||
class CustomFlutterViewControllerValue {
|
||||
const CustomFlutterViewControllerValue({
|
||||
required this.isInitialized,
|
||||
});
|
||||
|
||||
final bool isInitialized;
|
||||
|
||||
CustomFlutterViewControllerValue copyWith({
|
||||
bool? isInitialized,
|
||||
}) {
|
||||
return CustomFlutterViewControllerValue(
|
||||
isInitialized: isInitialized ?? this.isInitialized,
|
||||
);
|
||||
}
|
||||
|
||||
CustomFlutterViewControllerValue.uninitialized()
|
||||
: this(
|
||||
isInitialized: false,
|
||||
);
|
||||
}
|
||||
|
||||
/// Controls a WebView and provides streams for various change events.
|
||||
class CustomPlatformViewController
|
||||
extends ValueNotifier<CustomFlutterViewControllerValue> {
|
||||
Completer<void> _creatingCompleter = Completer<void>();
|
||||
int _textureId = 0;
|
||||
bool _isDisposed = false;
|
||||
|
||||
Future<void> get ready => _creatingCompleter.future;
|
||||
|
||||
late MethodChannel _methodChannel;
|
||||
late EventChannel _eventChannel;
|
||||
StreamSubscription? _eventStreamSubscription;
|
||||
|
||||
final StreamController<SystemMouseCursor> _cursorStreamController =
|
||||
StreamController<SystemMouseCursor>.broadcast();
|
||||
|
||||
/// A stream reflecting the current cursor style.
|
||||
Stream<SystemMouseCursor> get _cursor => _cursorStreamController.stream;
|
||||
|
||||
CustomPlatformViewController()
|
||||
: super(CustomFlutterViewControllerValue.uninitialized());
|
||||
|
||||
/// Initializes the underlying platform view.
|
||||
Future<void> initialize(
|
||||
{Function(int id)? onPlatformViewCreated, dynamic arguments}) async {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
_textureId = (await _pluginChannel.invokeMethod<int>(
|
||||
'createInAppWebView', arguments))!;
|
||||
|
||||
_methodChannel =
|
||||
MethodChannel('com.pichillilorenzo/custom_platform_view_$_textureId');
|
||||
_eventChannel =
|
||||
EventChannel('com.pichillilorenzo/custom_platform_view_${_textureId}_events');
|
||||
_eventStreamSubscription =
|
||||
_eventChannel.receiveBroadcastStream().listen((event) {
|
||||
final map = event as Map<dynamic, dynamic>;
|
||||
switch (map['type']) {
|
||||
case 'cursorChanged':
|
||||
_cursorStreamController.add(_getCursorByName(map['value']));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
_methodChannel.setMethodCallHandler((call) {
|
||||
throw MissingPluginException('Unknown method ${call.method}');
|
||||
});
|
||||
|
||||
value = value.copyWith(isInitialized: true);
|
||||
|
||||
_creatingCompleter.complete();
|
||||
|
||||
onPlatformViewCreated?.call(_textureId);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
await _creatingCompleter.future;
|
||||
if (!_isDisposed) {
|
||||
_isDisposed = true;
|
||||
await _eventStreamSubscription?.cancel();
|
||||
await _pluginChannel.invokeMethod('dispose', {"id": _textureId});
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// 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(InAppWebViewPointerEventKind 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 CustomPlatformView extends StatefulWidget {
|
||||
/// 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;
|
||||
|
||||
final dynamic creationParams;
|
||||
|
||||
final Function(int id)? onPlatformViewCreated;
|
||||
|
||||
const CustomPlatformView(
|
||||
{this.creationParams,
|
||||
this.onPlatformViewCreated,
|
||||
this.scaleFactor,
|
||||
this.filterQuality = FilterQuality.none});
|
||||
|
||||
@override
|
||||
_CustomPlatformViewState createState() => _CustomPlatformViewState();
|
||||
}
|
||||
|
||||
class _CustomPlatformViewState extends State<CustomPlatformView> {
|
||||
final GlobalKey _key = GlobalKey();
|
||||
final _downButtons = <int, PointerButton>{};
|
||||
|
||||
PointerDeviceKind _pointerKind = PointerDeviceKind.unknown;
|
||||
|
||||
MouseCursor _cursor = SystemMouseCursors.basic;
|
||||
|
||||
final _controller = CustomPlatformViewController();
|
||||
final _focusNode = FocusNode();
|
||||
|
||||
StreamSubscription? _cursorSubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_controller.initialize(
|
||||
onPlatformViewCreated: (id) {
|
||||
widget.onPlatformViewCreated?.call(id);
|
||||
setState(() {});
|
||||
},
|
||||
arguments: widget.creationParams);
|
||||
|
||||
// Report initial surface size
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _reportSurfaceSize());
|
||||
|
||||
_cursorSubscription = _controller._cursor.listen((cursor) {
|
||||
setState(() {
|
||||
_cursor = cursor;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Focus(
|
||||
autofocus: true,
|
||||
focusNode: _focusNode,
|
||||
canRequestFocus: true,
|
||||
debugLabel: "flutter_inappwebview_windows_custom_platform_view",
|
||||
onFocusChange: (focused) {
|
||||
|
||||
},
|
||||
child: 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) {
|
||||
if (!_focusNode.hasFocus) {
|
||||
_focusNode.requestFocus();
|
||||
Future.delayed(const Duration(milliseconds: 50), () {
|
||||
if (!_focusNode.hasFocus) {
|
||||
_focusNode.requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_pointerKind = ev.kind;
|
||||
if (ev.kind == PointerDeviceKind.touch) {
|
||||
_controller._setPointerUpdate(
|
||||
InAppWebViewPointerEventKind.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(
|
||||
InAppWebViewPointerEventKind.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(
|
||||
InAppWebViewPointerEventKind.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: (ev) {
|
||||
_controller._setScrollDelta(
|
||||
ev.panDelta.dx, ev.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();
|
||||
_controller.dispose();
|
||||
_focusNode.dispose();
|
||||
}
|
||||
}
|
@ -6,16 +6,16 @@ import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_pla
|
||||
import '../find_interaction/find_interaction_controller.dart';
|
||||
import 'in_app_webview_controller.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSHeadlessInAppWebView].
|
||||
/// Object specifying creation parameters for creating a [WindowsHeadlessInAppWebView].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformHeadlessInAppWebViewCreationParams] for
|
||||
/// more information.
|
||||
@immutable
|
||||
class MacOSHeadlessInAppWebViewCreationParams
|
||||
class WindowsHeadlessInAppWebViewCreationParams
|
||||
extends PlatformHeadlessInAppWebViewCreationParams {
|
||||
/// Creates a new [MacOSHeadlessInAppWebViewCreationParams] instance.
|
||||
MacOSHeadlessInAppWebViewCreationParams(
|
||||
/// Creates a new [WindowsHeadlessInAppWebViewCreationParams] instance.
|
||||
WindowsHeadlessInAppWebViewCreationParams(
|
||||
{super.controllerFromPlatform,
|
||||
super.initialSize,
|
||||
super.windowId,
|
||||
@ -128,8 +128,8 @@ class MacOSHeadlessInAppWebViewCreationParams
|
||||
super.pullToRefreshController,
|
||||
this.findInteractionController});
|
||||
|
||||
/// Creates a [MacOSHeadlessInAppWebViewCreationParams] instance based on [PlatformHeadlessInAppWebViewCreationParams].
|
||||
MacOSHeadlessInAppWebViewCreationParams.fromPlatformHeadlessInAppWebViewCreationParams(
|
||||
/// Creates a [WindowsHeadlessInAppWebViewCreationParams] instance based on [PlatformHeadlessInAppWebViewCreationParams].
|
||||
WindowsHeadlessInAppWebViewCreationParams.fromPlatformHeadlessInAppWebViewCreationParams(
|
||||
PlatformHeadlessInAppWebViewCreationParams params)
|
||||
: this(
|
||||
controllerFromPlatform: params.controllerFromPlatform,
|
||||
@ -245,7 +245,7 @@ class MacOSHeadlessInAppWebViewCreationParams
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView}
|
||||
class MacOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView
|
||||
class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView
|
||||
with ChannelController {
|
||||
@override
|
||||
late final String id;
|
||||
@ -258,12 +258,12 @@ class MacOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView
|
||||
|
||||
WindowsInAppWebViewController? _webViewController;
|
||||
|
||||
/// Constructs a [MacOSHeadlessInAppWebView].
|
||||
MacOSHeadlessInAppWebView(PlatformHeadlessInAppWebViewCreationParams params)
|
||||
/// Constructs a [WindowsHeadlessInAppWebView].
|
||||
WindowsHeadlessInAppWebView(PlatformHeadlessInAppWebViewCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSHeadlessInAppWebViewCreationParams
|
||||
params is WindowsHeadlessInAppWebViewCreationParams
|
||||
? params
|
||||
: MacOSHeadlessInAppWebViewCreationParams
|
||||
: WindowsHeadlessInAppWebViewCreationParams
|
||||
.fromPlatformHeadlessInAppWebViewCreationParams(params),
|
||||
) {
|
||||
id = IdGenerator.generate();
|
||||
@ -274,8 +274,8 @@ class MacOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView
|
||||
|
||||
dynamic _controllerFromPlatform;
|
||||
|
||||
MacOSHeadlessInAppWebViewCreationParams get _macosParams =>
|
||||
params as MacOSHeadlessInAppWebViewCreationParams;
|
||||
WindowsHeadlessInAppWebViewCreationParams get _macosParams =>
|
||||
params as WindowsHeadlessInAppWebViewCreationParams;
|
||||
|
||||
_init() {
|
||||
_webViewController = WindowsInAppWebViewController(
|
||||
@ -425,7 +425,7 @@ class MacOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView
|
||||
}
|
||||
}
|
||||
|
||||
extension InternalHeadlessInAppWebView on MacOSHeadlessInAppWebView {
|
||||
extension InternalHeadlessInAppWebView on WindowsHeadlessInAppWebView {
|
||||
Future<void> internalDispose() async {
|
||||
_started = false;
|
||||
_running = false;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
import 'headless_in_app_webview.dart';
|
||||
@ -7,13 +6,15 @@ import 'headless_in_app_webview.dart';
|
||||
import '../find_interaction/find_interaction_controller.dart';
|
||||
import 'in_app_webview_controller.dart';
|
||||
|
||||
import 'custom_platform_view.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [PlatformInAppWebViewWidget].
|
||||
///
|
||||
/// Platform specific implementations can add additional fields by extending
|
||||
/// this class.
|
||||
class MacOSInAppWebViewWidgetCreationParams
|
||||
class WindowsInAppWebViewWidgetCreationParams
|
||||
extends PlatformInAppWebViewWidgetCreationParams {
|
||||
MacOSInAppWebViewWidgetCreationParams(
|
||||
WindowsInAppWebViewWidgetCreationParams(
|
||||
{super.controllerFromPlatform,
|
||||
super.key,
|
||||
super.layoutDirection,
|
||||
@ -131,9 +132,9 @@ class MacOSInAppWebViewWidgetCreationParams
|
||||
super.pullToRefreshController,
|
||||
this.findInteractionController});
|
||||
|
||||
/// Constructs a [MacOSInAppWebViewWidgetCreationParams] using a
|
||||
/// Constructs a [WindowsInAppWebViewWidgetCreationParams] using a
|
||||
/// [PlatformInAppWebViewWidgetCreationParams].
|
||||
MacOSInAppWebViewWidgetCreationParams.fromPlatformInAppWebViewWidgetCreationParams(
|
||||
WindowsInAppWebViewWidgetCreationParams.fromPlatformInAppWebViewWidgetCreationParams(
|
||||
PlatformInAppWebViewWidgetCreationParams params)
|
||||
: this(
|
||||
controllerFromPlatform: params.controllerFromPlatform,
|
||||
@ -254,25 +255,25 @@ class MacOSInAppWebViewWidgetCreationParams
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget}
|
||||
class MacOSInAppWebViewWidget extends PlatformInAppWebViewWidget {
|
||||
/// Constructs a [MacOSInAppWebViewWidget].
|
||||
class WindowsInAppWebViewWidget extends PlatformInAppWebViewWidget {
|
||||
/// Constructs a [WindowsInAppWebViewWidget].
|
||||
///
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget}
|
||||
MacOSInAppWebViewWidget(PlatformInAppWebViewWidgetCreationParams params)
|
||||
WindowsInAppWebViewWidget(PlatformInAppWebViewWidgetCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSInAppWebViewWidgetCreationParams
|
||||
params is WindowsInAppWebViewWidgetCreationParams
|
||||
? params
|
||||
: MacOSInAppWebViewWidgetCreationParams
|
||||
: WindowsInAppWebViewWidgetCreationParams
|
||||
.fromPlatformInAppWebViewWidgetCreationParams(params),
|
||||
);
|
||||
|
||||
MacOSInAppWebViewWidgetCreationParams get _macosParams =>
|
||||
params as MacOSInAppWebViewWidgetCreationParams;
|
||||
WindowsInAppWebViewWidgetCreationParams get _macosParams =>
|
||||
params as WindowsInAppWebViewWidgetCreationParams;
|
||||
|
||||
WindowsInAppWebViewController? _controller;
|
||||
|
||||
MacOSHeadlessInAppWebView? get _macosHeadlessInAppWebView =>
|
||||
params.headlessWebView as MacOSHeadlessInAppWebView?;
|
||||
WindowsHeadlessInAppWebView? get _macosHeadlessInAppWebView =>
|
||||
params.headlessWebView as WindowsHeadlessInAppWebView?;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -300,10 +301,8 @@ class MacOSInAppWebViewWidget extends PlatformInAppWebViewWidget {
|
||||
}
|
||||
}
|
||||
|
||||
return UiKitView(
|
||||
viewType: 'com.pichillilorenzo/flutter_inappwebview',
|
||||
return CustomPlatformView(
|
||||
onPlatformViewCreated: _onPlatformViewCreated,
|
||||
gestureRecognizers: params.gestureRecognizers,
|
||||
creationParams: <String, dynamic>{
|
||||
'initialUrlRequest': params.initialUrlRequest?.toMap(),
|
||||
'initialFile': params.initialFile,
|
||||
@ -320,7 +319,6 @@ class MacOSInAppWebViewWidget extends PlatformInAppWebViewWidget {
|
||||
'keepAliveId': params.keepAlive?.id,
|
||||
'preventGestureDelay': params.preventGestureDelay
|
||||
},
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ class WindowsInAppWebViewControllerCreationParams
|
||||
}
|
||||
}
|
||||
|
||||
///Controls a WebView, such as an [InAppWebView] widget instance, a [MacOSHeadlessInAppWebView] instance or [WindowsInAppBrowser] WebView instance.
|
||||
///Controls a WebView, such as an [InAppWebView] widget instance, a [WindowsHeadlessInAppWebView] instance or [WindowsInAppBrowser] WebView instance.
|
||||
///
|
||||
///If you are using the [InAppWebView] widget, an [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated]
|
||||
///callback. Instead, if you are using an [WindowsInAppBrowser] instance, you can get it through the [WindowsInAppBrowser.webViewController] attribute.
|
||||
@ -1923,6 +1923,13 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController
|
||||
args.putIfAbsent('source', () => source);
|
||||
args.putIfAbsent('contentWorld', () => contentWorld?.toMap());
|
||||
var data = await channel?.invokeMethod('evaluateJavascript', args);
|
||||
if (data != null) {
|
||||
try {
|
||||
// try to json decode the data coming from JavaScript
|
||||
// otherwise return it as it is.
|
||||
data = json.decode(data);
|
||||
} catch (e) {}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
import 'in_app_browser/in_app_browser.dart';
|
||||
import 'in_app_webview/in_app_webview.dart';
|
||||
import 'in_app_webview/in_app_webview_controller.dart';
|
||||
|
||||
/// Implementation of [InAppWebViewPlatform] using the WebKit API.
|
||||
class WindowsInAppWebViewPlatform extends InAppWebViewPlatform {
|
||||
@ -9,6 +11,37 @@ class WindowsInAppWebViewPlatform extends InAppWebViewPlatform {
|
||||
InAppWebViewPlatform.instance = WindowsInAppWebViewPlatform();
|
||||
}
|
||||
|
||||
/// Creates a new [WindowsInAppWebViewController].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [InAppWebViewController] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
WindowsInAppWebViewController createPlatformInAppWebViewController(
|
||||
PlatformInAppWebViewControllerCreationParams params,
|
||||
) {
|
||||
return WindowsInAppWebViewController(params);
|
||||
}
|
||||
|
||||
/// Creates a new empty [WindowsInAppWebViewController] to access static methods.
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [InAppWebViewController] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
WindowsInAppWebViewController createPlatformInAppWebViewControllerStatic() {
|
||||
return WindowsInAppWebViewController.static();
|
||||
}
|
||||
|
||||
/// Creates a new [WindowsInAppWebViewWidget].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [InAppWebView] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
WindowsInAppWebViewWidget createPlatformInAppWebViewWidget(
|
||||
PlatformInAppWebViewWidgetCreationParams params,
|
||||
) {
|
||||
return WindowsInAppWebViewWidget(params);
|
||||
}
|
||||
|
||||
/// Creates a new [WindowsInAppBrowser].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
@ -28,5 +61,4 @@ class WindowsInAppWebViewPlatform extends InAppWebViewPlatform {
|
||||
WindowsInAppBrowser createPlatformInAppBrowserStatic() {
|
||||
return WindowsInAppBrowser.static();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,16 +21,9 @@ cmake_policy(VERSION 3.14...3.25)
|
||||
# not be changed
|
||||
set(PLUGIN_NAME "flutter_inappwebview_windows_plugin")
|
||||
|
||||
set(NUGET_URL https://dist.nuget.org/win-x86-commandline/latest/nuget.exe)
|
||||
|
||||
find_program(NUGET nuget)
|
||||
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()
|
||||
endif()
|
||||
|
||||
add_custom_target(${PROJECT_NAME}_DEPENDENCIES_DOWNLOAD ALL)
|
||||
@ -61,10 +54,25 @@ list(APPEND PLUGIN_SOURCES
|
||||
"types/web_resource_request.h"
|
||||
"types/web_resource_response.cpp"
|
||||
"types/web_resource_response.h"
|
||||
"custom_platform_view/custom_platform_view.cc"
|
||||
"custom_platform_view/custom_platform_view.h"
|
||||
"custom_platform_view/texture_bridge.cc"
|
||||
"custom_platform_view/texture_bridge.h"
|
||||
"custom_platform_view/graphics_context.cc"
|
||||
"custom_platform_view/graphics_context.h"
|
||||
"custom_platform_view/util/direct3d11.interop.cc"
|
||||
"custom_platform_view/util/direct3d11.interop.h"
|
||||
"custom_platform_view/util/rohelper.cc"
|
||||
"custom_platform_view/util/rohelper.h"
|
||||
"custom_platform_view/util/string_converter.cc"
|
||||
"custom_platform_view/util/string_converter.h"
|
||||
"custom_platform_view/util/swizzle.h"
|
||||
"in_app_webview/in_app_webview_settings.cpp"
|
||||
"in_app_webview/in_app_webview_settings.h"
|
||||
"in_app_webview/in_app_webview.cpp"
|
||||
"in_app_webview/in_app_webview.h"
|
||||
"in_app_webview/in_app_webview_manager.cpp"
|
||||
"in_app_webview/in_app_webview_manager.h"
|
||||
"in_app_webview/webview_channel_delegate.cpp"
|
||||
"in_app_webview/webview_channel_delegate.h"
|
||||
"in_app_browser/in_app_browser_settings.cpp"
|
||||
@ -85,6 +93,29 @@ add_library(${PLUGIN_NAME} SHARED
|
||||
${PLUGIN_SOURCES}
|
||||
)
|
||||
|
||||
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
|
||||
"custom_platform_view/texture_bridge_gpu.cc"
|
||||
"custom_platform_view/texture_bridge_gpu.h"
|
||||
)
|
||||
else()
|
||||
message(STATUS "Building with fallback PixelBuffer texture.")
|
||||
target_sources("${PLUGIN_NAME}" PRIVATE
|
||||
"custom_platform_view/texture_bridge_fallback.cc"
|
||||
"custom_platform_view/texture_bridge_fallback.h"
|
||||
"custom_platform_view/util/cpuid/cpuinfo.cc"
|
||||
"custom_platform_view/util/cpuid/cpuinfo.h"
|
||||
)
|
||||
# Enable AVX2 for pixel buffer conversions
|
||||
if(MSVC)
|
||||
target_compile_options(${PLUGIN_NAME} PRIVATE "/arch:AVX2")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Apply a standard set of build settings that are configured in the
|
||||
# application-level CMakeLists.txt. This can be removed for plugins that want
|
||||
# full control over build settings.
|
||||
|
@ -0,0 +1,312 @@
|
||||
#include "custom_platform_view.h"
|
||||
|
||||
#include <flutter/event_stream_handler_functions.h>
|
||||
#include <flutter/method_result_functions.h>
|
||||
|
||||
#ifdef HAVE_FLUTTER_D3D_TEXTURE
|
||||
#include "texture_bridge_gpu.h"
|
||||
#else
|
||||
#include "texture_bridge_fallback.h"
|
||||
#endif
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
constexpr auto kErrorInvalidArgs = "invalidArguments";
|
||||
|
||||
constexpr auto kMethodSetSize = "setSize";
|
||||
constexpr auto kMethodSetCursorPos = "setCursorPos";
|
||||
constexpr auto kMethodSetPointerUpdate = "setPointerUpdate";
|
||||
constexpr auto kMethodSetPointerButton = "setPointerButton";
|
||||
constexpr auto kMethodSetScrollDelta = "setScrollDelta";
|
||||
constexpr auto kMethodSetFpsLimit = "setFpsLimit";
|
||||
|
||||
constexpr auto kEventType = "type";
|
||||
constexpr auto kEventValue = "value";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
CustomPlatformView::CustomPlatformView(flutter::BinaryMessenger* messenger,
|
||||
flutter::TextureRegistrar* texture_registrar,
|
||||
GraphicsContext* graphics_context,
|
||||
HWND hwnd,
|
||||
std::unique_ptr<flutter_inappwebview_plugin::InAppWebView> webView)
|
||||
: hwnd_(hwnd), view(std::move(webView)), texture_registrar_(texture_registrar)
|
||||
{
|
||||
#ifdef HAVE_FLUTTER_D3D_TEXTURE
|
||||
texture_bridge_ =
|
||||
std::make_unique<TextureBridgeGpu>(graphics_context, view->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) {
|
||||
// view->SetSurfaceSize(size.width, size.height);
|
||||
//});
|
||||
|
||||
const auto method_channel_name = "com.pichillilorenzo/custom_platform_view_" + std::to_string(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 = "com.pichillilorenzo/custom_platform_view_" + std::to_string(texture_id_) + "_events";
|
||||
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));
|
||||
}
|
||||
|
||||
CustomPlatformView::~CustomPlatformView()
|
||||
{
|
||||
debugLog("dealloc CustomPlatformView");
|
||||
method_channel_->SetMethodCallHandler(nullptr);
|
||||
texture_registrar_->UnregisterTexture(texture_id_);
|
||||
}
|
||||
|
||||
void CustomPlatformView::RegisterEventHandlers()
|
||||
{
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
view->onSurfaceSizeChanged([this](size_t width, size_t height)
|
||||
{
|
||||
texture_bridge_->NotifySurfaceSizeChanged();
|
||||
});
|
||||
|
||||
view->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);
|
||||
});
|
||||
}
|
||||
|
||||
void CustomPlatformView::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 && view) {
|
||||
view->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 && view) {
|
||||
view->setPointerUpdate(*pointer,
|
||||
static_cast<flutter_inappwebview_plugin::InAppWebViewPointerEventKind>(*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 && view) {
|
||||
view->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 && view) {
|
||||
view->setPointerButtonState(
|
||||
static_cast<flutter_inappwebview_plugin::InAppWebViewPointerButton>(*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 && view) {
|
||||
const auto [width, height, scale_factor] = size.value();
|
||||
|
||||
view->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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
#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 "../in_app_webview/in_app_webview.h"
|
||||
#include "graphics_context.h"
|
||||
#include "texture_bridge.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
class CustomPlatformView {
|
||||
public:
|
||||
static inline const wchar_t* CLASS_NAME = L"CustomPlatformView";
|
||||
|
||||
const std::unique_ptr<flutter_inappwebview_plugin::InAppWebView> view;
|
||||
|
||||
CustomPlatformView(flutter::BinaryMessenger* messenger,
|
||||
flutter::TextureRegistrar* texture_registrar,
|
||||
GraphicsContext* graphics_context,
|
||||
HWND hwnd,
|
||||
std::unique_ptr<flutter_inappwebview_plugin::InAppWebView> webView);
|
||||
~CustomPlatformView();
|
||||
|
||||
TextureBridge* texture_bridge() const { return texture_bridge_.get(); }
|
||||
|
||||
int64_t texture_id() const { return texture_id_; }
|
||||
private:
|
||||
HWND hwnd_;
|
||||
std::unique_ptr<flutter::TextureVariant> flutter_texture_;
|
||||
std::unique_ptr<TextureBridge> texture_bridge_;
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
#include "graphics_context.h"
|
||||
|
||||
#include "util/d3dutil.h"
|
||||
#include "util/direct3d11.interop.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
GraphicsContext::GraphicsContext(rx::RoHelper* rohelper) : rohelper_(rohelper)
|
||||
{
|
||||
device_ = CreateD3DDevice();
|
||||
if (!device_) {
|
||||
return;
|
||||
}
|
||||
|
||||
device_->GetImmediateContext(device_context_.put());
|
||||
if (FAILED(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;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <D3d11.h>
|
||||
#include <windows.graphics.capture.h>
|
||||
#include <windows.ui.composition.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
|
||||
#include "util/rohelper.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
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 };
|
||||
};
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
#include "texture_bridge.h"
|
||||
|
||||
#include <windows.foundation.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#include "util/direct3d11.interop.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
const int kNumBuffers = 1;
|
||||
|
||||
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_ =
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.graphics.capture.h>
|
||||
#include <wrl.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
#include "graphics_context.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
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;
|
||||
};
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
#include "texture_bridge_fallback.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "util/direct3d11.interop.h"
|
||||
#include "util/swizzle.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <flutter/texture_registrar.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "texture_bridge.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
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);
|
||||
};
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
#include "texture_bridge_gpu.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "util/direct3d11.interop.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <flutter/texture_registrar.h>
|
||||
|
||||
#include "texture_bridge.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
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);
|
||||
};
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <winrt/base.h>
|
||||
|
||||
#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
|
@ -0,0 +1,80 @@
|
||||
#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
|
@ -0,0 +1,105 @@
|
||||
#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
|
@ -0,0 +1,69 @@
|
||||
#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
|
@ -0,0 +1,43 @@
|
||||
#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
|
@ -0,0 +1,36 @@
|
||||
#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
|
@ -0,0 +1,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cpuinfo_impl.h"
|
||||
|
||||
namespace cpuid {
|
||||
|
||||
void init_cpuinfo(cpuinfo::impl& info) { (void)info; }
|
||||
} // namespace cpuid
|
@ -0,0 +1,31 @@
|
||||
#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;
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
#include "direct3d11.interop.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
|
||||
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
|
@ -0,0 +1,51 @@
|
||||
#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 flutter_inappwebview_plugin
|
||||
{
|
||||
|
||||
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
|
@ -0,0 +1,244 @@
|
||||
// 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
|
@ -0,0 +1,97 @@
|
||||
// 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
|
@ -0,0 +1,54 @@
|
||||
#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
|
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace util {
|
||||
std::string Utf8FromUtf16(std::wstring_view utf16_string);
|
||||
std::wstring Utf16FromUtf8(std::string_view utf8_string);
|
||||
} // namespace util
|
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -4,6 +4,11 @@
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
|
||||
#include "in_app_browser/in_app_browser_manager.h"
|
||||
#include "in_app_webview/in_app_webview_manager.h"
|
||||
|
||||
#pragma comment(lib, "Shlwapi.lib")
|
||||
#pragma comment(lib, "dxgi.lib")
|
||||
#pragma comment(lib, "d3d11.lib")
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
@ -18,6 +23,7 @@ namespace flutter_inappwebview_plugin
|
||||
FlutterInappwebviewWindowsPlugin::FlutterInappwebviewWindowsPlugin(flutter::PluginRegistrarWindows* registrar)
|
||||
: registrar(registrar)
|
||||
{
|
||||
inAppWebViewManager = std::make_unique<InAppWebViewManager>(this);
|
||||
inAppBrowserManager = std::make_unique<InAppBrowserManager>(this);
|
||||
}
|
||||
|
||||
|
@ -6,11 +6,13 @@
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
class InAppWebViewManager;
|
||||
class InAppBrowserManager;
|
||||
|
||||
class FlutterInappwebviewWindowsPlugin : public flutter::Plugin {
|
||||
public:
|
||||
flutter::PluginRegistrarWindows* registrar;
|
||||
std::unique_ptr<InAppWebViewManager> inAppWebViewManager;
|
||||
std::unique_ptr<InAppBrowserManager> inAppBrowserManager;
|
||||
|
||||
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar);
|
||||
|
@ -26,7 +26,7 @@ namespace flutter_inappwebview_plugin
|
||||
|
||||
m_hWnd = CreateWindowEx(
|
||||
0, // Optional window styles.
|
||||
InAppBrowser::CLASS_NAME, // Window class
|
||||
wndClass.lpszClassName, // Window class
|
||||
L"", // Window text
|
||||
WS_OVERLAPPEDWINDOW, // Window style
|
||||
|
||||
@ -35,25 +35,35 @@ namespace flutter_inappwebview_plugin
|
||||
|
||||
NULL, // Parent window
|
||||
NULL, // Menu
|
||||
m_hInstance,// Instance handle
|
||||
wndClass.hInstance,// Instance handle
|
||||
this // Additional application data
|
||||
);
|
||||
|
||||
ShowWindow(m_hWnd, SW_SHOW);
|
||||
ShowWindow(m_hWnd, settings->hidden ? SW_HIDE : SW_SHOW);
|
||||
|
||||
InAppWebViewCreationParams webViewParams = {
|
||||
id,
|
||||
params.initialWebViewSettings
|
||||
};
|
||||
|
||||
webView = std::make_unique<InAppWebView>(plugin, webViewParams, m_hWnd, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id, [this]() -> void
|
||||
InAppWebView::createInAppWebViewEnv(m_hWnd, false,
|
||||
[this, webViewParams](wil::com_ptr<ICoreWebView2Environment> webViewEnv, wil::com_ptr<ICoreWebView2Controller> webViewController, wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController) -> void
|
||||
{
|
||||
if (channelDelegate) {
|
||||
channelDelegate->onBrowserCreated();
|
||||
}
|
||||
if (webViewEnv && webViewController) {
|
||||
webView = std::make_unique<InAppWebView>(this, this->plugin, webViewParams, m_hWnd, std::move(webViewEnv), std::move(webViewController), nullptr);
|
||||
webView->initChannel(std::nullopt, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id);
|
||||
|
||||
if (initialUrlRequest.has_value()) {
|
||||
webView->loadUrl(initialUrlRequest.value());
|
||||
if (channelDelegate) {
|
||||
channelDelegate->onBrowserCreated();
|
||||
}
|
||||
|
||||
if (initialUrlRequest.has_value()) {
|
||||
webView->loadUrl(initialUrlRequest.value());
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "Cannot create the InAppWebView instance!" << std::endl;
|
||||
close();
|
||||
}
|
||||
});
|
||||
|
||||
@ -148,6 +158,35 @@ namespace flutter_inappwebview_plugin
|
||||
// }).Get());
|
||||
}
|
||||
|
||||
void InAppBrowser::close() const
|
||||
{
|
||||
DestroyWindow(m_hWnd);
|
||||
}
|
||||
|
||||
void InAppBrowser::show() const
|
||||
{
|
||||
ShowWindow(m_hWnd, SW_SHOW);
|
||||
webView->webViewController->put_IsVisible(true);
|
||||
}
|
||||
|
||||
void InAppBrowser::hide() const
|
||||
{
|
||||
ShowWindow(m_hWnd, SW_HIDE);
|
||||
webView->webViewController->put_IsVisible(false);
|
||||
}
|
||||
|
||||
bool InAppBrowser::isHidden() const
|
||||
{
|
||||
return !IsWindowVisible(m_hWnd);
|
||||
}
|
||||
|
||||
void InAppBrowser::didChangeTitle(const std::optional<std::string>& title) const
|
||||
{
|
||||
if (title.has_value()) {
|
||||
SetWindowText(m_hWnd, ansi_to_wide(title.value()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK InAppBrowser::WndProc(
|
||||
HWND window,
|
||||
UINT message,
|
||||
|
@ -43,6 +43,13 @@ namespace flutter_inappwebview_plugin
|
||||
InAppBrowser(const FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params);
|
||||
~InAppBrowser();
|
||||
|
||||
void close() const;
|
||||
void show() const;
|
||||
void hide() const;
|
||||
bool isHidden() const;
|
||||
|
||||
void didChangeTitle(const std::optional<std::string>& title) const;
|
||||
|
||||
private:
|
||||
const HINSTANCE m_hInstance;
|
||||
HWND m_hWnd;
|
||||
|
@ -18,7 +18,7 @@ namespace flutter_inappwebview_plugin
|
||||
{
|
||||
if (method_call.method_name().compare("open") == 0) {
|
||||
auto* arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
|
||||
open(arguments);
|
||||
createInAppWebView(arguments);
|
||||
result->Success(flutter::EncodableValue(true));
|
||||
}
|
||||
else {
|
||||
@ -26,7 +26,7 @@ namespace flutter_inappwebview_plugin
|
||||
}
|
||||
}
|
||||
|
||||
void InAppBrowserManager::open(const flutter::EncodableMap* arguments)
|
||||
void InAppBrowserManager::createInAppWebView(const flutter::EncodableMap* arguments)
|
||||
{
|
||||
auto id = get_fl_map_value<std::string>(*arguments, "id");
|
||||
auto urlRequestMap = get_optional_fl_map_value<flutter::EncodableMap>(*arguments, "urlRequest");
|
||||
|
@ -27,7 +27,7 @@ namespace flutter_inappwebview_plugin
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
|
||||
void open(const flutter::EncodableMap* arguments);
|
||||
void createInAppWebView(const flutter::EncodableMap* arguments);
|
||||
};
|
||||
}
|
||||
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_MANAGER_H_
|
@ -1,77 +1,135 @@
|
||||
#pragma comment(lib, "Shlwapi.lib")
|
||||
|
||||
#include <cstring>
|
||||
#include <Shlwapi.h>
|
||||
#include <WebView2EnvironmentOptions.h>
|
||||
#include <wil/wrl.h>
|
||||
|
||||
#include "../custom_platform_view/util/composition.desktop.interop.h"
|
||||
#include "../types/web_resource_error.h"
|
||||
#include "../types/web_resource_request.h"
|
||||
#include "../utils/strconv.h"
|
||||
#include "../utils/util.h"
|
||||
#include "in_app_webview.h"
|
||||
#include "in_app_webview_manager.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
InAppWebView::InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::function<void()> completionHandler)
|
||||
: plugin(plugin), id(params.id), settings(params.initialSettings), channelDelegate(std::make_unique<WebViewChannelDelegate>(this, plugin->registrar->messenger()))
|
||||
InAppWebView::InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, wil::com_ptr<ICoreWebView2Environment> webViewEnv,
|
||||
wil::com_ptr<ICoreWebView2Controller> webViewController,
|
||||
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController)
|
||||
: plugin(plugin), id(params.id), webViewEnv(std::move(webViewEnv)), webViewController(std::move(webViewController)), webViewCompositionController(std::move(webViewCompositionController)),
|
||||
settings(params.initialSettings)
|
||||
{
|
||||
createWebView(parentWindow, completionHandler);
|
||||
this->webViewController->get_CoreWebView2(webView.put());
|
||||
|
||||
if (this->webViewCompositionController) {
|
||||
if (!createSurface(parentWindow, plugin->inAppWebViewManager->compositor())) {
|
||||
std::cerr << "Cannot create InAppWebView surface\n";
|
||||
}
|
||||
registerSurfaceEventHandlers();
|
||||
}
|
||||
else {
|
||||
// Resize WebView to fit the bounds of the parent window
|
||||
RECT bounds;
|
||||
GetClientRect(parentWindow, &bounds);
|
||||
this->webViewController->put_Bounds(bounds);
|
||||
}
|
||||
|
||||
wil::com_ptr<ICoreWebView2Settings> webView2Settings;
|
||||
if (SUCCEEDED(webView->get_Settings(&webView2Settings))) {
|
||||
webView2Settings->put_IsScriptEnabled(settings->javaScriptEnabled);
|
||||
webView2Settings->put_IsZoomControlEnabled(settings->supportZoom);
|
||||
|
||||
wil::com_ptr<ICoreWebView2Settings2> webView2Settings2;
|
||||
if (SUCCEEDED(webView2Settings->QueryInterface(IID_PPV_ARGS(&webView2Settings2)))) {
|
||||
if (!settings->userAgent.empty()) {
|
||||
webView2Settings2->put_UserAgent(ansi_to_wide(settings->userAgent).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerEventHandlers();
|
||||
}
|
||||
|
||||
InAppWebView::InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::string& channelName, const std::function<void()> completionHandler)
|
||||
: plugin(plugin), id(params.id), settings(params.initialSettings), channelDelegate(std::make_unique<WebViewChannelDelegate>(this, plugin->registrar->messenger(), channelName))
|
||||
InAppWebView::InAppWebView(InAppBrowser* inAppBrowser, const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, wil::com_ptr<ICoreWebView2Environment> webViewEnv,
|
||||
wil::com_ptr<ICoreWebView2Controller> webViewController,
|
||||
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController)
|
||||
: InAppWebView(plugin, params, parentWindow, std::move(webViewEnv), std::move(webViewController), std::move(webViewCompositionController))
|
||||
{
|
||||
createWebView(parentWindow, completionHandler);
|
||||
this->inAppBrowser = inAppBrowser;
|
||||
}
|
||||
|
||||
void InAppWebView::createWebView(const HWND parentWindow, const std::function<void()> completionHandler)
|
||||
void InAppWebView::createInAppWebViewEnv(const HWND parentWindow, const bool willBeSurface, std::function<void(wil::com_ptr<ICoreWebView2Environment> webViewEnv,
|
||||
wil::com_ptr<ICoreWebView2Controller> webViewController,
|
||||
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController)> completionHandler)
|
||||
{
|
||||
CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr,
|
||||
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
|
||||
[parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT
|
||||
[parentWindow, completionHandler, willBeSurface](HRESULT result, wil::com_ptr<ICoreWebView2Environment> env) -> HRESULT
|
||||
{
|
||||
webViewEnv = env;
|
||||
// Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window HWND
|
||||
env->CreateCoreWebView2Controller(parentWindow, Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
|
||||
[parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT
|
||||
{
|
||||
if (controller != nullptr) {
|
||||
webViewController = controller;
|
||||
webViewController->get_CoreWebView2(webView.put());
|
||||
}
|
||||
if (FAILED(result) || !env) {
|
||||
completionHandler(nullptr, nullptr, nullptr);
|
||||
debugLog(getErrorMessage(result));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
wil::com_ptr<ICoreWebView2Settings> webView2Settings;
|
||||
if (SUCCEEDED(webView->get_Settings(&webView2Settings))) {
|
||||
webView2Settings->put_IsScriptEnabled(settings->javaScriptEnabled);
|
||||
webView2Settings->put_IsZoomControlEnabled(settings->supportZoom);
|
||||
webView2Settings->put_IsStatusBarEnabled(true);
|
||||
wil::com_ptr<ICoreWebView2Environment3> webViewEnv3;
|
||||
if (willBeSurface && SUCCEEDED(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) {
|
||||
webViewEnv3->CreateCoreWebView2CompositionController(parentWindow, Callback<ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler>(
|
||||
[completionHandler, env](HRESULT result, wil::com_ptr<ICoreWebView2CompositionController> compositionController) -> HRESULT
|
||||
{
|
||||
wil::com_ptr<ICoreWebView2Controller3> webViewController = compositionController.try_query<ICoreWebView2Controller3>();
|
||||
|
||||
wil::com_ptr<ICoreWebView2Settings2> webView2Settings2;
|
||||
if (SUCCEEDED(webView2Settings->QueryInterface(IID_PPV_ARGS(&webView2Settings2)))) {
|
||||
if (!settings->userAgent.empty()) {
|
||||
webView2Settings2->put_UserAgent(ansi_to_wide(settings->userAgent).c_str());
|
||||
}
|
||||
if (FAILED(result) || !webViewController) {
|
||||
completionHandler(nullptr, nullptr, nullptr);
|
||||
debugLog(getErrorMessage(result));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
ICoreWebView2Controller3* webViewController3;
|
||||
HRESULT hr = webViewController->QueryInterface(IID_PPV_ARGS(&webViewController3));
|
||||
if (SUCCEEDED(hr)) {
|
||||
webViewController3->put_BoundsMode(COREWEBVIEW2_BOUNDS_MODE_USE_RAW_PIXELS);
|
||||
webViewController3->put_ShouldDetectMonitorScaleChanges(FALSE);
|
||||
webViewController3->put_RasterizationScale(1.0);
|
||||
}
|
||||
else {
|
||||
debugLog(getErrorMessage(hr));
|
||||
}
|
||||
|
||||
completionHandler(std::move(env), std::move(webViewController), std::move(compositionController));
|
||||
return S_OK;
|
||||
}
|
||||
).Get());
|
||||
}
|
||||
else {
|
||||
env->CreateCoreWebView2Controller(parentWindow, Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
|
||||
[completionHandler, env](HRESULT result, wil::com_ptr<ICoreWebView2Controller> controller) -> HRESULT
|
||||
{
|
||||
if (FAILED(result) || !controller) {
|
||||
completionHandler(nullptr, nullptr, nullptr);
|
||||
debugLog(getErrorMessage(result));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Resize WebView to fit the bounds of the parent window
|
||||
RECT bounds;
|
||||
GetClientRect(parentWindow, &bounds);
|
||||
webViewController->put_Bounds(bounds);
|
||||
|
||||
registerEventHandlers();
|
||||
|
||||
completionHandler();
|
||||
|
||||
return S_OK;
|
||||
}).Get());
|
||||
completionHandler(std::move(env), std::move(controller), nullptr);
|
||||
return S_OK;
|
||||
}).Get());
|
||||
}
|
||||
return S_OK;
|
||||
}).Get());
|
||||
}
|
||||
|
||||
void InAppWebView::initChannel(const std::optional<std::variant<std::string, int64_t>> viewId, const std::optional<std::string> channelName)
|
||||
{
|
||||
if (viewId.has_value()) {
|
||||
id = viewId.value();
|
||||
}
|
||||
channelDelegate = channelName.has_value() ? std::make_unique<WebViewChannelDelegate>(this, plugin->registrar->messenger(), channelName.value()) :
|
||||
std::make_unique<WebViewChannelDelegate>(this, plugin->registrar->messenger());
|
||||
}
|
||||
|
||||
void InAppWebView::registerEventHandlers()
|
||||
{
|
||||
if (!webView) {
|
||||
@ -124,17 +182,17 @@ namespace flutter_inappwebview_plugin
|
||||
|
||||
UINT64 navigationId;
|
||||
if (SUCCEEDED(args->get_NavigationId(&navigationId))) {
|
||||
navigationActions.insert({ navigationId, navigationAction });
|
||||
navigationActions.insert({ navigationId, navigationAction }); callShouldOverrideUrlLoading_ =
|
||||
}
|
||||
|
||||
if (callShouldOverrideUrlLoading && requestMethod == nullptr) {
|
||||
if (callShouldOverrideUrlLoading_ && requestMethod == nullptr) {
|
||||
// for some reason, we can't cancel and load an URL with other HTTP methods than GET,
|
||||
// so ignore the shouldOverrideUrlLoading event.
|
||||
|
||||
auto callback = std::make_unique<WebViewChannelDelegate::ShouldOverrideUrlLoadingCallback>();
|
||||
callback->nonNullSuccess = [this, urlRequest](const NavigationActionPolicy actionPolicy)
|
||||
{
|
||||
callShouldOverrideUrlLoading = false;
|
||||
callShouldOverrideUrlLoading_ = false;
|
||||
if (actionPolicy == allow) {
|
||||
loadUrl(*urlRequest);
|
||||
}
|
||||
@ -142,7 +200,7 @@ namespace flutter_inappwebview_plugin
|
||||
};
|
||||
auto defaultBehaviour = [this, urlRequest](const std::optional<NavigationActionPolicy> actionPolicy)
|
||||
{
|
||||
callShouldOverrideUrlLoading = false;
|
||||
callShouldOverrideUrlLoading_ = false;
|
||||
loadUrl(*urlRequest);
|
||||
};
|
||||
callback->defaultBehaviour = defaultBehaviour;
|
||||
@ -155,7 +213,7 @@ namespace flutter_inappwebview_plugin
|
||||
args->put_Cancel(true);
|
||||
}
|
||||
else {
|
||||
callShouldOverrideUrlLoading = true;
|
||||
callShouldOverrideUrlLoading_ = true;
|
||||
channelDelegate->onLoadStart(url);
|
||||
args->put_Cancel(false);
|
||||
}
|
||||
@ -207,14 +265,53 @@ namespace flutter_inappwebview_plugin
|
||||
return S_OK;
|
||||
}
|
||||
).Get(), nullptr);
|
||||
|
||||
webView->add_DocumentTitleChanged(Callback<ICoreWebView2DocumentTitleChangedEventHandler>(
|
||||
[this](ICoreWebView2* sender, IUnknown* args)
|
||||
{
|
||||
if (channelDelegate) {
|
||||
wil::unique_cotaskmem_string title;
|
||||
sender->get_DocumentTitle(&title);
|
||||
channelDelegate->onTitleChanged(title.is_valid() ? wide_to_ansi(title.get()) : std::optional<std::string>{});
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
).Get(), nullptr);
|
||||
}
|
||||
|
||||
void InAppWebView::registerSurfaceEventHandlers()
|
||||
{
|
||||
if (!webViewCompositionController) {
|
||||
return;
|
||||
}
|
||||
|
||||
webViewCompositionController->add_CursorChanged(
|
||||
Callback<ICoreWebView2CursorChangedEventHandler>(
|
||||
[this](ICoreWebView2CompositionController* sender,
|
||||
IUnknown* args) -> HRESULT
|
||||
{
|
||||
HCURSOR cursor;
|
||||
if (cursorChangedCallback_ &&
|
||||
sender->get_Cursor(&cursor) == S_OK) {
|
||||
cursorChangedCallback_(cursor);
|
||||
}
|
||||
return S_OK;
|
||||
})
|
||||
.Get(), nullptr);
|
||||
}
|
||||
|
||||
std::optional<std::string> InAppWebView::getUrl() const
|
||||
{
|
||||
LPWSTR uri = nullptr;
|
||||
LPWSTR uri;
|
||||
return SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(uri) : std::optional<std::string>{};
|
||||
}
|
||||
|
||||
std::optional<std::string> InAppWebView::getTitle() const
|
||||
{
|
||||
LPWSTR title;
|
||||
return SUCCEEDED(webView->get_DocumentTitle(&title)) ? wide_to_utf8(title) : std::optional<std::string>{};
|
||||
}
|
||||
|
||||
void InAppWebView::loadUrl(const URLRequest& urlRequest) const
|
||||
{
|
||||
if (!webView || !urlRequest.url.has_value()) {
|
||||
@ -261,6 +358,338 @@ namespace flutter_inappwebview_plugin
|
||||
}
|
||||
}
|
||||
|
||||
void InAppWebView::reload() const
|
||||
{
|
||||
webView->Reload();
|
||||
}
|
||||
|
||||
void InAppWebView::goBack() const
|
||||
{
|
||||
webView->GoBack();
|
||||
}
|
||||
|
||||
void InAppWebView::goForward() const
|
||||
{
|
||||
webView->GoForward();
|
||||
}
|
||||
|
||||
void InAppWebView::evaluateJavascript(const std::string& source, std::function<void(std::string)> completionHanlder) const
|
||||
{
|
||||
webView->ExecuteScript(ansi_to_wide(source).c_str(),
|
||||
Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
|
||||
[completionHanlder = std::move(completionHanlder)](HRESULT error, PCWSTR result) -> HRESULT
|
||||
{
|
||||
if (error != S_OK) {
|
||||
debugLog(getErrorMessage(error));
|
||||
}
|
||||
completionHanlder(wide_to_ansi(result));
|
||||
return S_OK;
|
||||
}).Get());
|
||||
}
|
||||
|
||||
// flutter_view
|
||||
void InAppWebView::setSurfaceSize(size_t width, size_t height, float scale_factor)
|
||||
{
|
||||
if (!webViewController) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (surface_ && width > 0 && height > 0) {
|
||||
scaleFactor_ = 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 });
|
||||
|
||||
wil::com_ptr<ICoreWebView2Controller3> webViewController3;
|
||||
if (SUCCEEDED(webViewController->QueryInterface(IID_PPV_ARGS(&webViewController3)))) {
|
||||
webViewController3->put_RasterizationScale(scale_factor);
|
||||
}
|
||||
|
||||
if (webViewController->put_Bounds(bounds) != S_OK) {
|
||||
std::cerr << "Setting webview bounds failed." << std::endl;
|
||||
}
|
||||
|
||||
if (surfaceSizeChangedCallback_) {
|
||||
surfaceSizeChangedCallback_(width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InAppWebView::setCursorPos(double x, double y)
|
||||
{
|
||||
if (!webViewCompositionController) {
|
||||
return;
|
||||
}
|
||||
|
||||
POINT point;
|
||||
point.x = static_cast<LONG>(x * scaleFactor_);
|
||||
point.y = static_cast<LONG>(y * scaleFactor_);
|
||||
lastCursorPos_ = point;
|
||||
|
||||
// https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.774.44
|
||||
webViewCompositionController->SendMouseInput(
|
||||
COREWEBVIEW2_MOUSE_EVENT_KIND::COREWEBVIEW2_MOUSE_EVENT_KIND_MOVE,
|
||||
virtualKeys_.state(), 0, point);
|
||||
}
|
||||
|
||||
void InAppWebView::setPointerUpdate(int32_t pointer,
|
||||
InAppWebViewPointerEventKind eventKind, double x,
|
||||
double y, double size, double pressure)
|
||||
{
|
||||
if (!webViewEnv || !webViewCompositionController) {
|
||||
return;
|
||||
}
|
||||
|
||||
COREWEBVIEW2_POINTER_EVENT_KIND event =
|
||||
COREWEBVIEW2_POINTER_EVENT_KIND_UPDATE;
|
||||
UINT32 pointerFlags = POINTER_FLAG_NONE;
|
||||
switch (eventKind) {
|
||||
case InAppWebViewPointerEventKind::Activate:
|
||||
event = COREWEBVIEW2_POINTER_EVENT_KIND_ACTIVATE;
|
||||
break;
|
||||
case InAppWebViewPointerEventKind::Down:
|
||||
event = COREWEBVIEW2_POINTER_EVENT_KIND_DOWN;
|
||||
pointerFlags =
|
||||
POINTER_FLAG_DOWN | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT;
|
||||
break;
|
||||
case InAppWebViewPointerEventKind::Enter:
|
||||
event = COREWEBVIEW2_POINTER_EVENT_KIND_ENTER;
|
||||
break;
|
||||
case InAppWebViewPointerEventKind::Leave:
|
||||
event = COREWEBVIEW2_POINTER_EVENT_KIND_LEAVE;
|
||||
break;
|
||||
case InAppWebViewPointerEventKind::Up:
|
||||
event = COREWEBVIEW2_POINTER_EVENT_KIND_UP;
|
||||
pointerFlags = POINTER_FLAG_UP;
|
||||
break;
|
||||
case InAppWebViewPointerEventKind::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 * scaleFactor_);
|
||||
point.y = static_cast<LONG>(y * scaleFactor_);
|
||||
|
||||
RECT rect;
|
||||
rect.left = point.x - 2;
|
||||
rect.right = point.x + 2;
|
||||
rect.top = point.y - 2;
|
||||
rect.bottom = point.y + 2;
|
||||
|
||||
wil::com_ptr<ICoreWebView2Environment3> webViewEnv3;
|
||||
if (SUCCEEDED(webViewEnv->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) {
|
||||
wil::com_ptr<ICoreWebView2PointerInfo> pInfo;
|
||||
if (SUCCEEDED(webViewEnv3->CreateCoreWebView2PointerInfo(&pInfo))) {
|
||||
if (pInfo) {
|
||||
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);
|
||||
webViewCompositionController->SendPointerInput(event, pInfo.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InAppWebView::setPointerButtonState(InAppWebViewPointerButton button, bool is_down)
|
||||
{
|
||||
if (!webViewCompositionController) {
|
||||
return;
|
||||
}
|
||||
|
||||
COREWEBVIEW2_MOUSE_EVENT_KIND kind;
|
||||
switch (button) {
|
||||
case InAppWebViewPointerButton::Primary:
|
||||
virtualKeys_.setIsLeftButtonDown(is_down);
|
||||
kind = is_down ? COREWEBVIEW2_MOUSE_EVENT_KIND_LEFT_BUTTON_DOWN
|
||||
: COREWEBVIEW2_MOUSE_EVENT_KIND_LEFT_BUTTON_UP;
|
||||
break;
|
||||
case InAppWebViewPointerButton::Secondary:
|
||||
virtualKeys_.setIsRightButtonDown(is_down);
|
||||
kind = is_down ? COREWEBVIEW2_MOUSE_EVENT_KIND_RIGHT_BUTTON_DOWN
|
||||
: COREWEBVIEW2_MOUSE_EVENT_KIND_RIGHT_BUTTON_UP;
|
||||
break;
|
||||
case InAppWebViewPointerButton::Tertiary:
|
||||
virtualKeys_.setIsMiddleButtonDown(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);
|
||||
}
|
||||
|
||||
webViewCompositionController->SendMouseInput(kind, virtualKeys_.state(), 0,
|
||||
lastCursorPos_);
|
||||
}
|
||||
|
||||
void InAppWebView::sendScroll(double delta, bool horizontal)
|
||||
{
|
||||
if (!webViewCompositionController) {
|
||||
return;
|
||||
}
|
||||
|
||||
// delta * 6 gives me a multiple of WHEEL_DELTA (120)
|
||||
constexpr auto kScrollMultiplier = 6;
|
||||
|
||||
auto offset = static_cast<short>(delta * kScrollMultiplier);
|
||||
|
||||
/*
|
||||
// For some reason,
|
||||
// setting the point other than (x: 0, y: 0)
|
||||
// will not make the scroll work.
|
||||
// Unfortunately, this will break the scroll event
|
||||
// for nested HTML scrollable elements.
|
||||
POINT point;
|
||||
point.x = 0;
|
||||
point.y = 0;
|
||||
|
||||
|
||||
if (horizontal) {
|
||||
webViewCompositionController->SendMouseInput(
|
||||
COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL, virtual_keys_.state(),
|
||||
offset, point);
|
||||
}
|
||||
else {
|
||||
webViewCompositionController->SendMouseInput(COREWEBVIEW2_MOUSE_EVENT_KIND_WHEEL,
|
||||
virtual_keys_.state(), offset,
|
||||
point);
|
||||
}
|
||||
*/
|
||||
|
||||
// Workaround for scroll events
|
||||
auto workaroundScrollJS = "(function(horizontal, offset, x, y) { \
|
||||
function elemCanScrollY(elem) { \
|
||||
if (elem.scrollTop > 0) { \
|
||||
return elem; \
|
||||
} else { \
|
||||
elem.scrollTop++; \
|
||||
const top = elem.scrollTop; \
|
||||
top && (elem.scrollTop = 0); \
|
||||
if (top > 0) { \
|
||||
return elem; \
|
||||
} else { \
|
||||
return elemCanScrollY(elem.parentElement); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
function elemCanScrollX(elem) { \
|
||||
if (elem.scrollLeft > 0) { \
|
||||
return elem; \
|
||||
} else { \
|
||||
elem.scrollLeft++; \
|
||||
const left = elem.scrollLeft; \
|
||||
left && (elem.scrollLeft = 0); \
|
||||
if (left > 0) { \
|
||||
return elem; \
|
||||
} else { \
|
||||
return elemCanScrollX(elem.parentElement); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
const elem = document.elementFromPoint(x, y); \
|
||||
const elem2 = horizontal ? elemCanScrollX(elem) : elemCanScrollY(elem); \
|
||||
const handled = elem2 != null && elem2 != document.documentElement && elem2 != document.body; \
|
||||
if (handled) { \
|
||||
elem2.scrollBy({left: horizontal ? offset : 0, top: horizontal ? 0 : offset}); \
|
||||
} \
|
||||
return handled; \
|
||||
})(" + std::to_string(horizontal) + ", " + std::to_string(offset) + ", " + std::to_string(lastCursorPos_.x) + ", " + std::to_string(lastCursorPos_.y) + ");";
|
||||
|
||||
webView->ExecuteScript(ansi_to_wide(workaroundScrollJS).c_str(), Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
|
||||
[this, horizontal, offset](HRESULT error, PCWSTR result) -> HRESULT
|
||||
{
|
||||
if (webViewCompositionController && (error != S_OK || wide_to_ansi(result).compare("false") == 0)) {
|
||||
// try to use native mouse wheel handler
|
||||
|
||||
POINT point;
|
||||
point.x = 0;
|
||||
point.y = 0;
|
||||
|
||||
if (horizontal) {
|
||||
webViewCompositionController->SendMouseInput(
|
||||
COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL, virtualKeys_.state(),
|
||||
offset, point);
|
||||
}
|
||||
else {
|
||||
webViewCompositionController->SendMouseInput(COREWEBVIEW2_MOUSE_EVENT_KIND_WHEEL,
|
||||
virtualKeys_.state(), offset,
|
||||
point);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}).Get());
|
||||
}
|
||||
|
||||
void InAppWebView::setScrollDelta(double delta_x, double delta_y)
|
||||
{
|
||||
if (!webViewCompositionController) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (delta_x != 0.0) {
|
||||
sendScroll(delta_x, true);
|
||||
}
|
||||
if (delta_y != 0.0) {
|
||||
sendScroll(delta_y, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool InAppWebView::createSurface(const HWND parentWindow,
|
||||
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor> compositor)
|
||||
{
|
||||
if (!webViewCompositionController || !webViewController) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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());
|
||||
webViewCompositionController->put_RootVisualTarget(webview_visual2.get());
|
||||
|
||||
webViewController->put_IsVisible(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InAppWebView::isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus)
|
||||
{
|
||||
return webErrorStatus >= COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_COMMON_NAME_IS_INCORRECT && webErrorStatus <= COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID;
|
||||
@ -276,6 +705,7 @@ namespace flutter_inappwebview_plugin
|
||||
webViewController->Close();
|
||||
}
|
||||
navigationActions.clear();
|
||||
inAppBrowser = nullptr;
|
||||
plugin = nullptr;
|
||||
}
|
||||
}
|
@ -4,6 +4,9 @@
|
||||
#include <functional>
|
||||
#include <WebView2.h>
|
||||
#include <wil/com.h>
|
||||
#include <windows.ui.composition.desktop.h>
|
||||
#include <windows.ui.composition.h>
|
||||
#include <winrt/base.h>
|
||||
|
||||
#include "../flutter_inappwebview_windows_plugin.h"
|
||||
#include "../types/navigation_action.h"
|
||||
@ -13,10 +16,59 @@
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
class InAppBrowser;
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
// custom_platform_view
|
||||
enum class InAppWebViewPointerButton { None, Primary, Secondary, Tertiary };
|
||||
enum class InAppWebViewPointerEventKind { Activate, Down, Enter, Leave, Up, Update };
|
||||
typedef std::function<void(size_t width, size_t height)>
|
||||
SurfaceSizeChangedCallback;
|
||||
typedef std::function<void(const HCURSOR)> CursorChangedCallback;
|
||||
struct VirtualKeyState {
|
||||
public:
|
||||
inline void setIsLeftButtonDown(bool is_down)
|
||||
{
|
||||
set(COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS::
|
||||
COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_LEFT_BUTTON,
|
||||
is_down);
|
||||
}
|
||||
|
||||
inline void setIsRightButtonDown(bool is_down)
|
||||
{
|
||||
set(COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS::
|
||||
COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_RIGHT_BUTTON,
|
||||
is_down);
|
||||
}
|
||||
|
||||
inline void setIsMiddleButtonDown(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 InAppWebViewCreationParams {
|
||||
const std::variant<std::string, int> id;
|
||||
const std::variant<std::string, int64_t> id;
|
||||
const std::shared_ptr<InAppWebViewSettings> initialSettings;
|
||||
};
|
||||
|
||||
@ -26,26 +78,75 @@ namespace flutter_inappwebview_plugin
|
||||
static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_";
|
||||
|
||||
const FlutterInappwebviewWindowsPlugin* plugin;
|
||||
const std::variant<std::string, int> id;
|
||||
std::variant<std::string, int64_t> id;
|
||||
wil::com_ptr<ICoreWebView2Environment> webViewEnv;
|
||||
wil::com_ptr<ICoreWebView2Controller> webViewController;
|
||||
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController;
|
||||
wil::com_ptr<ICoreWebView2> webView;
|
||||
const std::unique_ptr<WebViewChannelDelegate> channelDelegate;
|
||||
std::unique_ptr<WebViewChannelDelegate> channelDelegate;
|
||||
std::map<UINT64, std::shared_ptr<NavigationAction>> navigationActions = {};
|
||||
const std::shared_ptr<InAppWebViewSettings> settings;
|
||||
InAppBrowser* inAppBrowser = nullptr;
|
||||
|
||||
InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::function<void()> completionHandler);
|
||||
InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::string& channelName, const std::function<void()> completionHandler);
|
||||
InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow,
|
||||
wil::com_ptr<ICoreWebView2Environment> webViewEnv,
|
||||
wil::com_ptr<ICoreWebView2Controller> webViewController,
|
||||
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController);
|
||||
InAppWebView(InAppBrowser* inAppBrowser, const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow,
|
||||
wil::com_ptr<ICoreWebView2Environment> webViewEnv,
|
||||
wil::com_ptr<ICoreWebView2Controller> webViewController,
|
||||
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController);
|
||||
~InAppWebView();
|
||||
|
||||
static void createInAppWebViewEnv(const HWND parentWindow, const bool willBeSurface, std::function<void(wil::com_ptr<ICoreWebView2Environment> webViewEnv,
|
||||
wil::com_ptr<ICoreWebView2Controller> webViewController,
|
||||
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController)> completionHandler);
|
||||
|
||||
// custom_platform_view
|
||||
ABI::Windows::UI::Composition::IVisual* const surface()
|
||||
{
|
||||
return surface_.get();
|
||||
}
|
||||
void setSurfaceSize(size_t width, size_t height, float scale_factor);
|
||||
void setCursorPos(double x, double y);
|
||||
void setPointerUpdate(int32_t pointer, InAppWebViewPointerEventKind eventKind,
|
||||
double x, double y, double size, double pressure);
|
||||
void setPointerButtonState(InAppWebViewPointerButton button, bool isDown);
|
||||
void sendScroll(double offset, bool horizontal);
|
||||
void setScrollDelta(double delta_x, double delta_y);
|
||||
void onSurfaceSizeChanged(SurfaceSizeChangedCallback callback)
|
||||
{
|
||||
surfaceSizeChangedCallback_ = std::move(callback);
|
||||
}
|
||||
void onCursorChanged(CursorChangedCallback callback)
|
||||
{
|
||||
cursorChangedCallback_ = std::move(callback);
|
||||
}
|
||||
bool createSurface(const HWND parentWindow,
|
||||
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor> compositor);
|
||||
|
||||
void initChannel(const std::optional<std::variant<std::string, int64_t>> viewId, const std::optional<std::string> channelName);
|
||||
std::optional<std::string> getUrl() const;
|
||||
std::optional<std::string> getTitle() const;
|
||||
void loadUrl(const URLRequest& urlRequest) const;
|
||||
void reload() const;
|
||||
void goBack() const;
|
||||
void goForward() const;
|
||||
void evaluateJavascript(const std::string& source, std::function<void(std::string)> completionHanlder) const;
|
||||
|
||||
static bool isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus);
|
||||
private:
|
||||
bool callShouldOverrideUrlLoading = true;
|
||||
void createWebView(const HWND parentWindow, const std::function<void()> completionHandler);
|
||||
// custom_platform_view
|
||||
winrt::com_ptr<ABI::Windows::UI::Composition::IVisual> surface_;
|
||||
SurfaceSizeChangedCallback surfaceSizeChangedCallback_;
|
||||
CursorChangedCallback cursorChangedCallback_;
|
||||
float scaleFactor_ = 1.0;
|
||||
POINT lastCursorPos_ = { 0, 0 };
|
||||
VirtualKeyState virtualKeys_;
|
||||
|
||||
bool callShouldOverrideUrlLoading_ = true;
|
||||
void InAppWebView::registerEventHandlers();
|
||||
void InAppWebView::registerSurfaceEventHandlers();
|
||||
};
|
||||
}
|
||||
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_H_
|
@ -0,0 +1,162 @@
|
||||
#include <DispatcherQueue.h>
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/standard_method_codec.h>
|
||||
#include <shlobj.h>
|
||||
#include <windows.graphics.capture.h>
|
||||
|
||||
#include "../in_app_webview/in_app_webview_settings.h"
|
||||
#include "../types/url_request.h"
|
||||
#include "../utils/flutter.h"
|
||||
#include "in_app_webview_manager.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
InAppWebViewManager::InAppWebViewManager(const FlutterInappwebviewWindowsPlugin* plugin)
|
||||
: plugin(plugin),
|
||||
ChannelDelegate(plugin->registrar->messenger(), InAppWebViewManager::METHOD_CHANNEL_NAME),
|
||||
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());
|
||||
compositor_ = graphics_context_->CreateCompositor();
|
||||
valid_ = graphics_context_->IsValid();
|
||||
}
|
||||
|
||||
windowClass_.lpszClassName = CustomPlatformView::CLASS_NAME;
|
||||
windowClass_.lpfnWndProc = &DefWindowProc;
|
||||
|
||||
RegisterClass(&windowClass_);
|
||||
}
|
||||
|
||||
void InAppWebViewManager::HandleMethodCall(const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
|
||||
{
|
||||
auto* arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
|
||||
|
||||
if (method_call.method_name().compare("createInAppWebView") == 0) {
|
||||
if (isSupported()) {
|
||||
createInAppWebView(arguments, std::move(result));
|
||||
}
|
||||
else {
|
||||
result->Error("0", "Creating an InAppWebView instance is not supported! Graphics Context is not valid!");
|
||||
}
|
||||
}
|
||||
else if (method_call.method_name().compare("dispose") == 0) {
|
||||
auto id = get_fl_map_value<int64_t>(*arguments, "id");
|
||||
if (map_contains(webViews, (uint64_t)id)) {
|
||||
webViews.erase(id);
|
||||
}
|
||||
result->Success();
|
||||
}
|
||||
else {
|
||||
result->NotImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
void InAppWebViewManager::createInAppWebView(const flutter::EncodableMap* arguments, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
|
||||
{
|
||||
auto result_ = std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>(std::move(result));
|
||||
|
||||
auto settingsMap = get_fl_map_value<flutter::EncodableMap>(*arguments, "initialSettings");
|
||||
auto urlRequestMap = get_optional_fl_map_value<flutter::EncodableMap>(*arguments, "initialUrlRequest");
|
||||
|
||||
auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, CW_DEFAULT,
|
||||
CW_DEFAULT, 0, 0, HWND_MESSAGE, nullptr,
|
||||
windowClass_.hInstance, nullptr);
|
||||
|
||||
InAppWebView::createInAppWebViewEnv(hwnd, true,
|
||||
[=](wil::com_ptr<ICoreWebView2Environment> webViewEnv,
|
||||
wil::com_ptr<ICoreWebView2Controller> webViewController,
|
||||
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController)
|
||||
{
|
||||
if (webViewEnv && webViewController && webViewCompositionController) {
|
||||
auto initialSettings = std::make_unique<InAppWebViewSettings>(settingsMap);
|
||||
|
||||
InAppWebViewCreationParams params = {
|
||||
"",
|
||||
std::move(initialSettings),
|
||||
};
|
||||
|
||||
auto inAppWebView = std::make_unique<InAppWebView>(plugin, params, hwnd,
|
||||
std::move(webViewEnv), std::move(webViewController), std::move(webViewCompositionController)
|
||||
);
|
||||
|
||||
std::optional<URLRequest> urlRequest = urlRequestMap.has_value() ? std::make_optional<URLRequest>(urlRequestMap.value()) : std::optional<URLRequest>{};
|
||||
if (urlRequest.has_value()) {
|
||||
inAppWebView->loadUrl(urlRequest.value());
|
||||
}
|
||||
|
||||
auto customPlatformView = std::make_unique<CustomPlatformView>(plugin->registrar->messenger(),
|
||||
plugin->registrar->texture_registrar(),
|
||||
graphics_context(),
|
||||
hwnd,
|
||||
std::move(inAppWebView));
|
||||
|
||||
auto textureId = customPlatformView->texture_id();
|
||||
|
||||
customPlatformView->view->initChannel(textureId, std::nullopt);
|
||||
|
||||
webViews.insert({ textureId, std::move(customPlatformView) });
|
||||
|
||||
result_->Success(flutter::EncodableValue(textureId));
|
||||
}
|
||||
else {
|
||||
result_->Error("0", "Cannot create the InAppWebView instance!");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool InAppWebViewManager::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;
|
||||
}
|
||||
|
||||
InAppWebViewManager::~InAppWebViewManager()
|
||||
{
|
||||
debugLog("dealloc InAppWebViewManager");
|
||||
webViews.clear();
|
||||
UnregisterClass(windowClass_.lpszClassName, nullptr);
|
||||
plugin = nullptr;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_MANAGER_H_
|
||||
#define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_MANAGER_H_
|
||||
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/standard_message_codec.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <wil/com.h>
|
||||
#include <winrt/base.h>
|
||||
|
||||
#include "../custom_platform_view/custom_platform_view.h"
|
||||
#include "../custom_platform_view/graphics_context.h"
|
||||
#include "../custom_platform_view/util/rohelper.h"
|
||||
#include "../flutter_inappwebview_windows_plugin.h"
|
||||
#include "../types/channel_delegate.h"
|
||||
#include "windows.ui.composition.h"
|
||||
|
||||
namespace flutter_inappwebview_plugin
|
||||
{
|
||||
class InAppWebViewManager : public ChannelDelegate
|
||||
{
|
||||
public:
|
||||
static inline const std::string METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview";
|
||||
|
||||
const FlutterInappwebviewWindowsPlugin* plugin;
|
||||
std::map<uint64_t, std::unique_ptr<CustomPlatformView>> webViews;
|
||||
|
||||
bool isSupported() const { return valid_; }
|
||||
bool isGraphicsCaptureSessionSupported();
|
||||
GraphicsContext* graphics_context() const
|
||||
{
|
||||
return graphics_context_.get();
|
||||
};
|
||||
rx::RoHelper* rohelper() const { return rohelper_.get(); }
|
||||
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor> compositor() const
|
||||
{
|
||||
return compositor_;
|
||||
}
|
||||
|
||||
InAppWebViewManager(const FlutterInappwebviewWindowsPlugin* plugin);
|
||||
~InAppWebViewManager();
|
||||
|
||||
void HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
|
||||
void createInAppWebView(const flutter::EncodableMap* arguments, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
private:
|
||||
std::unique_ptr<rx::RoHelper> rohelper_;
|
||||
winrt::com_ptr<ABI::Windows::System::IDispatcherQueueController>
|
||||
dispatcher_queue_controller_;
|
||||
std::unique_ptr<GraphicsContext> graphics_context_;
|
||||
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor> compositor_;
|
||||
WNDCLASS windowClass_ = {};
|
||||
bool valid_ = false;
|
||||
};
|
||||
}
|
||||
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_MANAGER_H_
|
@ -1,3 +1,4 @@
|
||||
#include "../in_app_browser/in_app_browser.h"
|
||||
#include "../types/base_callback_result.h"
|
||||
#include "../utils/flutter.h"
|
||||
#include "../utils/strconv.h"
|
||||
@ -34,15 +35,53 @@ namespace flutter_inappwebview_plugin
|
||||
return;
|
||||
}
|
||||
|
||||
auto& arguments = std::get<flutter::EncodableMap>(*method_call.arguments());
|
||||
|
||||
if (method_call.method_name().compare("getUrl") == 0) {
|
||||
result->Success(make_fl_value(webView->getUrl()));
|
||||
}
|
||||
else if (method_call.method_name().compare("getTitle") == 0) {
|
||||
result->Success(make_fl_value(webView->getUrl()));
|
||||
}
|
||||
else if (method_call.method_name().compare("loadUrl") == 0) {
|
||||
auto& arguments = std::get<flutter::EncodableMap>(*method_call.arguments());
|
||||
auto urlRequest = std::make_unique<URLRequest>(get_fl_map_value<flutter::EncodableMap>(arguments, "urlRequest"));
|
||||
webView->loadUrl(*urlRequest);
|
||||
result->Success(make_fl_value(true));
|
||||
}
|
||||
else if (method_call.method_name().compare("reload") == 0) {
|
||||
webView->reload();
|
||||
result->Success(make_fl_value(true));
|
||||
}
|
||||
else if (method_call.method_name().compare("goBack") == 0) {
|
||||
webView->goBack();
|
||||
result->Success(make_fl_value(true));
|
||||
}
|
||||
else if (method_call.method_name().compare("goForward") == 0) {
|
||||
webView->goForward();
|
||||
result->Success(make_fl_value(true));
|
||||
}
|
||||
else if (method_call.method_name().compare("evaluateJavascript") == 0) {
|
||||
auto result_ = std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>(std::move(result));
|
||||
|
||||
auto source = get_fl_map_value<std::string>(arguments, "source");
|
||||
webView->evaluateJavascript(source, [result_ = std::move(result_)](const std::string& value)
|
||||
{
|
||||
result_->Success(make_fl_value(value));
|
||||
});
|
||||
}
|
||||
// for inAppBrowser
|
||||
else if (webView->inAppBrowser && method_call.method_name().compare("show") == 0) {
|
||||
webView->inAppBrowser->show();
|
||||
result->Success(make_fl_value(true));
|
||||
}
|
||||
else if (webView->inAppBrowser && method_call.method_name().compare("hide") == 0) {
|
||||
webView->inAppBrowser->hide();
|
||||
result->Success(make_fl_value(true));
|
||||
}
|
||||
else if (webView->inAppBrowser && method_call.method_name().compare("close") == 0) {
|
||||
webView->inAppBrowser->close();
|
||||
result->Success(make_fl_value(true));
|
||||
}
|
||||
else {
|
||||
result->NotImplemented();
|
||||
}
|
||||
@ -108,6 +147,22 @@ namespace flutter_inappwebview_plugin
|
||||
channel->InvokeMethod("onReceivedHttpError", std::move(arguments));
|
||||
}
|
||||
|
||||
void WebViewChannelDelegate::onTitleChanged(const std::optional<std::string>& title) const
|
||||
{
|
||||
if (!channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto arguments = std::make_unique<flutter::EncodableValue>(flutter::EncodableMap{
|
||||
{"title", make_fl_value(title)}
|
||||
});
|
||||
channel->InvokeMethod("onTitleChanged", std::move(arguments));
|
||||
|
||||
if (webView && webView->inAppBrowser) {
|
||||
webView->inAppBrowser->didChangeTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
WebViewChannelDelegate::~WebViewChannelDelegate()
|
||||
{
|
||||
debugLog("dealloc WebViewChannelDelegate");
|
||||
|
@ -39,8 +39,9 @@ namespace flutter_inappwebview_plugin
|
||||
void onLoadStart(const std::optional<std::string>& url) const;
|
||||
void onLoadStop(const std::optional<std::string>& url) const;
|
||||
void shouldOverrideUrlLoading(std::shared_ptr<NavigationAction> navigationAction, std::unique_ptr<ShouldOverrideUrlLoadingCallback> callback) const;
|
||||
void WebViewChannelDelegate::onReceivedError(std::shared_ptr<WebResourceRequest> request, std::shared_ptr<WebResourceError> error) const;
|
||||
void WebViewChannelDelegate::onReceivedHttpError(std::shared_ptr<WebResourceRequest> request, std::shared_ptr<WebResourceResponse> error) const;
|
||||
void onReceivedError(std::shared_ptr<WebResourceRequest> request, std::shared_ptr<WebResourceError> error) const;
|
||||
void onReceivedHttpError(std::shared_ptr<WebResourceRequest> request, std::shared_ptr<WebResourceResponse> error) const;
|
||||
void onTitleChanged(const std::optional<std::string>& title) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <comdef.h>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@ -48,13 +49,19 @@ namespace flutter_inappwebview_plugin
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline std::string getErrorMessage(const HRESULT& error)
|
||||
{
|
||||
_com_error err(error);
|
||||
return wide_to_ansi(err.ErrorMessage());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline std::optional<T> make_pointer_optional(const T* value)
|
||||
{
|
||||
return value == nullptr ? std::nullopt : std::make_optional<T>(*value);
|
||||
}
|
||||
|
||||
static inline std::string variant_to_string(const std::variant<std::string, int>& var)
|
||||
static inline std::string variant_to_string(const std::variant<std::string, int64_t>& var)
|
||||
{
|
||||
return std::visit([](auto&& arg)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user