blob: a4c71f2778089aa4d704023931f99d2251264ce6 [file]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:convert' show utf8;
import 'dart:ffi' hide Size;
import 'dart:io';
import 'dart:ui' show Display, FlutterView;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import '../foundation/_features.dart';
import '_window.dart';
import '_window_positioner.dart';
import 'binding.dart';
// Do not import this file in production applications or packages published
// to pub.dev. Flutter will make breaking changes to this file, even in patch
// versions.
//
// All APIs in this file must be private or must:
//
// 1. Have the `@internal` attribute.
// 2. Throw an `UnsupportedError` if `isWindowingEnabled`
// is `false`.
//
// See: https://github.com/flutter/flutter/issues/30701.
const String _kWindowingDisabledErrorMessage = '''
Windowing APIs are not enabled.
Windowing APIs are currently experimental. Do not use windowing APIs in
production applications or plugins published to pub.dev.
To try experimental windowing APIs:
1. Switch to Flutter's main release channel.
2. Turn on the windowing feature flag.
See: https://github.com/flutter/flutter/issues/30701.
''';
/// [WindowingOwner] implementation for macOS.
///
/// If [Platform.isMacOS] is false, then the constructor will throw an
/// [UnsupportedError].
///
/// {@macro flutter.widgets.windowing.experimental}
///
/// See also:
///
/// * [WindowingOwner], the abstract class that manages native windows.
class WindowingOwnerMacOS extends WindowingOwner {
/// Creates a new [WindowingOwnerMacOS] instance.
///
/// {@macro flutter.widgets.windowing.experimental}
///
/// See also:
///
/// * [WindowingOwner], the abstract class that manages native windows.
@internal
WindowingOwnerMacOS() {
if (!isWindowingEnabled) {
throw UnsupportedError(_kWindowingDisabledErrorMessage);
}
if (!Platform.isMacOS) {
throw UnsupportedError('Only available on the macOS platform');
}
assert(
WidgetsBinding.instance.platformDispatcher.engineId != null,
'WindowingOwnerMacOS must be created after the engine has been initialized.',
);
}
@override
RegularWindowController createRegularWindowController({
required RegularWindowControllerDelegate delegate,
Size? preferredSize,
BoxConstraints? preferredConstraints,
String? title,
}) {
final res = RegularWindowControllerMacOS(
owner: this,
delegate: delegate,
preferredSize: preferredSize,
title: title,
);
_activeControllers.add(res);
return res;
}
@override
DialogWindowController createDialogWindowController({
required DialogWindowControllerDelegate delegate,
Size? preferredSize,
BoxConstraints? preferredConstraints,
BaseWindowController? parent,
String? title,
}) {
final res = DialogWindowControllerMacOS(
owner: this,
delegate: delegate,
preferredSize: preferredSize,
parent: parent,
title: title,
);
_activeControllers.add(res);
return res;
}
@internal
@override
TooltipWindowController createTooltipWindowController({
required TooltipWindowControllerDelegate delegate,
required BoxConstraints preferredConstraints,
required bool isSizedToContent,
required Rect anchorRect,
required WindowPositioner positioner,
required BaseWindowController parent,
}) {
final res = TooltipWindowControllerMacOS(
owner: this,
delegate: delegate,
contentSizeConstraints: preferredConstraints,
anchorRect: anchorRect,
positioner: positioner,
parent: parent,
);
_activeControllers.add(res);
return res;
}
@internal
@override
PopupWindowController createPopupWindowController({
required PopupWindowControllerDelegate delegate,
required BoxConstraints preferredConstraints,
required Rect anchorRect,
required WindowPositioner positioner,
required BaseWindowController parent,
}) {
throw UnimplementedError('Popup windows are not yet implemented on MacOS.');
}
final List<_WindowControllerMixin> _activeControllers = <_WindowControllerMixin>[];
/// Returns the window handle for the given [view], or null is the window
/// handle is not available.
///
/// The window handle is a pointer to the NSWindow instance.
static Pointer<Void> getWindowHandle(FlutterView view) {
return _MacOSPlatformInterface.getWindowHandle(
PlatformDispatcher.instance.engineId!,
view.viewId,
);
}
}
mixin _WindowControllerMixin {
void _initController(WindowingOwnerMacOS owner) {
if (!isWindowingEnabled) {
throw UnsupportedError(_kWindowingDisabledErrorMessage);
}
_onShouldClose = NativeCallable<Void Function()>.isolateLocal(_handleOnShouldClose);
_onWillClose = NativeCallable<Void Function()>.isolateLocal(_handleOnWillClose);
_onResize = NativeCallable<Void Function()>.isolateLocal(_handleOnResize);
_onGetWindowPosition =
NativeCallable<
Pointer<_Rect> Function(
Pointer<_Size> childSize,
Pointer<_Rect> parentRect,
Pointer<_Rect> outputRect,
)
>.isolateLocal(_handleOnGetWindowPosition);
_owner = owner;
_owner._activeControllers.add(this);
}
void _handleOnShouldClose();
void _handleOnResize();
@mustCallSuper
void _handleOnWillClose() {
_onWillClose.close();
_onShouldClose.close();
_onResize.close();
_onGetWindowPosition.close();
_destroyed = true;
_owner._activeControllers.remove(this);
}
@mustCallSuper
Pointer<_Rect> _handleOnGetWindowPosition(
Pointer<_Size> childSize,
Pointer<_Rect> parentRect,
Pointer<_Rect> outputRect,
) {
return Pointer<_Rect>.fromAddress(0);
}
void _ensureNotDestroyed() {
if (_destroyed) {
throw StateError('Window has been destroyed.');
}
}
FlutterView get rootView;
/// Returns window handle for the current window.
/// The handle is a pointer to NSWindow instance.
Pointer<Void> getWindowHandle() {
_ensureNotDestroyed();
return WindowingOwnerMacOS.getWindowHandle(rootView);
}
Size get contentSize {
_ensureNotDestroyed();
return _MacOSPlatformInterface.getWindowContentSize(getWindowHandle());
}
void destroy() {
if (_destroyed) {
return;
}
final Pointer<Void> handle = getWindowHandle();
_MacOSPlatformInterface.destroyWindow(handle);
}
bool get destroyed => _destroyed;
bool _destroyed = false;
late final NativeCallable<Void Function()> _onShouldClose;
late final NativeCallable<Void Function()> _onWillClose;
late final NativeCallable<Void Function()> _onResize;
late final NativeCallable<
Pointer<_Rect> Function(
Pointer<_Size> childSize,
Pointer<_Rect> parentRect,
Pointer<_Rect> outputRect,
)
>
_onGetWindowPosition;
late final WindowingOwnerMacOS _owner;
}
/// MacOS specific implementation of [TooltipWindowController].
///
/// /// {@macro flutter.widgets.windowing.experimental}
class TooltipWindowControllerMacOS extends TooltipWindowController with _WindowControllerMixin {
/// Creates a new tooltip window controller for macOS.
TooltipWindowControllerMacOS({
required WindowingOwnerMacOS owner,
required TooltipWindowControllerDelegate delegate,
required BoxConstraints contentSizeConstraints,
required BaseWindowController parent,
required Rect anchorRect,
required WindowPositioner positioner,
}) : _anchorRect = anchorRect,
_positioner = positioner,
_delegate = delegate,
_parent = parent,
super.empty() {
_initController(owner);
final int viewId = _MacOSPlatformInterface.createTooltipWindow(
preferredConstraints: contentSizeConstraints,
onShouldClose: _onShouldClose.nativeFunction,
onWillClose: _onWillClose.nativeFunction,
onNotifyListeners: _onResize.nativeFunction,
onGetWindowPosition: _onGetWindowPosition.nativeFunction,
parentViewId: parent.rootView.viewId,
);
final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere(
(FlutterView view) => view.viewId == viewId,
);
rootView = flutterView;
}
@override
void updatePosition({Rect? anchorRect, WindowPositioner? positioner}) {
if (anchorRect != null) {
_anchorRect = anchorRect;
}
if (positioner != null) {
_positioner = positioner;
}
_MacOSPlatformInterface.updateWindowPosition(getWindowHandle());
}
@override
void _handleOnShouldClose() {
destroy();
}
@override
void _handleOnWillClose() {
super._handleOnWillClose();
_delegate.onWindowDestroyed();
}
@override
void _handleOnResize() {
notifyListeners();
}
@override
Pointer<_Rect> _handleOnGetWindowPosition(
Pointer<_Size> childSize,
Pointer<_Rect> parentRect,
Pointer<_Rect> outputRect,
) {
super._handleOnGetWindowPosition(childSize, parentRect, outputRect);
final Pointer<_Rect> result = _allocator<_Rect>();
final Rect targetRect = _positioner.placeWindow(
childSize: childSize.ref.toSize(),
anchorRect: _anchorRect.translate(parentRect.ref.left, parentRect.ref.top),
parentRect: parentRect.ref.toRect(),
displayRect: outputRect.ref.toRect(),
);
result.ref.left = targetRect.left;
result.ref.top = targetRect.top;
result.ref.width = childSize.ref.width;
result.ref.height = childSize.ref.height;
return result;
}
@override
BaseWindowController get parent => _parent;
@override
void setConstraints(BoxConstraints constraints) {
_ensureNotDestroyed();
_MacOSPlatformInterface.setWindowConstraints(getWindowHandle(), constraints);
}
final TooltipWindowControllerDelegate _delegate;
final BaseWindowController _parent;
WindowPositioner _positioner;
Rect _anchorRect;
}
/// Implementation of [RegularWindowController] for the macOS platform.
///
/// {@macro flutter.widgets.windowing.experimental}
///
/// See also:
///
/// * [RegularWindowController], the base class for regular windows.
class RegularWindowControllerMacOS extends RegularWindowController with _WindowControllerMixin {
/// Creates a new regular window controller for macOS. When this constructor
/// completes the FlutterView is created and framework is aware of it.
RegularWindowControllerMacOS({
required WindowingOwnerMacOS owner,
required RegularWindowControllerDelegate delegate,
required Size? preferredSize,
BoxConstraints? preferredConstraints,
String? title,
}) : _delegate = delegate,
super.empty() {
_initController(owner);
final int viewId = _MacOSPlatformInterface.createRegularWindow(
preferredSize: preferredSize,
preferredConstraints: preferredConstraints,
onShouldClose: _onShouldClose.nativeFunction,
onWillClose: _onWillClose.nativeFunction,
onNotifyListeners: _onResize.nativeFunction,
);
final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere(
(FlutterView view) => view.viewId == viewId,
);
rootView = flutterView;
if (title != null) {
setTitle(title);
}
}
@override
void _handleOnShouldClose() {
_delegate.onWindowCloseRequested(this);
}
@override
void _handleOnWillClose() {
super._handleOnWillClose();
_delegate.onWindowDestroyed();
}
@override
void _handleOnResize() {
notifyListeners();
}
@override
@internal
void setSize(Size size) {
_ensureNotDestroyed();
_MacOSPlatformInterface.setWindowContentSize(getWindowHandle(), size);
}
@override
@internal
void setConstraints(BoxConstraints constraints) {
_ensureNotDestroyed();
_MacOSPlatformInterface.setWindowConstraints(getWindowHandle(), constraints);
}
@override
void setTitle(String title) {
_ensureNotDestroyed();
_MacOSPlatformInterface.setWindowTitle(getWindowHandle(), title);
notifyListeners();
}
@override
Size get contentSize {
_ensureNotDestroyed();
return _MacOSPlatformInterface.getWindowContentSize(getWindowHandle());
}
@override
void activate() {
_ensureNotDestroyed();
_MacOSPlatformInterface.activate(getWindowHandle());
}
@override
void setMaximized(bool maximized) {
_ensureNotDestroyed();
_MacOSPlatformInterface.setMaximized(getWindowHandle(), maximized);
}
@override
bool get isMaximized {
_ensureNotDestroyed();
return _MacOSPlatformInterface.isMaximized(getWindowHandle());
}
@override
void setMinimized(bool minimized) {
_ensureNotDestroyed();
if (minimized) {
_MacOSPlatformInterface.minimize(getWindowHandle());
} else {
_MacOSPlatformInterface.unminimize(getWindowHandle());
}
}
@override
bool get isMinimized {
_ensureNotDestroyed();
return _MacOSPlatformInterface.isMinimized(getWindowHandle());
}
@override
void setFullscreen(bool fullscreen, {Display? display}) {
_ensureNotDestroyed();
_MacOSPlatformInterface.setFullscreen(getWindowHandle(), fullscreen);
}
@override
bool get isFullscreen {
_ensureNotDestroyed();
return _MacOSPlatformInterface.isFullscreen(getWindowHandle());
}
final RegularWindowControllerDelegate _delegate;
@override
bool get isActivated => _MacOSPlatformInterface.isActivated(getWindowHandle());
@override
String get title => _MacOSPlatformInterface.getTitle(getWindowHandle());
}
/// Implementation of [DialogWindowController] for the macOS platform.
///
/// {@macro flutter.widgets.windowing.experimental}
///
/// See also:
///
/// * [DialogWindowController], the base class for dialog windows.
class DialogWindowControllerMacOS extends DialogWindowController with _WindowControllerMixin {
/// Creates a new regular window controller for macOS. When this constructor
/// completes the FlutterView is created and framework is aware of it.
DialogWindowControllerMacOS({
required WindowingOwnerMacOS owner,
required DialogWindowControllerDelegate delegate,
required Size? preferredSize,
this.parent,
BoxConstraints? preferredConstraints,
String? title,
}) : _delegate = delegate,
super.empty() {
_initController(owner);
final int viewId = _MacOSPlatformInterface.createDialogWindow(
preferredSize: preferredSize,
preferredConstraints: preferredConstraints,
onShouldClose: _onShouldClose.nativeFunction,
onWillClose: _onWillClose.nativeFunction,
onNotifyListeners: _onResize.nativeFunction,
parentViewId: parent?.rootView.viewId,
);
final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere(
(FlutterView view) => view.viewId == viewId,
);
rootView = flutterView;
if (title != null) {
setTitle(title);
}
}
@override
void _handleOnShouldClose() {
_delegate.onWindowCloseRequested(this);
}
@override
void _handleOnWillClose() {
super._handleOnWillClose();
_delegate.onWindowDestroyed();
}
@override
void _handleOnResize() {
notifyListeners();
}
@override
@internal
void setSize(Size size) {
_ensureNotDestroyed();
_MacOSPlatformInterface.setWindowContentSize(getWindowHandle(), size);
}
@override
@internal
void setConstraints(BoxConstraints constraints) {
_ensureNotDestroyed();
_MacOSPlatformInterface.setWindowConstraints(getWindowHandle(), constraints);
}
@override
void setTitle(String title) {
_ensureNotDestroyed();
_MacOSPlatformInterface.setWindowTitle(getWindowHandle(), title);
notifyListeners();
}
final DialogWindowControllerDelegate _delegate;
@override
Size get contentSize {
_ensureNotDestroyed();
return _MacOSPlatformInterface.getWindowContentSize(getWindowHandle());
}
@override
void activate() {
_ensureNotDestroyed();
_MacOSPlatformInterface.activate(getWindowHandle());
}
@override
void setMinimized(bool minimized) {
_ensureNotDestroyed();
if (minimized) {
_MacOSPlatformInterface.minimize(getWindowHandle());
} else {
_MacOSPlatformInterface.unminimize(getWindowHandle());
}
}
@override
bool get isMinimized {
_ensureNotDestroyed();
return _MacOSPlatformInterface.isMinimized(getWindowHandle());
}
@override
bool get isActivated => _MacOSPlatformInterface.isActivated(getWindowHandle());
@override
String get title => _MacOSPlatformInterface.getTitle(getWindowHandle());
@override
final BaseWindowController? parent;
}
final class _WindowCreationRequest extends Struct {
@Bool()
external bool hasSize;
external _Size contentSize;
@Bool()
external bool hasConstraints;
external _Constraints constraints;
@Int64()
external int parentViewId;
external Pointer<NativeFunction<Void Function()>> onShouldClose;
external Pointer<NativeFunction<Void Function()>> onWillClose;
external Pointer<NativeFunction<Void Function()>> onNotifyListeners;
external Pointer<
NativeFunction<
Pointer<_Rect> Function(
Pointer<_Size> childSize,
Pointer<_Rect> parentRect,
Pointer<_Rect> outputRect,
)
>
>
onGetWindowPosition;
}
final class _Size extends Struct {
@Double()
external double width;
@Double()
external double height;
@override
String toString() {
return 'Size(width: $width, height: $height)';
}
Size toSize() {
return Size(width, height);
}
}
final class _Rect extends Struct {
@Double()
external double left;
@Double()
external double top;
@Double()
external double width;
@Double()
external double height;
Rect toRect() {
return Rect.fromLTWH(left, top, width, height);
}
@override
String toString() {
return 'Rect(left: $left, top: $top, width: $width, height: $height)';
}
}
final class _Constraints extends Struct {
@Double()
external double minWidth;
@Double()
external double minHeight;
@Double()
external double maxWidth;
@Double()
external double maxHeight;
}
class _MacOSPlatformInterface {
@Native<Pointer<Void> Function(Int64, Int64)>(symbol: 'InternalFlutter_Window_GetHandle')
external static Pointer<Void> getWindowHandle(int engineId, int viewId);
@Native<Void Function(Pointer<Void>, Pointer<_Size>)>(
symbol: 'InternalFlutter_Window_SetContentSize',
)
external static void _setWindowContentSize(Pointer<Void> windowHandle, Pointer<_Size> size);
static void setWindowContentSize(Pointer<Void> windowHandle, Size size) {
final Pointer<_Size> ffiSize = _allocator<_Size>();
ffiSize.ref
..width = size.width
..height = size.height;
_setWindowContentSize(windowHandle, ffiSize);
_allocator.free(ffiSize);
}
@Native<Void Function(Pointer<Void>, Pointer<_Constraints>)>(
symbol: 'InternalFlutter_Window_SetConstraints',
)
external static void _setWindowConstraints(
Pointer<Void> windowHandle,
Pointer<_Constraints> size,
);
static void setWindowConstraints(Pointer<Void> windowHandle, BoxConstraints constraints) {
final Pointer<_Constraints> ffiConstraints = _allocator<_Constraints>();
ffiConstraints.ref
..minWidth = constraints.minWidth
..minHeight = constraints.minHeight
..maxWidth = constraints.maxWidth
..maxHeight = constraints.maxHeight;
_setWindowConstraints(windowHandle, ffiConstraints);
_allocator.free(ffiConstraints);
}
@Native<Int64 Function(Int64, Pointer<_WindowCreationRequest>)>(
symbol: 'InternalFlutter_WindowController_CreateRegularWindow',
)
external static int _createRegularWindow(int engineId, Pointer<_WindowCreationRequest> request);
/// Creates a new window and returns the viewId of the created FlutterView.
static int createRegularWindow({
required Size? preferredSize,
BoxConstraints? preferredConstraints,
required Pointer<NativeFunction<Void Function()>> onShouldClose,
required Pointer<NativeFunction<Void Function()>> onWillClose,
required Pointer<NativeFunction<Void Function()>> onNotifyListeners,
}) {
final Pointer<_WindowCreationRequest> request = _allocator<_WindowCreationRequest>()
..ref.onShouldClose = onShouldClose
..ref.onWillClose = onWillClose
..ref.onNotifyListeners = onNotifyListeners;
if (preferredSize != null) {
request.ref
..hasSize = true
..contentSize.width = preferredSize.width
..contentSize.height = preferredSize.height;
}
if (preferredConstraints != null) {
request.ref
..hasConstraints = true
..constraints.minWidth = preferredConstraints.minWidth
..constraints.minHeight = preferredConstraints.minHeight
..constraints.maxWidth = preferredConstraints.maxWidth
..constraints.maxHeight = preferredConstraints.maxHeight;
}
final int viewId = _createRegularWindow(
WidgetsBinding.instance.platformDispatcher.engineId!,
request,
);
_allocator.free(request);
return viewId;
}
@Native<Int64 Function(Int64, Pointer<_WindowCreationRequest>)>(
symbol: 'InternalFlutter_WindowController_CreateDialogWindow',
)
external static int _createDialogWindow(int engineId, Pointer<_WindowCreationRequest> request);
/// Creates a new window and returns the viewId of the created FlutterView.
static int createDialogWindow({
required Size? preferredSize,
BoxConstraints? preferredConstraints,
int? parentViewId,
required Pointer<NativeFunction<Void Function()>> onShouldClose,
required Pointer<NativeFunction<Void Function()>> onWillClose,
required Pointer<NativeFunction<Void Function()>> onNotifyListeners,
}) {
final Pointer<_WindowCreationRequest> request = _allocator<_WindowCreationRequest>()
..ref.onShouldClose = onShouldClose
..ref.onWillClose = onWillClose
..ref.onNotifyListeners = onNotifyListeners
..ref.parentViewId = parentViewId ?? 0;
if (preferredSize != null) {
request.ref
..hasSize = true
..contentSize.width = preferredSize.width
..contentSize.height = preferredSize.height;
}
if (preferredConstraints != null) {
request.ref
..hasConstraints = true
..constraints.minWidth = preferredConstraints.minWidth
..constraints.minHeight = preferredConstraints.minHeight
..constraints.maxWidth = preferredConstraints.maxWidth
..constraints.maxHeight = preferredConstraints.maxHeight;
}
try {
final int viewId = _createDialogWindow(
WidgetsBinding.instance.platformDispatcher.engineId!,
request,
);
return viewId;
} finally {
_allocator.free(request);
}
}
@Native<Int64 Function(Int64, Pointer<_WindowCreationRequest>)>(
symbol: 'InternalFlutter_WindowController_CreateTooltipWindow',
)
external static int _createTooltipWindow(int engineId, Pointer<_WindowCreationRequest> request);
/// Creates a new window and returns the viewId of the created FlutterView.
static int createTooltipWindow({
required BoxConstraints preferredConstraints,
required int parentViewId,
required Pointer<NativeFunction<Void Function()>> onShouldClose,
required Pointer<NativeFunction<Void Function()>> onWillClose,
required Pointer<NativeFunction<Void Function()>> onNotifyListeners,
required Pointer<
NativeFunction<
Pointer<_Rect> Function(
Pointer<_Size> childSize,
Pointer<_Rect> parentRect,
Pointer<_Rect> outputRect,
)
>
>
onGetWindowPosition,
}) {
final Pointer<_WindowCreationRequest> request = _allocator<_WindowCreationRequest>()
..ref.onShouldClose = onShouldClose
..ref.onWillClose = onWillClose
..ref.onNotifyListeners = onNotifyListeners
..ref.onGetWindowPosition = onGetWindowPosition
..ref.parentViewId = parentViewId;
request.ref
..hasConstraints = true
..constraints.minWidth = preferredConstraints.minWidth
..constraints.minHeight = preferredConstraints.minHeight
..constraints.maxWidth = preferredConstraints.maxWidth
..constraints.maxHeight = preferredConstraints.maxHeight;
final int viewId = _createTooltipWindow(PlatformDispatcher.instance.engineId!, request);
_allocator.free(request);
return viewId;
}
@Native<Void Function(Int64, Pointer<Void>)>(symbol: 'InternalFlutter_Window_Destroy')
external static void _destroyWindow(int engineId, Pointer<Void> handle);
static void destroyWindow(Pointer<Void> windowHandle) {
_destroyWindow(WidgetsBinding.instance.platformDispatcher.engineId!, windowHandle);
}
@Native<_Size Function(Pointer<Void>)>(symbol: 'InternalFlutter_Window_GetContentSize')
external static _Size _getWindowContentSize(Pointer<Void> windowHandle);
static Size getWindowContentSize(Pointer<Void> windowHandle) {
final _Size size = _getWindowContentSize(windowHandle);
return Size(size.width, size.height);
}
@Native<Void Function(Pointer<Void>, Pointer<_Utf8>)>(symbol: 'InternalFlutter_Window_SetTitle')
external static void _setWindowTitle(Pointer<Void> windowHandle, Pointer<_Utf8> title);
static void setWindowTitle(Pointer<Void> windowHandle, String title) {
final Pointer<_Utf8> titlePointer = title.toNativeUtf8();
_setWindowTitle(windowHandle, titlePointer);
_allocator.free(titlePointer);
}
@Native<Void Function(Pointer<Void>, Bool)>(symbol: 'InternalFlutter_Window_SetMaximized')
external static void setMaximized(Pointer<Void> windowHandle, bool maximized);
@Native<Bool Function(Pointer<Void>)>(symbol: 'InternalFlutter_Window_IsMaximized')
external static bool isMaximized(Pointer<Void> windowHandle);
@Native<Void Function(Pointer<Void>)>(symbol: 'InternalFlutter_Window_Minimize')
external static void minimize(Pointer<Void> windowHandle);
@Native<Void Function(Pointer<Void>)>(symbol: 'InternalFlutter_Window_Unminimize')
external static void unminimize(Pointer<Void> windowHandle);
@Native<Bool Function(Pointer<Void>)>(symbol: 'InternalFlutter_Window_IsMinimized')
external static bool isMinimized(Pointer<Void> windowHandle);
@Native<Void Function(Pointer<Void>, Bool)>(symbol: 'InternalFlutter_Window_SetFullScreen')
external static void setFullscreen(Pointer<Void> windowHandle, bool fullscreen);
@Native<Bool Function(Pointer<Void>)>(symbol: 'InternalFlutter_Window_IsFullScreen')
external static bool isFullscreen(Pointer<Void> windowHandle);
@Native<Void Function(Pointer<Void>)>(symbol: 'InternalFlutter_Window_Activate')
external static void activate(Pointer<Void> windowHandle);
@Native<Pointer<_Utf8> Function(Pointer<Void>)>(symbol: 'InternalFlutter_Window_GetTitle')
external static Pointer<_Utf8> _getTitle(Pointer<Void> windowHandle);
static String getTitle(Pointer<Void> windowHandle) {
final Pointer<_Utf8> title = _getTitle(windowHandle);
final String result = title.toDartString();
_allocator.free(title);
return result;
}
@Native<Bool Function(Pointer<Void>)>(symbol: 'InternalFlutter_Window_IsActivated')
external static bool isActivated(Pointer<Void> windowHandle);
@Native<Void Function(Pointer<Void>)>(symbol: 'InternalFlutter_Window_UpdatePosition')
external static void updateWindowPosition(Pointer<Void> windowHandle);
}
// FFI utilities.
typedef _PosixCallocNative = Pointer<Void> Function(IntPtr num, IntPtr size);
@Native<_PosixCallocNative>(symbol: 'calloc')
external Pointer<Void> _posixCalloc(int num, int size);
typedef _PosixFreeNative = Void Function(Pointer<NativeType>);
@Native<Void Function(Pointer<NativeType>)>(symbol: 'free')
external void _posixFree(Pointer<NativeType> ptr);
final Pointer<NativeFunction<_PosixFreeNative>> _posixFreePointer =
Native.addressOf<NativeFunction<_PosixFreeNative>>(_posixFree);
const _CallocAllocator _allocator = _CallocAllocator._();
final class _CallocAllocator implements Allocator {
const _CallocAllocator._();
/// Allocates [byteCount] bytes of zero-initialized of memory on the native
/// heap.
@override
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
final Pointer<T> result = _posixCalloc(byteCount, 1).cast();
if (result.address == 0) {
throw ArgumentError('Could not allocate $byteCount bytes.');
}
return result;
}
/// Releases memory allocated on the native heap.
@override
void free(Pointer<NativeType> pointer) {
_posixFree(pointer);
}
/// Returns a pointer to a native free function.
Pointer<NativeFinalizerFunction> get nativeFree => _posixFreePointer;
}
/// The contents of a native zero-terminated array of UTF-8 code units.
///
/// The Utf8 type itself has no functionality, it's only intended to be used
/// through a `Pointer<Utf8>` representing the entire array. This pointer is
/// the equivalent of a char pointer (`const char*`) in C code.
final class _Utf8 extends Opaque {}
/// Extension method for converting a`Pointer<Utf8>` to a [String].
extension _Utf8Pointer on Pointer<_Utf8> {
/// Converts this UTF-8 encoded string to a Dart string.
///
/// Decodes the UTF-8 code units of this zero-terminated byte array as
/// Unicode code points and creates a Dart string containing those code
/// points.
///
/// If [length] is provided, zero-termination is ignored and the result can
/// contain NUL characters.
///
/// If [length] is not provided, the returned string is the string up til
/// but not including the first NUL character.
String toDartString({int? length}) {
_ensureNotNullptr('toDartString');
final Pointer<Uint8> codeUnits = cast<Uint8>();
if (length != null) {
RangeError.checkNotNegative(length, 'length');
} else {
length = _length(codeUnits);
}
return utf8.decode(codeUnits.asTypedList(length));
}
static int _length(Pointer<Uint8> codeUnits) {
var length = 0;
while (codeUnits[length] != 0) {
length++;
}
return length;
}
void _ensureNotNullptr(String operation) {
if (this == nullptr) {
throw UnsupportedError("Operation '$operation' not allowed on a 'nullptr'.");
}
}
}
/// Extension method for converting a [String] to a `Pointer<Utf8>`.
extension _StringUtf8Pointer on String {
/// Creates a zero-terminated [_Utf8] code-unit array from this String.
///
/// If this [String] contains NUL characters, converting it back to a string
/// using [_Utf8Pointer.toDartString] will truncate the result if a length is
/// not passed.
///
/// Unpaired surrogate code points in this [String] will be encoded as
/// replacement characters (U+FFFD, encoded as the bytes 0xEF 0xBF 0xBD) in
/// the UTF-8 encoded result. See [Utf8Encoder] for details on encoding.
///
/// Returns an [allocator]-allocated pointer to the result.
Pointer<_Utf8> toNativeUtf8({Allocator allocator = _allocator}) {
final Uint8List units = utf8.encode(this);
final Pointer<Uint8> result = allocator<Uint8>(units.length + 1);
final Uint8List nativeString = result.asTypedList(units.length + 1);
nativeString.setAll(0, units);
nativeString[units.length] = 0;
return result.cast();
}
}