From 772920eeed1c2a07300c7f3b796ee8225264e8f9 Mon Sep 17 00:00:00 2001 From: charafau Date: Tue, 31 May 2022 01:19:00 +0100 Subject: [PATCH 01/20] Remove circular dependency between compositor and surface Add equals methods to Surface class Change single surface view to multiple views in demo project Commented out focusing code in surface for now --- compositor_dart/lib/compositor_dart.dart | 39 +++++++--- compositor_dart/lib/surface.dart | 29 ++++++-- demo/lib/main.dart | 91 ++++++++++-------------- demo/pubspec.lock | 13 +--- src/surface.c | 23 +++--- 5 files changed, 104 insertions(+), 91 deletions(-) diff --git a/compositor_dart/lib/compositor_dart.dart b/compositor_dart/lib/compositor_dart.dart index df28a56..1157138 100644 --- a/compositor_dart/lib/compositor_dart.dart +++ b/compositor_dart/lib/compositor_dart.dart @@ -1,3 +1,4 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first library compositor_dart; import 'dart:async'; @@ -21,15 +22,33 @@ class Surface { final int gid; final int uid; - final Compositor compositor; - Surface({ required this.handle, required this.pid, required this.gid, required this.uid, - required this.compositor, }); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Surface && + other.handle == handle && + other.pid == pid && + other.gid == gid && + other.uid == uid; + } + + @override + int get hashCode { + return handle.hashCode ^ pid.hashCode ^ gid.hashCode ^ uid.hashCode; + } + + @override + String toString() { + return 'Surface(handle: $handle, pid: $pid, gid: $gid, uid: $uid)'; + } } class _CompositorPlatform { @@ -81,8 +100,6 @@ class _CompositorPlatform { } } -final Compositor compositor = Compositor(); - class Compositor { static void initLogger() { FlutterError.onError = (FlutterErrorDetails details) { @@ -111,17 +128,19 @@ class Compositor { pid: call.arguments["client_pid"], gid: call.arguments["client_gid"], uid: call.arguments["client_uid"], - compositor: this, ); - surfaces[surface.handle] = surface; + surfaces.putIfAbsent(surface.handle, () => surface); + surfaceMapped.add(surface); }); platform.addHandler("surface_unmap", (call) async { int handle = call.arguments["handle"]; - Surface surface = surfaces[handle]!; - surfaces.remove(handle); - surfaceUnmapped.add(surface); + if (surfaces.containsKey(handle)) { + Surface surface = surfaces[handle]!; + surfaces.remove(handle); + surfaceUnmapped.add(surface); + } }); platform.addHandler("flutter/keyevent", (call) async {}); diff --git a/compositor_dart/lib/surface.dart b/compositor_dart/lib/surface.dart index b2d03b2..e9d7b0c 100644 --- a/compositor_dart/lib/surface.dart +++ b/compositor_dart/lib/surface.dart @@ -23,7 +23,7 @@ class _MeasureSizeRenderObject extends RenderProxyBox { if (oldSize == newSize) return; oldSize = newSize; - WidgetsBinding.instance?.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { onChange(newSize); }); } @@ -46,8 +46,13 @@ class _MeasureSize extends SingleChildRenderObjectWidget { class SurfaceView extends StatefulWidget { final Surface surface; + final Compositor compositor; - const SurfaceView({Key? key, required this.surface}) : super(key: key); + const SurfaceView({ + Key? key, + required this.surface, + required this.compositor, + }) : super(key: key); @override State createState() { @@ -61,7 +66,10 @@ class _SurfaceViewState extends State { @override void initState() { super.initState(); - controller = _CompositorPlatformViewController(surface: widget.surface); + controller = _CompositorPlatformViewController( + surface: widget.surface, + compositor: widget.compositor, + ); } @override @@ -69,7 +77,10 @@ class _SurfaceViewState extends State { super.didUpdateWidget(oldWidget); if (oldWidget.surface != widget.surface) { controller.dispose(); - controller = _CompositorPlatformViewController(surface: widget.surface); + controller = _CompositorPlatformViewController( + surface: widget.surface, + compositor: widget.compositor, + ); } } @@ -90,7 +101,7 @@ class _SurfaceViewState extends State { int? keycode = physicalToXkbMap[event.physicalKey.usbHidUsage]; if (keycode != null) { - compositor.platform.surfaceSendKey( + widget.compositor.platform.surfaceSendKey( widget.surface, keycode, status, @@ -120,10 +131,14 @@ class _SurfaceViewState extends State { } class _CompositorPlatformViewController extends PlatformViewController { - Surface surface; + final Surface surface; + final Compositor compositor; Size size = const Size(100, 100); - _CompositorPlatformViewController({required this.surface}); + _CompositorPlatformViewController({ + required this.surface, + required this.compositor, + }); void setSize(Size size) { this.size = size; diff --git a/demo/lib/main.dart b/demo/lib/main.dart index 7dc39f7..a693132 100644 --- a/demo/lib/main.dart +++ b/demo/lib/main.dart @@ -1,4 +1,4 @@ -import 'dart:io'; +import 'dart:math'; import 'package:compositor_dart/compositor_dart.dart'; import 'package:compositor_dart/surface.dart'; @@ -54,45 +54,35 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - int _counter = 0; - Compositor compositor = Compositor(); - Surface? surface; + late Compositor compositor; + Map surfaces = {}; - _MyHomePageState() { - compositor.surfaceMapped.stream.listen((event) { + int? focusedSurface; + + @override + void initState() { + super.initState(); + compositor = Compositor(); + compositor.surfaceMapped.stream.listen((Surface event) { setState(() { - surface = event; + surfaces.putIfAbsent(event.handle, () => event); + focusedSurface = event.handle; }); }); - compositor.surfaceUnmapped.stream.listen((event) { - if (surface == event) { - setState(() { - surface = null; - }); - } - }); - } - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; + compositor.surfaceUnmapped.stream.listen((Surface event) { + setState(() { + surfaces.removeWhere((key, value) => key == event.handle); + if (surfaces.isNotEmpty) { + focusedSurface = surfaces.keys.last; + } else { + focusedSurface = null; + } + }); }); } @override Widget build(BuildContext context) { - Widget? surfaceView; - if (surface != null) { - surfaceView = SurfaceView( - surface: surface!, - ); - } - // This method is rerun every time setState is called, for instance as done // by the _incrementCounter method above. // @@ -103,9 +93,9 @@ class _MyHomePageState extends State { onKeyEvent: (node, KeyEvent event) { int? keycode = compositor.keyToXkb(event.physicalKey.usbHidUsage); - if (keycode != null && surface != null) { + if (keycode != null && focusedSurface != null) { compositor.platform.surfaceSendKey( - surface!, + surfaces[focusedSurface]!, keycode, event is KeyDownEvent ? KeyStatus.pressed : KeyStatus.released, event.timeStamp); @@ -122,7 +112,7 @@ class _MyHomePageState extends State { body: Center( // Center is a layout widget. It takes a single child and positions it // in the middle of the parent. - child: Column( + child: Row( // Column is also a layout widget. It takes a list of children and // arranges them vertically. By default, it sizes itself to fit its // children horizontally, and tries to be as tall as its parent. @@ -138,27 +128,22 @@ class _MyHomePageState extends State { // axis because Columns are vertical (the cross axis would be // horizontal). mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many timesa:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headline4, - ), - Container( - color: Colors.amber, - padding: const EdgeInsets.all(8.0), - child: SizedBox(width: 500, height: 500, child: surfaceView), - ), - ], + children: surfaces.entries + .map((MapEntry entry) => Container( + color: Colors + .primaries[Random().nextInt(Colors.primaries.length)], + child: SizedBox( + width: 500, + height: 500, + child: SurfaceView( + surface: entry.value, + compositor: compositor, + ), + ), + )) + .toList(), ), ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. ), ); } diff --git a/demo/pubspec.lock b/demo/pubspec.lock index ae7980e..043b316 100644 --- a/demo/pubspec.lock +++ b/demo/pubspec.lock @@ -42,7 +42,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" + version: "1.16.0" compositor_dart: dependency: "direct main" description: @@ -63,7 +63,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" flutter: dependency: "direct main" description: flutter @@ -170,13 +170,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.9" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" vector_math: dependency: transitive description: @@ -185,5 +178,5 @@ packages: source: hosted version: "2.1.2" sdks: - dart: ">=2.16.0 <3.0.0" + dart: ">=2.17.0-0 <3.0.0" flutter: ">=1.17.0" diff --git a/src/surface.c b/src/surface.c index 9673fcc..6dcc49a 100644 --- a/src/surface.c +++ b/src/surface.c @@ -27,17 +27,18 @@ static void focus_view(struct fwr_view *view, struct wlr_surface *surface) { /* Don't re-focus an already focused surface. */ return; } - if (prev_surface) { - /* - * Deactivate the previously focused surface. This lets the client know - * it no longer has focus and the client will repaint accordingly, e.g. - * stop displaying a caret. - */ - struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface( - seat->keyboard_state.focused_surface); - assert(previous->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - wlr_xdg_toplevel_set_activated(previous->toplevel, false); - } + // TODO: we should manage changing focus on dart side + // if (prev_surface) { + // /* + // * Deactivate the previously focused surface. This lets the client know + // * it no longer has focus and the client will repaint accordingly, e.g. + // * stop displaying a caret. + // */ + // struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface( + // seat->keyboard_state.focused_surface); + // assert(previous->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + // wlr_xdg_toplevel_set_activated(previous->toplevel, false); + // } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); /* Activate the new surface */ if(view->surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { From 7171f26c0923f3b33d66d9f257cc146ebbaface5 Mon Sep 17 00:00:00 2001 From: charafau Date: Wed, 1 Jun 2022 02:28:48 +0100 Subject: [PATCH 02/20] Add support for focused view Add changing focus for demo WM --- compositor_dart/lib/compositor_dart.dart | 4 +++ compositor_dart/lib/surface.dart | 9 +++++ demo/lib/main.dart | 26 ++++++++++----- include/messages.h | 8 ++++- include/surface.h | 3 +- src/flutter_wlroots.c | 4 +++ src/messages.c | 13 ++++++++ src/surface.c | 33 +++++++++++++++++++ .../flutter_embedder/meson_options.txt | 4 +-- 9 files changed, 91 insertions(+), 13 deletions(-) diff --git a/compositor_dart/lib/compositor_dart.dart b/compositor_dart/lib/compositor_dart.dart index 1157138..679c179 100644 --- a/compositor_dart/lib/compositor_dart.dart +++ b/compositor_dart/lib/compositor_dart.dart @@ -98,6 +98,10 @@ class _CompositorPlatform { ], ); } + + Future surfaceFocusViewWithHandle(int handle) async { + await channel.invokeMethod("surface_focus_from_handle", [handle]); + } } class Compositor { diff --git a/compositor_dart/lib/surface.dart b/compositor_dart/lib/surface.dart index e9d7b0c..36ae358 100644 --- a/compositor_dart/lib/surface.dart +++ b/compositor_dart/lib/surface.dart @@ -47,11 +47,13 @@ class _MeasureSize extends SingleChildRenderObjectWidget { class SurfaceView extends StatefulWidget { final Surface surface; final Compositor compositor; + final Function(Surface surface)? onPointerClick; const SurfaceView({ Key? key, required this.surface, required this.compositor, + this.onPointerClick, }) : super(key: key); @override @@ -69,6 +71,7 @@ class _SurfaceViewState extends State { controller = _CompositorPlatformViewController( surface: widget.surface, compositor: widget.compositor, + onPointerClick: widget.onPointerClick, ); } @@ -80,6 +83,7 @@ class _SurfaceViewState extends State { controller = _CompositorPlatformViewController( surface: widget.surface, compositor: widget.compositor, + onPointerClick: widget.onPointerClick, ); } } @@ -133,11 +137,13 @@ class _SurfaceViewState extends State { class _CompositorPlatformViewController extends PlatformViewController { final Surface surface; final Compositor compositor; + final Function(Surface surface)? onPointerClick; Size size = const Size(100, 100); _CompositorPlatformViewController({ required this.surface, required this.compositor, + this.onPointerClick, }); void setSize(Size size) { @@ -170,6 +176,9 @@ class _CompositorPlatformViewController extends PlatformViewController { Offset scrollAmount = Offset.zero; if (event is PointerDownEvent) { eventType = pointerDownEvent; + if (onPointerClick != null) { + onPointerClick!(surface); + } } else if (event is PointerUpEvent) { eventType = pointerUpEvent; } else if (event is PointerHoverEvent) { diff --git a/demo/lib/main.dart b/demo/lib/main.dart index a693132..15427d4 100644 --- a/demo/lib/main.dart +++ b/demo/lib/main.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:compositor_dart/compositor_dart.dart'; import 'package:compositor_dart/surface.dart'; import 'package:flutter/material.dart'; @@ -81,6 +79,13 @@ class _MyHomePageState extends State { }); } + void focusView(int handle) { + if (focusedSurface != handle) { + compositor.platform.surfaceFocusViewWithHandle(handle); + focusedSurface = handle; + } + } + @override Widget build(BuildContext context) { // This method is rerun every time setState is called, for instance as done @@ -129,16 +134,19 @@ class _MyHomePageState extends State { // horizontal). mainAxisAlignment: MainAxisAlignment.center, children: surfaces.entries - .map((MapEntry entry) => Container( - color: Colors - .primaries[Random().nextInt(Colors.primaries.length)], + .map((MapEntry entry) => GestureDetector( + onTap: () { + focusView(entry.key); + }, child: SizedBox( - width: 500, + width: 400, height: 500, child: SurfaceView( - surface: entry.value, - compositor: compositor, - ), + surface: entry.value, + compositor: compositor, + onPointerClick: (Surface surface) { + focusView(surface.handle); + }), ), )) .toList(), diff --git a/include/messages.h b/include/messages.h index 1b5e17f..dc743a9 100644 --- a/include/messages.h +++ b/include/messages.h @@ -34,4 +34,10 @@ struct surface_keyboard_key_message { int64_t timestamp; }; -bool decode_surface_keyboard_key_message(struct dart_value *value, struct surface_keyboard_key_message *out); \ No newline at end of file +bool decode_surface_keyboard_key_message(struct dart_value *value, struct surface_keyboard_key_message *out); + +struct surface_handle_focus_message { + uint32_t surface_handle; +}; + +bool decode_surface_handle_focus_message(struct dart_value *value, struct surface_handle_focus_message *out); \ No newline at end of file diff --git a/include/surface.h b/include/surface.h index 218a7ef..1071f4b 100644 --- a/include/surface.h +++ b/include/surface.h @@ -7,4 +7,5 @@ #include "instance.h" void fwr_new_xdg_surface(struct wl_listener *listener, void *data); -void fwr_handle_surface_toplevel_set_size(struct fwr_instance *instance, const FlutterPlatformMessageResponseHandle *handle, struct dart_value *args); \ No newline at end of file +void fwr_handle_surface_toplevel_set_size(struct fwr_instance *instance, const FlutterPlatformMessageResponseHandle *handle, struct dart_value *args); +void fwr_handle_surface_focus(struct fwr_instance *instance, const FlutterPlatformMessageResponseHandle *handle, struct dart_value *args); \ No newline at end of file diff --git a/src/flutter_wlroots.c b/src/flutter_wlroots.c index 79a0dd9..3fab341 100644 --- a/src/flutter_wlroots.c +++ b/src/flutter_wlroots.c @@ -162,6 +162,10 @@ static void engine_cb_platform_message( fwr_handle_surface_toplevel_set_size(instance, engine_message->response_handle, &args); return; } + if (strcmp(method_name, "surface_focus_from_handle") == 0) { + fwr_handle_surface_focus(instance, engine_message->response_handle, &args) ; + return; + } if (strcmp(method_name, "is_compositor") == 0) { // Just send a success response. diff --git a/src/messages.c b/src/messages.c index 1468ad6..9b8a34a 100644 --- a/src/messages.c +++ b/src/messages.c @@ -96,5 +96,18 @@ bool decode_surface_toplevel_set_size_message(struct dart_value *value, struct s DECODE_INTEGER(out->size_x, &value->list.values[1]); DECODE_INTEGER(out->size_y, &value->list.values[2]); + return true; +} + +bool decode_surface_handle_focus_message(struct dart_value *value, struct surface_handle_focus_message *out) { + if (value->type != dvList) { + return false; + } + if (value->list.length != 1) { + return false; + } + + DECODE_INTEGER(out->surface_handle, &value->list.values[0]); + return true; } \ No newline at end of file diff --git a/src/surface.c b/src/surface.c index 6dcc49a..2eba4d7 100644 --- a/src/surface.c +++ b/src/surface.c @@ -21,6 +21,7 @@ static void focus_view(struct fwr_view *view, struct wlr_surface *surface) { return; } struct fwr_instance *instance = view->instance; + instance->current_focused_view = view->handle; struct wlr_seat *seat = instance->seat; struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; if (prev_surface == surface) { @@ -200,4 +201,36 @@ void fwr_handle_surface_toplevel_set_size( wlr_log(WLR_ERROR, "Invalid toplevel set size message"); // Send failure instance->fl_proc_table.SendPlatformMessageResponse(instance->engine, handle, NULL, 0); +} + +void fwr_handle_surface_focus( + struct fwr_instance *instance, + const FlutterPlatformMessageResponseHandle *handle, + struct dart_value *args +){ + + struct surface_handle_focus_message message; + + if (!decode_surface_handle_focus_message(args, &message)) { + goto error; + } + + struct fwr_view *view; + if (!handle_map_get(instance->views, message.surface_handle, (void**) &view)) { + // This implies a race condition of surface removal. + // We return success here. + goto success; + } + + focus_view(view, view->surface->surface); + +success: + instance->fl_proc_table.SendPlatformMessageResponse(instance->engine, handle, method_call_null_success, sizeof(method_call_null_success)); + return; + +error: + wlr_log(WLR_ERROR, "Invalid toplevel set size message"); + // Send failure + instance->fl_proc_table.SendPlatformMessageResponse(instance->engine, handle, NULL, 0); + } \ No newline at end of file diff --git a/subprojects/flutter_embedder/meson_options.txt b/subprojects/flutter_embedder/meson_options.txt index a2fb228..5dede37 100644 --- a/subprojects/flutter_embedder/meson_options.txt +++ b/subprojects/flutter_embedder/meson_options.txt @@ -1,2 +1,2 @@ -option('engine_commit', type: 'string', value: '890a5fca2e34db413be624fc83aeea8e61d42ce6', description: 'Commit of engine build to use') -option('engine_embedder_hash', type: 'string', value: '2ab7c40a7aef8ba4b5766a930e95e3d5bd92c70d8d8d6cc3f28bb690ec030b14', description: 'If present, sha256 hash to validate embedder build') +option('engine_commit', type: 'string', value: 'caaafc5604ee9172293eb84a381be6aadd660317', description: 'Commit of engine build to use') +option('engine_embedder_hash', type: 'string', value: 'e4406385e65883db8389e2e061af5bb465564974adc53aff43b1b2c159b91ec0', description: 'If present, sha256 hash to validate embedder build') From edcb00f100041707d4a4e400189f2c3d1e0dc51d Mon Sep 17 00:00:00 2001 From: charafau Date: Sat, 4 Jun 2022 03:26:41 +0100 Subject: [PATCH 03/20] Add popup surface support --- compositor_dart/lib/compositor_dart.dart | 19 ++++++++++--- demo/lib/main.dart | 34 ++++++++++++------------ include/handle_map.h | 4 +++ include/instance.h | 2 ++ src/handle_map.cc | 32 ++++++++++++++++++++-- src/surface.c | 25 ++++++++++++++--- 6 files changed, 90 insertions(+), 26 deletions(-) diff --git a/compositor_dart/lib/compositor_dart.dart b/compositor_dart/lib/compositor_dart.dart index 679c179..2bdf1a3 100644 --- a/compositor_dart/lib/compositor_dart.dart +++ b/compositor_dart/lib/compositor_dart.dart @@ -21,12 +21,16 @@ class Surface { final int pid; final int gid; final int uid; + final bool isPopup; + final int parentHandle; Surface({ required this.handle, required this.pid, required this.gid, required this.uid, + required this.isPopup, + required this.parentHandle, }); @override @@ -37,17 +41,24 @@ class Surface { other.handle == handle && other.pid == pid && other.gid == gid && - other.uid == uid; + other.uid == uid && + other.isPopup == isPopup && + other.parentHandle == parentHandle; } @override int get hashCode { - return handle.hashCode ^ pid.hashCode ^ gid.hashCode ^ uid.hashCode; + return handle.hashCode ^ + pid.hashCode ^ + gid.hashCode ^ + uid.hashCode ^ + isPopup.hashCode ^ + parentHandle.hashCode; } @override String toString() { - return 'Surface(handle: $handle, pid: $pid, gid: $gid, uid: $uid)'; + return 'Surface(handle: $handle, pid: $pid, gid: $gid, uid: $uid, isPopup: $isPopup, parentHandle: $parentHandle)'; } } @@ -132,6 +143,8 @@ class Compositor { pid: call.arguments["client_pid"], gid: call.arguments["client_gid"], uid: call.arguments["client_uid"], + isPopup: call.arguments["is_popup"], + parentHandle: call.arguments["parent_handle"], ); surfaces.putIfAbsent(surface.handle, () => surface); diff --git a/demo/lib/main.dart b/demo/lib/main.dart index 15427d4..2b0d772 100644 --- a/demo/lib/main.dart +++ b/demo/lib/main.dart @@ -133,23 +133,23 @@ class _MyHomePageState extends State { // axis because Columns are vertical (the cross axis would be // horizontal). mainAxisAlignment: MainAxisAlignment.center, - children: surfaces.entries - .map((MapEntry entry) => GestureDetector( - onTap: () { - focusView(entry.key); - }, - child: SizedBox( - width: 400, - height: 500, - child: SurfaceView( - surface: entry.value, - compositor: compositor, - onPointerClick: (Surface surface) { - focusView(surface.handle); - }), - ), - )) - .toList(), + children: surfaces.entries.map((MapEntry entry) { + return GestureDetector( + onTap: () { + focusView(entry.key); + }, + child: SizedBox( + width: 400, + height: 500, + child: SurfaceView( + surface: entry.value, + compositor: compositor, + onPointerClick: (Surface surface) { + focusView(surface.handle); + }), + ), + ); + }).toList(), ), ), ), diff --git a/include/handle_map.h b/include/handle_map.h index 3c95b18..761a007 100644 --- a/include/handle_map.h +++ b/include/handle_map.h @@ -12,6 +12,10 @@ uint32_t handle_map_add(struct handle_map *map, void *value); bool handle_map_get(struct handle_map *map, uint32_t handle, void **out); bool handle_map_remove(struct handle_map *map, uint32_t handle); +void handle_map_add_reverse(struct handle_map *map, void* value, uint32_t handle); +uint32_t handle_map_find(struct handle_map *map, void* value); +bool handle_map_remove_reverse(struct handle_map *map, uint32_t handle); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/include/instance.h b/include/instance.h index cb3be50..cd2a896 100644 --- a/include/instance.h +++ b/include/instance.h @@ -91,6 +91,8 @@ struct fwr_view { struct fwr_instance *instance; struct wlr_xdg_surface *surface; + uint32_t parent_handle; + bool is_popup; struct wl_listener map; struct wl_listener unmap; diff --git a/src/handle_map.cc b/src/handle_map.cc index a06ee96..f8d1117 100644 --- a/src/handle_map.cc +++ b/src/handle_map.cc @@ -7,10 +7,11 @@ struct handle_map { uint32_t next; std::map map; + std::map reverse_map; }; extern "C" handle_map *handle_map_new() { - handle_map *map = new handle_map({1, {}}); + handle_map *map = new handle_map({1, {}, {}}); return map; } @@ -42,4 +43,31 @@ extern "C" bool handle_map_get(handle_map *map, uint32_t handle, void **out) { *out = entry->second; return true; } -} \ No newline at end of file +} + +extern "C" void handle_map_add_reverse(handle_map *map, void* value, uint32_t handle) { + map->reverse_map.insert({value, handle}); +} + +extern "C" uint32_t handle_map_find(handle_map *map, void* value) { + + auto entry = map->reverse_map.find(value); + if (entry == map->reverse_map.end()) { + return -2; + } else { + return entry->second; + } + +} + +extern "C" bool handle_map_remove_reverse(handle_map *map, uint32_t handle) { + + for (auto it = map->reverse_map.begin(); it != map->reverse_map.end(); ++it) { + if (it->second == handle){ + map->reverse_map.erase(it); + return true; + } + } + + return false; +} diff --git a/src/surface.c b/src/surface.c index 2eba4d7..3b2bb4d 100644 --- a/src/surface.c +++ b/src/surface.c @@ -69,7 +69,7 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) { msg_seg = message_builder_segment(&msg); struct message_builder_segment arg_seg = - message_builder_segment_push_map(&msg_seg, 4); + message_builder_segment_push_map(&msg_seg, 6); message_builder_segment_push_string(&arg_seg, "handle"); wlr_log(WLR_INFO, "viewhandle %d", view->handle); message_builder_segment_push_int64(&arg_seg, view->handle); @@ -79,6 +79,12 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) { message_builder_segment_push_int64(&arg_seg, uid); message_builder_segment_push_string(&arg_seg, "client_gid"); message_builder_segment_push_int64(&arg_seg, gid); + + message_builder_segment_push_string(&arg_seg, "is_popup"); + message_builder_segment_push_bool(&arg_seg, view->is_popup); + + message_builder_segment_push_string(&arg_seg, "parent_handle"); + message_builder_segment_push_int64(&arg_seg, view->parent_handle); message_builder_segment_finish(&arg_seg); message_builder_segment_finish(&msg_seg); @@ -144,6 +150,7 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { free(msg_buf); handle_map_remove(instance->views, view->handle); + handle_map_remove_reverse(instance->views, view->handle); } static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {} @@ -152,13 +159,21 @@ void fwr_new_xdg_surface(struct wl_listener *listener, void *data) { struct fwr_instance *instance = wl_container_of(listener, instance, new_xdg_surface); struct wlr_xdg_surface *xdg_surface = data; + struct fwr_view *view = calloc(1, sizeof(struct fwr_view)); + + struct wlr_xdg_surface *parent = 0; + uint32_t parent_handle = -1; if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - return; + parent = wlr_xdg_surface_from_wlr_surface(xdg_surface->popup->parent); + parent_handle = handle_map_find(instance->views, parent); + view->is_popup = true; + view->parent_handle = parent_handle; + } else { + view->is_popup = false; + view->parent_handle = -1; } - struct fwr_view *view = calloc(1, sizeof(struct fwr_view)); - view->instance = instance; view->surface = xdg_surface; @@ -170,6 +185,8 @@ void fwr_new_xdg_surface(struct wl_listener *listener, void *data) { wl_signal_add(&xdg_surface->events.destroy, &view->destroy); uint32_t view_handle = handle_map_add(instance->views, (void *)view); + handle_map_add_reverse(instance->views, xdg_surface, view_handle); + view->handle = view_handle; } From 8045de691f7673cce5f5ad9e4b3cac87a7c59e7f Mon Sep 17 00:00:00 2001 From: charafau Date: Tue, 7 Jun 2022 02:59:11 +0100 Subject: [PATCH 04/20] Add basic floating functionality --- demo/lib/constants.dart | 8 ++++ demo/lib/main.dart | 81 +++++++++++++++------------------ demo/lib/window.dart | 54 ++++++++++++++++++++++ demo/lib/window_decoration.dart | 28 ++++++++++++ 4 files changed, 127 insertions(+), 44 deletions(-) create mode 100644 demo/lib/constants.dart create mode 100644 demo/lib/window.dart create mode 100644 demo/lib/window_decoration.dart diff --git a/demo/lib/constants.dart b/demo/lib/constants.dart new file mode 100644 index 0000000..1a5f85b --- /dev/null +++ b/demo/lib/constants.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +final systemBorderRadius = BorderRadius.circular(6); + +const double windowWidth = 400; +const double windowHeight = 500; +const double initialPositionX = 200; +const double initialPositionY = 70; diff --git a/demo/lib/main.dart b/demo/lib/main.dart index 2b0d772..a47144e 100644 --- a/demo/lib/main.dart +++ b/demo/lib/main.dart @@ -1,5 +1,8 @@ import 'package:compositor_dart/compositor_dart.dart'; import 'package:compositor_dart/surface.dart'; +import 'package:demo/constants.dart'; +import 'package:demo/window.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -56,10 +59,16 @@ class _MyHomePageState extends State { Map surfaces = {}; int? focusedSurface; + late double mousePositionX; + late double mousePositionY; @override void initState() { super.initState(); + + mousePositionX = 0; + mousePositionY = 0; + compositor = Compositor(); compositor.surfaceMapped.stream.listen((Surface event) { setState(() { @@ -88,12 +97,6 @@ class _MyHomePageState extends State { @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. return Focus( onKeyEvent: (node, KeyEvent event) { int? keycode = compositor.keyToXkb(event.physicalKey.usbHidUsage); @@ -110,46 +113,36 @@ class _MyHomePageState extends State { autofocus: true, child: Scaffold( appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. title: Text(widget.title), ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Row( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: surfaces.entries.map((MapEntry entry) { - return GestureDetector( - onTap: () { - focusView(entry.key); - }, - child: SizedBox( - width: 400, - height: 500, - child: SurfaceView( - surface: entry.value, - compositor: compositor, - onPointerClick: (Surface surface) { - focusView(surface.handle); - }), - ), - ); - }).toList(), + body: SizedBox.expand( + child: MouseRegion( + onHover: (PointerHoverEvent event) { + mousePositionX = event.position.dx; + mousePositionY = event.position.dy; + }, + child: Stack( + children: surfaces.entries.map((MapEntry entry) { + final isPopup = entry.value.isPopup; + + return Window( + initialX: isPopup ? mousePositionX : initialPositionX, + initialY: isPopup ? mousePositionY : initialPositionY, + shouldDecorate: !isPopup, + onTap: () => focusView(entry.key), + child: SizedBox( + width: windowWidth, + height: windowHeight, + child: SurfaceView( + surface: entry.value, + compositor: compositor, + onPointerClick: (Surface surface) { + focusView(surface.handle); + }), + ), + ); + }).toList(), + ), ), ), ), diff --git a/demo/lib/window.dart b/demo/lib/window.dart new file mode 100644 index 0000000..75e6871 --- /dev/null +++ b/demo/lib/window.dart @@ -0,0 +1,54 @@ +import 'package:demo/window_decoration.dart'; +import 'package:flutter/material.dart'; + +class Window extends StatefulWidget { + final double initialX; + final double initialY; + final Widget child; + final VoidCallback onTap; + final bool shouldDecorate; + + const Window({ + Key? key, + required this.initialX, + required this.initialY, + required this.child, + required this.onTap, + this.shouldDecorate = true, + }) : super(key: key); + + @override + State createState() => _WindowState(); +} + +class _WindowState extends State { + late double initialX; + late double initialY; + + @override + void initState() { + super.initState(); + initialX = widget.initialX; + initialY = widget.initialY; + } + + @override + Widget build(BuildContext context) { + return Positioned( + left: initialX, + top: initialY, + child: GestureDetector( + onTap: widget.onTap, + onPanUpdate: (DragUpdateDetails details) { + setState(() { + initialX += details.delta.dx; + initialY += details.delta.dy; + }); + }, + child: widget.shouldDecorate + ? WindowDecoration(child: widget.child) + : widget.child, + ), + ); + } +} diff --git a/demo/lib/window_decoration.dart b/demo/lib/window_decoration.dart new file mode 100644 index 0000000..ed50ba5 --- /dev/null +++ b/demo/lib/window_decoration.dart @@ -0,0 +1,28 @@ +import 'package:demo/constants.dart'; +import 'package:flutter/material.dart'; + +class WindowDecoration extends StatelessWidget { + final Widget child; + + const WindowDecoration({ + Key? key, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: systemBorderRadius, + child: Column( + children: [ + Container( + color: Colors.red, + height: 30, + width: windowWidth, + ), + child, + ], + ), + ); + } +} From d5f59fcc7773e518c3e2652ee0c76ac80bd0193e Mon Sep 17 00:00:00 2001 From: charafau Date: Mon, 20 Jun 2022 03:04:32 +0100 Subject: [PATCH 05/20] remove hash and equal from surface --- compositor_dart/lib/compositor_dart.dart | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/compositor_dart/lib/compositor_dart.dart b/compositor_dart/lib/compositor_dart.dart index 2bdf1a3..8107deb 100644 --- a/compositor_dart/lib/compositor_dart.dart +++ b/compositor_dart/lib/compositor_dart.dart @@ -33,29 +33,6 @@ class Surface { required this.parentHandle, }); - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is Surface && - other.handle == handle && - other.pid == pid && - other.gid == gid && - other.uid == uid && - other.isPopup == isPopup && - other.parentHandle == parentHandle; - } - - @override - int get hashCode { - return handle.hashCode ^ - pid.hashCode ^ - gid.hashCode ^ - uid.hashCode ^ - isPopup.hashCode ^ - parentHandle.hashCode; - } - @override String toString() { return 'Surface(handle: $handle, pid: $pid, gid: $gid, uid: $uid, isPopup: $isPopup, parentHandle: $parentHandle)'; From 5fcf6f9de91df1a8a020eb24be0af37d4e82ed95 Mon Sep 17 00:00:00 2001 From: charafau Date: Mon, 20 Jun 2022 03:33:33 +0100 Subject: [PATCH 06/20] Add passing preffered size from surface to compositor --- compositor_dart/lib/compositor_dart.dart | 6 ++++++ demo/lib/main.dart | 5 +++-- demo/lib/window.dart | 7 ++++++- demo/lib/window_decoration.dart | 4 +++- src/surface.c | 13 +++++++++++-- 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/compositor_dart/lib/compositor_dart.dart b/compositor_dart/lib/compositor_dart.dart index 8107deb..ee3700c 100644 --- a/compositor_dart/lib/compositor_dart.dart +++ b/compositor_dart/lib/compositor_dart.dart @@ -23,6 +23,8 @@ class Surface { final int uid; final bool isPopup; final int parentHandle; + final int prefferedWidth; + final int prefferedHeight; Surface({ required this.handle, @@ -31,6 +33,8 @@ class Surface { required this.uid, required this.isPopup, required this.parentHandle, + required this.prefferedWidth, + required this.prefferedHeight, }); @override @@ -122,6 +126,8 @@ class Compositor { uid: call.arguments["client_uid"], isPopup: call.arguments["is_popup"], parentHandle: call.arguments["parent_handle"], + prefferedHeight: call.arguments['preffered_height'], + prefferedWidth: call.arguments['preffered_width'], ); surfaces.putIfAbsent(surface.handle, () => surface); diff --git a/demo/lib/main.dart b/demo/lib/main.dart index a47144e..4c2fc23 100644 --- a/demo/lib/main.dart +++ b/demo/lib/main.dart @@ -128,11 +128,12 @@ class _MyHomePageState extends State { return Window( initialX: isPopup ? mousePositionX : initialPositionX, initialY: isPopup ? mousePositionY : initialPositionY, + width: entry.value.prefferedWidth.toDouble(), shouldDecorate: !isPopup, onTap: () => focusView(entry.key), child: SizedBox( - width: windowWidth, - height: windowHeight, + width: entry.value.prefferedWidth.toDouble(), + height: entry.value.prefferedHeight.toDouble(), child: SurfaceView( surface: entry.value, compositor: compositor, diff --git a/demo/lib/window.dart b/demo/lib/window.dart index 75e6871..6c51fd9 100644 --- a/demo/lib/window.dart +++ b/demo/lib/window.dart @@ -7,6 +7,7 @@ class Window extends StatefulWidget { final Widget child; final VoidCallback onTap; final bool shouldDecorate; + final double width; const Window({ Key? key, @@ -15,6 +16,7 @@ class Window extends StatefulWidget { required this.child, required this.onTap, this.shouldDecorate = true, + required this.width, }) : super(key: key); @override @@ -46,7 +48,10 @@ class _WindowState extends State { }); }, child: widget.shouldDecorate - ? WindowDecoration(child: widget.child) + ? WindowDecoration( + width: widget.width, + child: widget.child, + ) : widget.child, ), ); diff --git a/demo/lib/window_decoration.dart b/demo/lib/window_decoration.dart index ed50ba5..8a3981e 100644 --- a/demo/lib/window_decoration.dart +++ b/demo/lib/window_decoration.dart @@ -3,10 +3,12 @@ import 'package:flutter/material.dart'; class WindowDecoration extends StatelessWidget { final Widget child; + final double width; const WindowDecoration({ Key? key, required this.child, + required this.width, }) : super(key: key); @override @@ -18,7 +20,7 @@ class WindowDecoration extends StatelessWidget { Container( color: Colors.red, height: 30, - width: windowWidth, + width: width, ), child, ], diff --git a/src/surface.c b/src/surface.c index 3b2bb4d..0572ccf 100644 --- a/src/surface.c +++ b/src/surface.c @@ -67,9 +67,11 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) { message_builder_segment_push_string(&msg_seg, "surface_map"); message_builder_segment_finish(&msg_seg); + struct wlr_box geometry = view->surface->current.geometry; + msg_seg = message_builder_segment(&msg); struct message_builder_segment arg_seg = - message_builder_segment_push_map(&msg_seg, 6); + message_builder_segment_push_map(&msg_seg, 8); message_builder_segment_push_string(&arg_seg, "handle"); wlr_log(WLR_INFO, "viewhandle %d", view->handle); message_builder_segment_push_int64(&arg_seg, view->handle); @@ -85,8 +87,15 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) { message_builder_segment_push_string(&arg_seg, "parent_handle"); message_builder_segment_push_int64(&arg_seg, view->parent_handle); - message_builder_segment_finish(&arg_seg); + message_builder_segment_push_string(&arg_seg, "preffered_width"); + message_builder_segment_push_int64(&arg_seg, geometry.width); + + message_builder_segment_push_string(&arg_seg, "preffered_height"); + message_builder_segment_push_int64(&arg_seg, geometry.height); + + message_builder_segment_finish(&arg_seg); + message_builder_segment_finish(&msg_seg); uint8_t *msg_buf; size_t msg_buf_len; From fd9fdee95c3b314135194d7dc728fca098f5b031 Mon Sep 17 00:00:00 2001 From: charafau Date: Tue, 21 Jun 2022 23:57:51 +0100 Subject: [PATCH 07/20] Remove reverse map Add function to find handle in map --- include/handle_map.h | 4 +--- src/handle_map.cc | 30 +++++++++--------------------- src/surface.c | 10 +++++++--- 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/include/handle_map.h b/include/handle_map.h index 761a007..5f249df 100644 --- a/include/handle_map.h +++ b/include/handle_map.h @@ -12,9 +12,7 @@ uint32_t handle_map_add(struct handle_map *map, void *value); bool handle_map_get(struct handle_map *map, uint32_t handle, void **out); bool handle_map_remove(struct handle_map *map, uint32_t handle); -void handle_map_add_reverse(struct handle_map *map, void* value, uint32_t handle); -uint32_t handle_map_find(struct handle_map *map, void* value); -bool handle_map_remove_reverse(struct handle_map *map, uint32_t handle); +uint32_t handle_map_find_handle(struct handle_map *map, void* (*get_data)(void* value)); #ifdef __cplusplus } diff --git a/src/handle_map.cc b/src/handle_map.cc index f8d1117..ab25959 100644 --- a/src/handle_map.cc +++ b/src/handle_map.cc @@ -7,11 +7,10 @@ struct handle_map { uint32_t next; std::map map; - std::map reverse_map; }; extern "C" handle_map *handle_map_new() { - handle_map *map = new handle_map({1, {}, {}}); + handle_map *map = new handle_map({1, {}}); return map; } @@ -45,29 +44,18 @@ extern "C" bool handle_map_get(handle_map *map, uint32_t handle, void **out) { } } -extern "C" void handle_map_add_reverse(handle_map *map, void* value, uint32_t handle) { - map->reverse_map.insert({value, handle}); -} - -extern "C" uint32_t handle_map_find(handle_map *map, void* value) { - - auto entry = map->reverse_map.find(value); - if (entry == map->reverse_map.end()) { - return -2; - } else { - return entry->second; - } -} +extern "C" uint32_t handle_map_find_handle(handle_map *map, void* (*get_data)(void* value)) { -extern "C" bool handle_map_remove_reverse(handle_map *map, uint32_t handle) { + int key = -2; - for (auto it = map->reverse_map.begin(); it != map->reverse_map.end(); ++it) { - if (it->second == handle){ - map->reverse_map.erase(it); - return true; + for(auto &it: map->map){ + if(it.second == get_data(it.second)) { + key = it.first; + break; } } - return false; + return key; } + diff --git a/src/surface.c b/src/surface.c index 0572ccf..3e1e9dc 100644 --- a/src/surface.c +++ b/src/surface.c @@ -159,11 +159,16 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { free(msg_buf); handle_map_remove(instance->views, view->handle); - handle_map_remove_reverse(instance->views, view->handle); } static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {} +void get_xdg_surface(void* fwr_view){ + struct fwr_view *view = fwr_view; + + return view->surface; +} + void fwr_new_xdg_surface(struct wl_listener *listener, void *data) { struct fwr_instance *instance = wl_container_of(listener, instance, new_xdg_surface); @@ -175,7 +180,7 @@ void fwr_new_xdg_surface(struct wl_listener *listener, void *data) { if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { parent = wlr_xdg_surface_from_wlr_surface(xdg_surface->popup->parent); - parent_handle = handle_map_find(instance->views, parent); + parent_handle = handle_map_find_handle(instance->views, get_xdg_surface); view->is_popup = true; view->parent_handle = parent_handle; } else { @@ -194,7 +199,6 @@ void fwr_new_xdg_surface(struct wl_listener *listener, void *data) { wl_signal_add(&xdg_surface->events.destroy, &view->destroy); uint32_t view_handle = handle_map_add(instance->views, (void *)view); - handle_map_add_reverse(instance->views, xdg_surface, view_handle); view->handle = view_handle; } From 7d23a403f4074195fee61b0953aa839f979b25af Mon Sep 17 00:00:00 2001 From: charafau Date: Wed, 22 Jun 2022 19:38:00 +0100 Subject: [PATCH 08/20] Remove map find function Add pointer to fwr_view in xdg_surface --- include/handle_map.h | 2 -- src/handle_map.cc | 15 --------------- src/surface.c | 11 +++-------- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/include/handle_map.h b/include/handle_map.h index 5f249df..3c95b18 100644 --- a/include/handle_map.h +++ b/include/handle_map.h @@ -12,8 +12,6 @@ uint32_t handle_map_add(struct handle_map *map, void *value); bool handle_map_get(struct handle_map *map, uint32_t handle, void **out); bool handle_map_remove(struct handle_map *map, uint32_t handle); -uint32_t handle_map_find_handle(struct handle_map *map, void* (*get_data)(void* value)); - #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/src/handle_map.cc b/src/handle_map.cc index ab25959..ceb273e 100644 --- a/src/handle_map.cc +++ b/src/handle_map.cc @@ -44,18 +44,3 @@ extern "C" bool handle_map_get(handle_map *map, uint32_t handle, void **out) { } } - -extern "C" uint32_t handle_map_find_handle(handle_map *map, void* (*get_data)(void* value)) { - - int key = -2; - - for(auto &it: map->map){ - if(it.second == get_data(it.second)) { - key = it.first; - break; - } - } - - return key; -} - diff --git a/src/surface.c b/src/surface.c index 3e1e9dc..1bbade4 100644 --- a/src/surface.c +++ b/src/surface.c @@ -163,26 +163,21 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {} -void get_xdg_surface(void* fwr_view){ - struct fwr_view *view = fwr_view; - - return view->surface; -} - void fwr_new_xdg_surface(struct wl_listener *listener, void *data) { struct fwr_instance *instance = wl_container_of(listener, instance, new_xdg_surface); struct wlr_xdg_surface *xdg_surface = data; struct fwr_view *view = calloc(1, sizeof(struct fwr_view)); + xdg_surface->data = view; struct wlr_xdg_surface *parent = 0; uint32_t parent_handle = -1; if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { parent = wlr_xdg_surface_from_wlr_surface(xdg_surface->popup->parent); - parent_handle = handle_map_find_handle(instance->views, get_xdg_surface); + struct fwr_view *parent_view = parent->data; view->is_popup = true; - view->parent_handle = parent_handle; + view->parent_handle = parent_view->handle; } else { view->is_popup = false; view->parent_handle = -1; From cd07c96ab8d00a73b09902289c410f37ba395151 Mon Sep 17 00:00:00 2001 From: charafau Date: Wed, 20 Jul 2022 00:13:53 +0100 Subject: [PATCH 09/20] Add window maximize event --- compositor_dart/lib/compositor_dart.dart | 25 ++++- demo/lib/constants.dart | 2 + demo/lib/main.dart | 86 +++++++++++------ demo/lib/window.dart | 6 +- demo/lib/window_decoration.dart | 4 +- include/input.h | 7 ++ include/instance.h | 8 ++ src/surface.c | 114 +++++++++++++++++++++++ 8 files changed, 217 insertions(+), 35 deletions(-) diff --git a/compositor_dart/lib/compositor_dart.dart b/compositor_dart/lib/compositor_dart.dart index ee3700c..a0b91e5 100644 --- a/compositor_dart/lib/compositor_dart.dart +++ b/compositor_dart/lib/compositor_dart.dart @@ -12,6 +12,18 @@ import 'package:logging/logging.dart'; enum KeyStatus { released, pressed } +enum WindowEventType { maximize, minimize } + +class WindowEvent { + final WindowEventType windowEventType; + final int handle; + + WindowEvent({ + required this.windowEventType, + required this.handle, + }); +} + class Surface { // This is actually used as a pointer on the compositor side. // It should always be returned exactly as is to the compositiors, @@ -25,6 +37,7 @@ class Surface { final int parentHandle; final int prefferedWidth; final int prefferedHeight; + bool isMaximized; Surface({ required this.handle, @@ -35,11 +48,12 @@ class Surface { required this.parentHandle, required this.prefferedWidth, required this.prefferedHeight, + this.isMaximized = false, }); @override String toString() { - return 'Surface(handle: $handle, pid: $pid, gid: $gid, uid: $uid, isPopup: $isPopup, parentHandle: $parentHandle)'; + return 'Surface(handle: $handle, pid: $pid, gid: $gid, uid: $uid, isPopup: $isPopup, parentHandle: $parentHandle, isMaximized: $isMaximized)'; } } @@ -114,6 +128,8 @@ class Compositor { // Emits an event when a surface has been added and is ready to be presented on the screen. StreamController surfaceMapped = StreamController.broadcast(); StreamController surfaceUnmapped = StreamController.broadcast(); + StreamController maximizeWindowEvents = + StreamController.broadcast(); int? keyToXkb(int physicalKey) => physicalToXkbMap[physicalKey]; @@ -144,5 +160,12 @@ class Compositor { }); platform.addHandler("flutter/keyevent", (call) async {}); + + platform.addHandler('window_maximize', (call) async { + int handle = call.arguments['handle']; + + maximizeWindowEvents.add(WindowEvent( + windowEventType: WindowEventType.maximize, handle: handle)); + }); } } diff --git a/demo/lib/constants.dart b/demo/lib/constants.dart index 1a5f85b..f231d89 100644 --- a/demo/lib/constants.dart +++ b/demo/lib/constants.dart @@ -6,3 +6,5 @@ const double windowWidth = 400; const double windowHeight = 500; const double initialPositionX = 200; const double initialPositionY = 70; +const double windowDecorationHeight = 30; +const Color windowDecorationColor = Colors.red; diff --git a/demo/lib/main.dart b/demo/lib/main.dart index 4c2fc23..3a3b7e1 100644 --- a/demo/lib/main.dart +++ b/demo/lib/main.dart @@ -86,6 +86,15 @@ class _MyHomePageState extends State { } }); }); + + compositor.maximizeWindowEvents.stream.listen((WindowEvent event) { + if (event.windowEventType == WindowEventType.maximize) { + setState(() { + surfaces[event.handle]!.isMaximized = + !surfaces[event.handle]!.isMaximized; + }); + } + }); } void focusView(int handle) { @@ -115,37 +124,54 @@ class _MyHomePageState extends State { appBar: AppBar( title: Text(widget.title), ), - body: SizedBox.expand( - child: MouseRegion( - onHover: (PointerHoverEvent event) { - mousePositionX = event.position.dx; - mousePositionY = event.position.dy; - }, - child: Stack( - children: surfaces.entries.map((MapEntry entry) { - final isPopup = entry.value.isPopup; - - return Window( - initialX: isPopup ? mousePositionX : initialPositionX, - initialY: isPopup ? mousePositionY : initialPositionY, - width: entry.value.prefferedWidth.toDouble(), - shouldDecorate: !isPopup, - onTap: () => focusView(entry.key), - child: SizedBox( - width: entry.value.prefferedWidth.toDouble(), - height: entry.value.prefferedHeight.toDouble(), - child: SurfaceView( - surface: entry.value, - compositor: compositor, - onPointerClick: (Surface surface) { - focusView(surface.handle); - }), - ), - ); - }).toList(), + body: LayoutBuilder(builder: (context, constraints) { + return SizedBox.expand( + child: MouseRegion( + onHover: (PointerHoverEvent event) { + mousePositionX = event.position.dx; + mousePositionY = event.position.dy; + }, + child: Stack( + children: surfaces.entries.map((MapEntry entry) { + final isPopup = entry.value.isPopup; + + return Window( + initialX: entry.value.isMaximized + ? 0 + : (isPopup ? mousePositionX : initialPositionX), + initialY: entry.value.isMaximized + ? 0 + : (isPopup ? mousePositionY : initialPositionY), + width: entry.value.isMaximized + ? constraints.maxWidth + : entry.value.prefferedWidth.toDouble(), + shouldDecorate: !isPopup, + isMaximized: entry.value.isMaximized, + onTap: () => focusView(entry.key), + child: SizedBox( + width: entry.value.isMaximized + ? constraints.maxWidth + : (entry.value.prefferedWidth != 0 + ? entry.value.prefferedWidth.toDouble() + : 200), + height: entry.value.isMaximized + ? (constraints.maxHeight - windowDecorationHeight) + : (entry.value.prefferedHeight != 0 + ? entry.value.prefferedHeight.toDouble() + : 300), + child: SurfaceView( + surface: entry.value, + compositor: compositor, + onPointerClick: (Surface surface) { + focusView(surface.handle); + }), + ), + ); + }).toList(), + ), ), - ), - ), + ); + }), ), ); } diff --git a/demo/lib/window.dart b/demo/lib/window.dart index 6c51fd9..ef2a498 100644 --- a/demo/lib/window.dart +++ b/demo/lib/window.dart @@ -8,6 +8,7 @@ class Window extends StatefulWidget { final VoidCallback onTap; final bool shouldDecorate; final double width; + final bool isMaximized; const Window({ Key? key, @@ -17,6 +18,7 @@ class Window extends StatefulWidget { required this.onTap, this.shouldDecorate = true, required this.width, + required this.isMaximized, }) : super(key: key); @override @@ -37,8 +39,8 @@ class _WindowState extends State { @override Widget build(BuildContext context) { return Positioned( - left: initialX, - top: initialY, + left: widget.isMaximized ? 0 : initialX, + top: widget.isMaximized ? 0 : initialY, child: GestureDetector( onTap: widget.onTap, onPanUpdate: (DragUpdateDetails details) { diff --git a/demo/lib/window_decoration.dart b/demo/lib/window_decoration.dart index 8a3981e..e6e2fd8 100644 --- a/demo/lib/window_decoration.dart +++ b/demo/lib/window_decoration.dart @@ -18,8 +18,8 @@ class WindowDecoration extends StatelessWidget { child: Column( children: [ Container( - color: Colors.red, - height: 30, + color: windowDecorationColor, + height: windowDecorationHeight, width: width, ), child, diff --git a/include/input.h b/include/input.h index 23705fe..a8c8d12 100644 --- a/include/input.h +++ b/include/input.h @@ -6,6 +6,13 @@ #include "shaders.h" #include "standard_message_codec.h" +enum fwr_cursor_mode { + FWR_CURSOR_PASSTHROUGH, + FWR_CURSOR_MOVE, + FWR_CURSOR_RESIZE, +}; + + struct fwr_input_state { uint32_t mouse_button_mask; uint32_t fl_mouse_button_mask; diff --git a/include/instance.h b/include/instance.h index cd2a896..f71b621 100644 --- a/include/instance.h +++ b/include/instance.h @@ -90,6 +90,7 @@ struct fwr_view { uint32_t handle; struct fwr_instance *instance; + struct wlr_xdg_toplevel *xdg_toplevel; struct wlr_xdg_surface *surface; uint32_t parent_handle; bool is_popup; @@ -98,6 +99,13 @@ struct fwr_view { struct wl_listener unmap; struct wl_listener destroy; struct wl_listener commit; + + struct wl_listener request_move; + struct wl_listener request_resize; + struct wl_listener request_maximize; + struct wl_listener request_fullscreen; + struct wl_listener request_minimize; + }; struct fwr_keyboard { diff --git a/src/surface.c b/src/surface.c index 1bbade4..d49dff3 100644 --- a/src/surface.c +++ b/src/surface.c @@ -163,12 +163,103 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {} +static void xdg_toplevel_request_move( + struct wl_listener *listener, void *data) { + /* This event is raised when a client would like to begin an interactive + * move, typically because the user clicked on their client-side + * decorations. Note that a more sophisticated compositor should check the + * provided serial against a list of button press serials sent to this + * client, to prevent the client from requesting this whenever they want. */ + struct fwr_view *view = wl_container_of(listener, view, request_move); + // begin_interactive(view, FWR_CURSOR_MOVE, 0); +} + +static void xdg_toplevel_request_resize( + struct wl_listener *listener, void *data) { + /* This event is raised when a client would like to begin an interactive + * resize, typically because the user clicked on their client-side + * decorations. Note that a more sophisticated compositor should check the + * provided serial against a list of button press serials sent to this + * client, to prevent the client from requesting this whenever they want. */ + struct wlr_xdg_toplevel_resize_event *event = data; + struct fwr_view *view = wl_container_of(listener, view, request_resize); + wlr_log(WLR_INFO, "request resize for view %d", view->handle); + // begin_interactive(view, FWR_CURSOR_RESIZE, event->edges); +} + +static void xdg_toplevel_request_maximize( + struct wl_listener *listener, void *data) { + /* This event is raised when a client would like to maximize itself, + * typically because the user clicked on the maximize button on + * client-side decorations. tinywl doesn't support maximization, but + * to conform to xdg-shell protocol we still must send a configure. + * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ + struct fwr_view *view = + wl_container_of(listener, view, request_maximize); + wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); + struct fwr_instance *instance = view->instance; + + struct message_builder msg = message_builder_new(); + struct message_builder_segment msg_seg = message_builder_segment(&msg); + message_builder_segment_push_string(&msg_seg, "window_maximize"); + message_builder_segment_finish(&msg_seg); + + msg_seg = message_builder_segment(&msg); + struct message_builder_segment arg_seg = + message_builder_segment_push_map(&msg_seg, 1); + message_builder_segment_push_string(&arg_seg, "handle"); + message_builder_segment_push_int64(&arg_seg, view->handle); + message_builder_segment_finish(&arg_seg); + + message_builder_segment_finish(&msg_seg); + uint8_t *msg_buf; + size_t msg_buf_len; + message_builder_finish(&msg, &msg_buf, &msg_buf_len); + + FlutterPlatformMessageResponseHandle *response_handle; + instance->fl_proc_table.PlatformMessageCreateResponseHandle( + instance->engine, cb, NULL, &response_handle); + + FlutterPlatformMessage platform_message = {}; + platform_message.struct_size = sizeof(FlutterPlatformMessage); + platform_message.channel = "wlroots"; + platform_message.message = msg_buf; + platform_message.message_size = msg_buf_len; + platform_message.response_handle = response_handle; + instance->fl_proc_table.SendPlatformMessage(instance->engine, + &platform_message); + + + free(msg_buf); + +} + +static void xdg_toplevel_request_fullscreen( + struct wl_listener *listener, void *data) { + + /* Just as with request_maximize, we must send a configure here. */ + struct fwr_view *view = + wl_container_of(listener, view, request_fullscreen); + wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); +} + +static void xdg_toplevel_request_minimize( + struct wl_listener *listener, void *data) { + + /* Just as with request_minimize, we must send a configure here. */ + struct fwr_view *view = + wl_container_of(listener, view, request_minimize); + wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); +} + + void fwr_new_xdg_surface(struct wl_listener *listener, void *data) { struct fwr_instance *instance = wl_container_of(listener, instance, new_xdg_surface); struct wlr_xdg_surface *xdg_surface = data; struct fwr_view *view = calloc(1, sizeof(struct fwr_view)); xdg_surface->data = view; + view->xdg_toplevel = xdg_surface->toplevel; struct wlr_xdg_surface *parent = 0; uint32_t parent_handle = -1; @@ -193,6 +284,29 @@ void fwr_new_xdg_surface(struct wl_listener *listener, void *data) { view->destroy.notify = xdg_toplevel_destroy; wl_signal_add(&xdg_surface->events.destroy, &view->destroy); + + struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel; + view->request_move.notify = xdg_toplevel_request_move; + wl_signal_add(&toplevel->events.request_move, &view->request_move); + + + view->request_resize.notify = xdg_toplevel_request_resize; + wl_signal_add(&toplevel->events.request_resize, &view->request_resize); + + view->request_maximize.notify = xdg_toplevel_request_maximize; + wl_signal_add(&toplevel->events.request_maximize, + &view->request_maximize); + view->request_fullscreen.notify = xdg_toplevel_request_fullscreen; + wl_signal_add(&toplevel->events.request_fullscreen, + &view->request_fullscreen); + + view->request_minimize.notify = xdg_toplevel_request_fullscreen; + wl_signal_add(&toplevel->events.request_minimize, + &view->request_minimize); + + + + uint32_t view_handle = handle_map_add(instance->views, (void *)view); view->handle = view_handle; From c348f7c15aaccbad93b5001176ef073959cb69d9 Mon Sep 17 00:00:00 2001 From: charafau Date: Wed, 20 Jul 2022 00:54:15 +0100 Subject: [PATCH 10/20] add minimize event handling --- compositor_dart/lib/compositor_dart.dart | 16 +++-- demo/lib/main.dart | 13 +++- src/surface.c | 76 ++++++++++++++---------- 3 files changed, 66 insertions(+), 39 deletions(-) diff --git a/compositor_dart/lib/compositor_dart.dart b/compositor_dart/lib/compositor_dart.dart index a0b91e5..73fb105 100644 --- a/compositor_dart/lib/compositor_dart.dart +++ b/compositor_dart/lib/compositor_dart.dart @@ -38,6 +38,7 @@ class Surface { final int prefferedWidth; final int prefferedHeight; bool isMaximized; + bool isMinimized; Surface({ required this.handle, @@ -49,11 +50,12 @@ class Surface { required this.prefferedWidth, required this.prefferedHeight, this.isMaximized = false, + this.isMinimized = false, }); @override String toString() { - return 'Surface(handle: $handle, pid: $pid, gid: $gid, uid: $uid, isPopup: $isPopup, parentHandle: $parentHandle, isMaximized: $isMaximized)'; + return 'Surface(handle: $handle, pid: $pid, gid: $gid, uid: $uid, isPopup: $isPopup, parentHandle: $parentHandle, isMaximized: $isMaximized, isMinimized: $isMinimized)'; } } @@ -128,8 +130,7 @@ class Compositor { // Emits an event when a surface has been added and is ready to be presented on the screen. StreamController surfaceMapped = StreamController.broadcast(); StreamController surfaceUnmapped = StreamController.broadcast(); - StreamController maximizeWindowEvents = - StreamController.broadcast(); + StreamController windowEvents = StreamController.broadcast(); int? keyToXkb(int physicalKey) => physicalToXkbMap[physicalKey]; @@ -164,8 +165,15 @@ class Compositor { platform.addHandler('window_maximize', (call) async { int handle = call.arguments['handle']; - maximizeWindowEvents.add(WindowEvent( + windowEvents.add(WindowEvent( windowEventType: WindowEventType.maximize, handle: handle)); }); + + platform.addHandler('window_minimize', (call) async { + int handle = call.arguments['handle']; + + windowEvents.add(WindowEvent( + windowEventType: WindowEventType.minimize, handle: handle)); + }); } } diff --git a/demo/lib/main.dart b/demo/lib/main.dart index 3a3b7e1..04175fc 100644 --- a/demo/lib/main.dart +++ b/demo/lib/main.dart @@ -87,13 +87,20 @@ class _MyHomePageState extends State { }); }); - compositor.maximizeWindowEvents.stream.listen((WindowEvent event) { + compositor.windowEvents.stream.listen((WindowEvent event) { if (event.windowEventType == WindowEventType.maximize) { setState(() { surfaces[event.handle]!.isMaximized = !surfaces[event.handle]!.isMaximized; }); } + + if (event.windowEventType == WindowEventType.minimize) { + setState(() { + surfaces[event.handle]!.isMinimized = + !surfaces[event.handle]!.isMinimized; + }); + } }); } @@ -132,7 +139,9 @@ class _MyHomePageState extends State { mousePositionY = event.position.dy; }, child: Stack( - children: surfaces.entries.map((MapEntry entry) { + children: surfaces.entries + .skipWhile((entry) => entry.value.isMinimized) + .map((MapEntry entry) { final isPopup = entry.value.isPopup; return Window( diff --git a/src/surface.c b/src/surface.c index d49dff3..fd4c31f 100644 --- a/src/surface.c +++ b/src/surface.c @@ -163,6 +163,44 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {} + +static void send_window_event(struct fwr_instance *instance, uint32_t handle, const char *event) { + + struct message_builder msg = message_builder_new(); + struct message_builder_segment msg_seg = message_builder_segment(&msg); + message_builder_segment_push_string(&msg_seg, event); + message_builder_segment_finish(&msg_seg); + + msg_seg = message_builder_segment(&msg); + struct message_builder_segment arg_seg = + message_builder_segment_push_map(&msg_seg, 1); + message_builder_segment_push_string(&arg_seg, "handle"); + message_builder_segment_push_int64(&arg_seg, handle); + message_builder_segment_finish(&arg_seg); + + message_builder_segment_finish(&msg_seg); + uint8_t *msg_buf; + size_t msg_buf_len; + message_builder_finish(&msg, &msg_buf, &msg_buf_len); + + FlutterPlatformMessageResponseHandle *response_handle; + instance->fl_proc_table.PlatformMessageCreateResponseHandle( + instance->engine, cb, NULL, &response_handle); + + FlutterPlatformMessage platform_message = {}; + platform_message.struct_size = sizeof(FlutterPlatformMessage); + platform_message.channel = "wlroots"; + platform_message.message = msg_buf; + platform_message.message_size = msg_buf_len; + platform_message.response_handle = response_handle; + instance->fl_proc_table.SendPlatformMessage(instance->engine, + &platform_message); + + + free(msg_buf); + +} + static void xdg_toplevel_request_move( struct wl_listener *listener, void *data) { /* This event is raised when a client would like to begin an interactive @@ -199,38 +237,7 @@ static void xdg_toplevel_request_maximize( wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); struct fwr_instance *instance = view->instance; - struct message_builder msg = message_builder_new(); - struct message_builder_segment msg_seg = message_builder_segment(&msg); - message_builder_segment_push_string(&msg_seg, "window_maximize"); - message_builder_segment_finish(&msg_seg); - - msg_seg = message_builder_segment(&msg); - struct message_builder_segment arg_seg = - message_builder_segment_push_map(&msg_seg, 1); - message_builder_segment_push_string(&arg_seg, "handle"); - message_builder_segment_push_int64(&arg_seg, view->handle); - message_builder_segment_finish(&arg_seg); - - message_builder_segment_finish(&msg_seg); - uint8_t *msg_buf; - size_t msg_buf_len; - message_builder_finish(&msg, &msg_buf, &msg_buf_len); - - FlutterPlatformMessageResponseHandle *response_handle; - instance->fl_proc_table.PlatformMessageCreateResponseHandle( - instance->engine, cb, NULL, &response_handle); - - FlutterPlatformMessage platform_message = {}; - platform_message.struct_size = sizeof(FlutterPlatformMessage); - platform_message.channel = "wlroots"; - platform_message.message = msg_buf; - platform_message.message_size = msg_buf_len; - platform_message.response_handle = response_handle; - instance->fl_proc_table.SendPlatformMessage(instance->engine, - &platform_message); - - - free(msg_buf); + send_window_event(instance, view->handle, "window_maximize"); } @@ -250,6 +257,9 @@ static void xdg_toplevel_request_minimize( struct fwr_view *view = wl_container_of(listener, view, request_minimize); wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); + struct fwr_instance *instance = view->instance; + + send_window_event(instance, view->handle, "window_minimize"); } @@ -300,7 +310,7 @@ void fwr_new_xdg_surface(struct wl_listener *listener, void *data) { wl_signal_add(&toplevel->events.request_fullscreen, &view->request_fullscreen); - view->request_minimize.notify = xdg_toplevel_request_fullscreen; + view->request_minimize.notify = xdg_toplevel_request_minimize; wl_signal_add(&toplevel->events.request_minimize, &view->request_minimize); From f0538202204ad1aed6e01d01e190903307426d55 Mon Sep 17 00:00:00 2001 From: charafau Date: Wed, 20 Jul 2022 01:01:21 +0100 Subject: [PATCH 11/20] pass fullscreen request to compositor --- src/surface.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/surface.c b/src/surface.c index fd4c31f..34bb604 100644 --- a/src/surface.c +++ b/src/surface.c @@ -248,6 +248,8 @@ static void xdg_toplevel_request_fullscreen( struct fwr_view *view = wl_container_of(listener, view, request_fullscreen); wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); + struct fwr_instance *instance = view->instance; + send_window_event(instance, view->handle, "window_fullscreen"); } static void xdg_toplevel_request_minimize( From a5805ddb41f878689464578f3a74581b496d908d Mon Sep 17 00:00:00 2001 From: charafau Date: Wed, 20 Jul 2022 01:15:31 +0100 Subject: [PATCH 12/20] pass window move event --- src/surface.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/surface.c b/src/surface.c index 34bb604..63ea6f9 100644 --- a/src/surface.c +++ b/src/surface.c @@ -209,7 +209,10 @@ static void xdg_toplevel_request_move( * provided serial against a list of button press serials sent to this * client, to prevent the client from requesting this whenever they want. */ struct fwr_view *view = wl_container_of(listener, view, request_move); + // should change cursor in wlroots // begin_interactive(view, FWR_CURSOR_MOVE, 0); + struct fwr_instance *instance = view->instance; + send_window_event(instance, view->handle, "window_move"); } static void xdg_toplevel_request_resize( From b3d8cd6c18be3557ae2dfae368d178cbf7f1ea0e Mon Sep 17 00:00:00 2001 From: charafau Date: Wed, 20 Jul 2022 01:24:41 +0100 Subject: [PATCH 13/20] pass resize event fix qt pop ups --- src/surface.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/surface.c b/src/surface.c index 63ea6f9..86fcfc0 100644 --- a/src/surface.c +++ b/src/surface.c @@ -226,6 +226,8 @@ static void xdg_toplevel_request_resize( struct fwr_view *view = wl_container_of(listener, view, request_resize); wlr_log(WLR_INFO, "request resize for view %d", view->handle); // begin_interactive(view, FWR_CURSOR_RESIZE, event->edges); + struct fwr_instance *instance = view->instance; + send_window_event(instance, view->handle, "window_resize"); } static void xdg_toplevel_request_maximize( @@ -300,25 +302,28 @@ void fwr_new_xdg_surface(struct wl_listener *listener, void *data) { wl_signal_add(&xdg_surface->events.destroy, &view->destroy); - struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel; - view->request_move.notify = xdg_toplevel_request_move; - wl_signal_add(&toplevel->events.request_move, &view->request_move); + // only set window events if not pop-up, otherwise, exception for qt + if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel; + view->request_move.notify = xdg_toplevel_request_move; + wl_signal_add(&toplevel->events.request_move, &view->request_move); - view->request_resize.notify = xdg_toplevel_request_resize; - wl_signal_add(&toplevel->events.request_resize, &view->request_resize); + view->request_resize.notify = xdg_toplevel_request_resize; + wl_signal_add(&toplevel->events.request_resize, &view->request_resize); - view->request_maximize.notify = xdg_toplevel_request_maximize; - wl_signal_add(&toplevel->events.request_maximize, - &view->request_maximize); - view->request_fullscreen.notify = xdg_toplevel_request_fullscreen; - wl_signal_add(&toplevel->events.request_fullscreen, - &view->request_fullscreen); + view->request_maximize.notify = xdg_toplevel_request_maximize; + wl_signal_add(&toplevel->events.request_maximize, + &view->request_maximize); + view->request_fullscreen.notify = xdg_toplevel_request_fullscreen; + wl_signal_add(&toplevel->events.request_fullscreen, + &view->request_fullscreen); - view->request_minimize.notify = xdg_toplevel_request_minimize; - wl_signal_add(&toplevel->events.request_minimize, - &view->request_minimize); + view->request_minimize.notify = xdg_toplevel_request_minimize; + wl_signal_add(&toplevel->events.request_minimize, + &view->request_minimize); + } From 2d16c6fbcfa9bdeffd16e11357c54638a06a1982 Mon Sep 17 00:00:00 2001 From: charafau Date: Wed, 20 Jul 2022 02:01:47 +0100 Subject: [PATCH 14/20] set preffered with from toplevel state for popups --- src/surface.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/surface.c b/src/surface.c index 86fcfc0..a82ff6e 100644 --- a/src/surface.c +++ b/src/surface.c @@ -68,6 +68,7 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) { message_builder_segment_finish(&msg_seg); struct wlr_box geometry = view->surface->current.geometry; + struct wlr_xdg_toplevel_state toplevel_state = view->xdg_toplevel->current; msg_seg = message_builder_segment(&msg); struct message_builder_segment arg_seg = @@ -88,11 +89,19 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) { message_builder_segment_push_string(&arg_seg, "parent_handle"); message_builder_segment_push_int64(&arg_seg, view->parent_handle); - message_builder_segment_push_string(&arg_seg, "preffered_width"); - message_builder_segment_push_int64(&arg_seg, geometry.width); + if(view->is_popup){ + message_builder_segment_push_string(&arg_seg, "preffered_width"); + message_builder_segment_push_int64(&arg_seg, toplevel_state.width); - message_builder_segment_push_string(&arg_seg, "preffered_height"); - message_builder_segment_push_int64(&arg_seg, geometry.height); + message_builder_segment_push_string(&arg_seg, "preffered_height"); + message_builder_segment_push_int64(&arg_seg, toplevel_state.height); + } else { + message_builder_segment_push_string(&arg_seg, "preffered_width"); + message_builder_segment_push_int64(&arg_seg, geometry.width); + + message_builder_segment_push_string(&arg_seg, "preffered_height"); + message_builder_segment_push_int64(&arg_seg, geometry.height); + } message_builder_segment_finish(&arg_seg); From d5ac87dd3e42fe24e9ccef64b261cd5cdf5104d0 Mon Sep 17 00:00:00 2001 From: charafau Date: Fri, 22 Jul 2022 02:49:34 +0100 Subject: [PATCH 15/20] refactor window decoration --- demo/lib/constants.dart | 3 ++- demo/lib/main.dart | 3 +++ demo/lib/window.dart | 6 +++++- demo/lib/window_decoration.dart | 11 ++++++++--- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/demo/lib/constants.dart b/demo/lib/constants.dart index f231d89..484a898 100644 --- a/demo/lib/constants.dart +++ b/demo/lib/constants.dart @@ -7,4 +7,5 @@ const double windowHeight = 500; const double initialPositionX = 200; const double initialPositionY = 70; const double windowDecorationHeight = 30; -const Color windowDecorationColor = Colors.red; +const Color windowDecorationColor = Color(0xff1c1c1e); +const borderWidth = 1; diff --git a/demo/lib/main.dart b/demo/lib/main.dart index 04175fc..33e7ba2 100644 --- a/demo/lib/main.dart +++ b/demo/lib/main.dart @@ -154,6 +154,9 @@ class _MyHomePageState extends State { width: entry.value.isMaximized ? constraints.maxWidth : entry.value.prefferedWidth.toDouble(), + height: entry.value.isMaximized + ? constraints.maxHeight + : entry.value.prefferedHeight.toDouble(), shouldDecorate: !isPopup, isMaximized: entry.value.isMaximized, onTap: () => focusView(entry.key), diff --git a/demo/lib/window.dart b/demo/lib/window.dart index ef2a498..38b166f 100644 --- a/demo/lib/window.dart +++ b/demo/lib/window.dart @@ -1,3 +1,4 @@ +import 'package:demo/constants.dart'; import 'package:demo/window_decoration.dart'; import 'package:flutter/material.dart'; @@ -8,6 +9,7 @@ class Window extends StatefulWidget { final VoidCallback onTap; final bool shouldDecorate; final double width; + final double height; final bool isMaximized; const Window({ @@ -18,6 +20,7 @@ class Window extends StatefulWidget { required this.onTap, this.shouldDecorate = true, required this.width, + required this.height, required this.isMaximized, }) : super(key: key); @@ -51,7 +54,8 @@ class _WindowState extends State { }, child: widget.shouldDecorate ? WindowDecoration( - width: widget.width, + width: widget.width + borderWidth * 2, + height: widget.height + borderWidth + windowDecorationHeight, child: widget.child, ) : widget.child, diff --git a/demo/lib/window_decoration.dart b/demo/lib/window_decoration.dart index e6e2fd8..c141ed0 100644 --- a/demo/lib/window_decoration.dart +++ b/demo/lib/window_decoration.dart @@ -4,25 +4,30 @@ import 'package:flutter/material.dart'; class WindowDecoration extends StatelessWidget { final Widget child; final double width; + final double height; const WindowDecoration({ Key? key, required this.child, required this.width, + required this.height, }) : super(key: key); @override Widget build(BuildContext context) { return ClipRRect( borderRadius: systemBorderRadius, - child: Column( + child: Stack( children: [ Container( color: windowDecorationColor, - height: windowDecorationHeight, + height: height, width: width, ), - child, + Padding( + padding: const EdgeInsets.only(top: windowDecorationHeight), + child: child, + ), ], ), ); From 478aa92aa2074c2aa942ac4587eb58eea63bc0de Mon Sep 17 00:00:00 2001 From: charafau Date: Fri, 22 Jul 2022 04:08:15 +0100 Subject: [PATCH 16/20] add window resizing --- demo/lib/main.dart | 24 ++++--------- demo/lib/window.dart | 84 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 81 insertions(+), 27 deletions(-) diff --git a/demo/lib/main.dart b/demo/lib/main.dart index 33e7ba2..442cbae 100644 --- a/demo/lib/main.dart +++ b/demo/lib/main.dart @@ -160,24 +160,12 @@ class _MyHomePageState extends State { shouldDecorate: !isPopup, isMaximized: entry.value.isMaximized, onTap: () => focusView(entry.key), - child: SizedBox( - width: entry.value.isMaximized - ? constraints.maxWidth - : (entry.value.prefferedWidth != 0 - ? entry.value.prefferedWidth.toDouble() - : 200), - height: entry.value.isMaximized - ? (constraints.maxHeight - windowDecorationHeight) - : (entry.value.prefferedHeight != 0 - ? entry.value.prefferedHeight.toDouble() - : 300), - child: SurfaceView( - surface: entry.value, - compositor: compositor, - onPointerClick: (Surface surface) { - focusView(surface.handle); - }), - ), + child: SurfaceView( + surface: entry.value, + compositor: compositor, + onPointerClick: (Surface surface) { + focusView(surface.handle); + }), ); }).toList(), ), diff --git a/demo/lib/window.dart b/demo/lib/window.dart index 38b166f..6c0859a 100644 --- a/demo/lib/window.dart +++ b/demo/lib/window.dart @@ -31,34 +31,100 @@ class Window extends StatefulWidget { class _WindowState extends State { late double initialX; late double initialY; + late double windowWidth; + late double windowHeight; + late bool isResizingLeft; + late bool isResizingRight; + late bool isResizingTop; + late bool isResizingBottom; @override void initState() { super.initState(); initialX = widget.initialX; initialY = widget.initialY; + if (widget.shouldDecorate) { + windowWidth = widget.width + borderWidth * 2; + windowHeight = widget.height + borderWidth + windowDecorationHeight; + } else { + windowWidth = widget.width + borderWidth * 2; + windowHeight = widget.height + borderWidth; + } + isResizingLeft = false; + isResizingRight = false; + isResizingTop = false; + isResizingBottom = false; } @override Widget build(BuildContext context) { + final child = widget.shouldDecorate + ? WindowDecoration( + width: windowWidth + borderWidth * 2, + height: windowHeight + borderWidth + windowDecorationHeight, + child: widget.child, + ) + : widget.child; + return Positioned( left: widget.isMaximized ? 0 : initialX, top: widget.isMaximized ? 0 : initialY, child: GestureDetector( onTap: widget.onTap, + onPanStart: (details) { + if (details.localPosition.dx <= 10) { + isResizingLeft = true; + } + if (details.localPosition.dx >= windowWidth - 10) { + isResizingRight = true; + } + if (details.localPosition.dy <= 10) { + isResizingTop = true; + } + if (details.localPosition.dy >= windowHeight - 10) { + isResizingBottom = true; + } + setState(() {}); + }, onPanUpdate: (DragUpdateDetails details) { setState(() { - initialX += details.delta.dx; - initialY += details.delta.dy; + if (isResizingLeft) { + initialX += details.delta.dx; + windowWidth += (-details.delta.dx); + } + if (isResizingRight) { + windowWidth += details.delta.dx; + } + if (isResizingTop) { + initialY += details.delta.dy; + windowHeight += (-details.delta.dy); + } + if (isResizingBottom) { + windowHeight += details.delta.dy; + } + + if (!isResizingLeft && + !isResizingRight && + !isResizingTop && + !isResizingBottom) { + initialX += details.delta.dx; + initialY += details.delta.dy; + } + }); + }, + onPanEnd: (details) { + setState(() { + isResizingLeft = false; + isResizingRight = false; + isResizingTop = false; + isResizingBottom = false; }); }, - child: widget.shouldDecorate - ? WindowDecoration( - width: widget.width + borderWidth * 2, - height: widget.height + borderWidth + windowDecorationHeight, - child: widget.child, - ) - : widget.child, + child: SizedBox( + width: windowWidth, + height: windowHeight, + child: child, + ), ), ); } From 8f5c45011395bb44ce28714e0cb8e6351295fc32 Mon Sep 17 00:00:00 2001 From: charafau Date: Fri, 22 Jul 2022 04:16:05 +0100 Subject: [PATCH 17/20] add window control --- demo/lib/constants.dart | 2 ++ demo/lib/window_decoration.dart | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/demo/lib/constants.dart b/demo/lib/constants.dart index 484a898..70df71e 100644 --- a/demo/lib/constants.dart +++ b/demo/lib/constants.dart @@ -7,5 +7,7 @@ const double windowHeight = 500; const double initialPositionX = 200; const double initialPositionY = 70; const double windowDecorationHeight = 30; +const double windowDecorationControlSize = 14; const Color windowDecorationColor = Color(0xff1c1c1e); +const Color windowDecorationControlColor = Color(0xffffffff); const borderWidth = 1; diff --git a/demo/lib/window_decoration.dart b/demo/lib/window_decoration.dart index c141ed0..6eb8c13 100644 --- a/demo/lib/window_decoration.dart +++ b/demo/lib/window_decoration.dart @@ -28,6 +28,18 @@ class WindowDecoration extends StatelessWidget { padding: const EdgeInsets.only(top: windowDecorationHeight), child: child, ), + Positioned( + right: 0, + child: IconButton( + iconSize: windowDecorationControlSize, + icon: const Icon( + Icons.close, + color: windowDecorationControlColor, + size: windowDecorationControlSize, + ), + onPressed: () {}, + ), + ), ], ), ); From 45eab9e2b50e470b1db5b3e9752dd798cd50392a Mon Sep 17 00:00:00 2001 From: charafau Date: Sun, 7 Aug 2022 22:28:52 +0900 Subject: [PATCH 18/20] pass surface width and height pass offsets for surface pass contante widht and height --- compositor_dart/lib/compositor_dart.dart | 30 +++++++++--- demo/lib/main.dart | 16 ++++--- demo/lib/window_clipper.dart | 29 ++++++++++++ include/instance.h | 3 ++ src/surface.c | 58 ++++++++++++++++++------ 5 files changed, 108 insertions(+), 28 deletions(-) create mode 100644 demo/lib/window_clipper.dart diff --git a/compositor_dart/lib/compositor_dart.dart b/compositor_dart/lib/compositor_dart.dart index 73fb105..706bbdb 100644 --- a/compositor_dart/lib/compositor_dart.dart +++ b/compositor_dart/lib/compositor_dart.dart @@ -35,8 +35,14 @@ class Surface { final int uid; final bool isPopup; final int parentHandle; - final int prefferedWidth; - final int prefferedHeight; + final int surfaceWidth; + final int surfaceHeight; + final int offsetTop; + final int offsetLeft; + final int offsetRight; + final int offsetBottom; + final int contentWidth; + final int contentHeight; bool isMaximized; bool isMinimized; @@ -47,8 +53,14 @@ class Surface { required this.uid, required this.isPopup, required this.parentHandle, - required this.prefferedWidth, - required this.prefferedHeight, + required this.surfaceWidth, + required this.surfaceHeight, + required this.offsetTop, + required this.offsetLeft, + required this.offsetRight, + required this.offsetBottom, + required this.contentWidth, + required this.contentHeight, this.isMaximized = false, this.isMinimized = false, }); @@ -143,8 +155,14 @@ class Compositor { uid: call.arguments["client_uid"], isPopup: call.arguments["is_popup"], parentHandle: call.arguments["parent_handle"], - prefferedHeight: call.arguments['preffered_height'], - prefferedWidth: call.arguments['preffered_width'], + surfaceHeight: call.arguments['surface_height'], + surfaceWidth: call.arguments['surface_width'], + offsetTop: call.arguments['offset_top'], + offsetLeft: call.arguments['offset_left'], + offsetRight: call.arguments['offset_right'], + offsetBottom: call.arguments['offset_bottom'], + contentWidth: call.arguments['content_width'], + contentHeight: call.arguments['content_height'], ); surfaces.putIfAbsent(surface.handle, () => surface); diff --git a/demo/lib/main.dart b/demo/lib/main.dart index 442cbae..214d71c 100644 --- a/demo/lib/main.dart +++ b/demo/lib/main.dart @@ -2,6 +2,7 @@ import 'package:compositor_dart/compositor_dart.dart'; import 'package:compositor_dart/surface.dart'; import 'package:demo/constants.dart'; import 'package:demo/window.dart'; +import 'package:demo/window_clipper.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -153,19 +154,20 @@ class _MyHomePageState extends State { : (isPopup ? mousePositionY : initialPositionY), width: entry.value.isMaximized ? constraints.maxWidth - : entry.value.prefferedWidth.toDouble(), + : entry.value.contentWidth.toDouble(), height: entry.value.isMaximized ? constraints.maxHeight - : entry.value.prefferedHeight.toDouble(), + : entry.value.contentHeight.toDouble(), shouldDecorate: !isPopup, isMaximized: entry.value.isMaximized, onTap: () => focusView(entry.key), child: SurfaceView( - surface: entry.value, - compositor: compositor, - onPointerClick: (Surface surface) { - focusView(surface.handle); - }), + surface: entry.value, + compositor: compositor, + onPointerClick: (Surface surface) { + focusView(surface.handle); + }, + ), ); }).toList(), ), diff --git a/demo/lib/window_clipper.dart b/demo/lib/window_clipper.dart new file mode 100644 index 0000000..66ae030 --- /dev/null +++ b/demo/lib/window_clipper.dart @@ -0,0 +1,29 @@ +import 'package:flutter/widgets.dart'; + +class WindowClipper extends CustomClipper { + final int offsetLeft; + final int offsetTop; + final int contentWidth; + final int contentHeight; + + WindowClipper({ + required this.offsetLeft, + required this.offsetTop, + required this.contentWidth, + required this.contentHeight, + }); + + @override + Rect getClip(Size size) { + return Rect.fromLTRB( + offsetLeft.toDouble(), + offsetTop.toDouble(), + contentWidth.toDouble() + offsetLeft, + contentHeight.toDouble() + offsetTop); + } + + @override + bool shouldReclip(covariant CustomClipper oldClipper) { + return true; + } +} diff --git a/include/instance.h b/include/instance.h index f71b621..73e893a 100644 --- a/include/instance.h +++ b/include/instance.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "shaders.h" #include "renderer.h" @@ -106,6 +107,8 @@ struct fwr_view { struct wl_listener request_fullscreen; struct wl_listener request_minimize; + struct wlr_box geometry; + }; struct fwr_keyboard { diff --git a/src/surface.c b/src/surface.c index a82ff6e..a061bef 100644 --- a/src/surface.c +++ b/src/surface.c @@ -67,12 +67,20 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) { message_builder_segment_push_string(&msg_seg, "surface_map"); message_builder_segment_finish(&msg_seg); - struct wlr_box geometry = view->surface->current.geometry; + + wlr_xdg_surface_get_geometry(view->surface, &view->geometry); + + // struct wlr_box geometry = view->surface->current.geometry; + + struct wlr_box geometry = view->geometry; struct wlr_xdg_toplevel_state toplevel_state = view->xdg_toplevel->current; + int surface_width = view->surface->surface->current.width; + int surface_height = view->surface->surface->current.height; + msg_seg = message_builder_segment(&msg); struct message_builder_segment arg_seg = - message_builder_segment_push_map(&msg_seg, 8); + message_builder_segment_push_map(&msg_seg, 14); message_builder_segment_push_string(&arg_seg, "handle"); wlr_log(WLR_INFO, "viewhandle %d", view->handle); message_builder_segment_push_int64(&arg_seg, view->handle); @@ -89,19 +97,41 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) { message_builder_segment_push_string(&arg_seg, "parent_handle"); message_builder_segment_push_int64(&arg_seg, view->parent_handle); - if(view->is_popup){ - message_builder_segment_push_string(&arg_seg, "preffered_width"); - message_builder_segment_push_int64(&arg_seg, toplevel_state.width); + // if(view->is_popup){ + // message_builder_segment_push_string(&arg_seg, "preffered_width"); + // message_builder_segment_push_int64(&arg_seg, toplevel_state.width); - message_builder_segment_push_string(&arg_seg, "preffered_height"); - message_builder_segment_push_int64(&arg_seg, toplevel_state.height); - } else { - message_builder_segment_push_string(&arg_seg, "preffered_width"); - message_builder_segment_push_int64(&arg_seg, geometry.width); + // message_builder_segment_push_string(&arg_seg, "preffered_height"); + // message_builder_segment_push_int64(&arg_seg, toplevel_state.height); + // } else { + + // this is geometry so content width + // need to add 2 more parameters - toplevel_state.width and height as whole buffer + message_builder_segment_push_string(&arg_seg, "surface_width"); + message_builder_segment_push_int64(&arg_seg, surface_width); + + message_builder_segment_push_string(&arg_seg, "surface_height"); + message_builder_segment_push_int64(&arg_seg, surface_height); - message_builder_segment_push_string(&arg_seg, "preffered_height"); - message_builder_segment_push_int64(&arg_seg, geometry.height); - } + + message_builder_segment_push_string(&arg_seg, "offset_left"); + message_builder_segment_push_int64(&arg_seg, geometry.x); + + message_builder_segment_push_string(&arg_seg, "offset_top"); + message_builder_segment_push_int64(&arg_seg, geometry.y); + + message_builder_segment_push_string(&arg_seg, "offset_right"); + message_builder_segment_push_int64(&arg_seg, surface_width - (geometry.width + geometry.x)); + + message_builder_segment_push_string(&arg_seg, "offset_bottom"); + message_builder_segment_push_int64(&arg_seg, surface_height - (geometry.height + geometry.y)); + + message_builder_segment_push_string(&arg_seg, "content_width"); + message_builder_segment_push_int64(&arg_seg, geometry.width); + + message_builder_segment_push_string(&arg_seg, "content_height"); + message_builder_segment_push_int64(&arg_seg, geometry.height); + // } message_builder_segment_finish(&arg_seg); @@ -334,8 +364,6 @@ void fwr_new_xdg_surface(struct wl_listener *listener, void *data) { } - - uint32_t view_handle = handle_map_add(instance->views, (void *)view); view->handle = view_handle; From a2c7e1b27108af41411b0ac84169cc7e6cef5f94 Mon Sep 17 00:00:00 2001 From: charafau Date: Wed, 16 Nov 2022 02:59:54 +0900 Subject: [PATCH 19/20] fix interceptor widget bindings --- .../lib/platform/interceptor_widgets_binding.dart | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/compositor_dart/lib/platform/interceptor_widgets_binding.dart b/compositor_dart/lib/platform/interceptor_widgets_binding.dart index 1549455..f571d3a 100644 --- a/compositor_dart/lib/platform/interceptor_widgets_binding.dart +++ b/compositor_dart/lib/platform/interceptor_widgets_binding.dart @@ -5,22 +5,27 @@ import 'package:flutter/services.dart'; class InterceptorWidgetsBinding extends WidgetsFlutterBinding { InterceptorBinaryMessenger? _binaryMessenger; + static WidgetsBinding? instance; + @override void initInstances() { super.initInstances(); _binaryMessenger = InterceptorBinaryMessenger(super.defaultBinaryMessenger); + instance = this; } @override BinaryMessenger get defaultBinaryMessenger { - return _binaryMessenger == null ? super.defaultBinaryMessenger : _binaryMessenger!; + return _binaryMessenger == null + ? super.defaultBinaryMessenger + : _binaryMessenger!; } static WidgetsBinding ensureInitialized() { - if (WidgetsBinding.instance == null) { + if (InterceptorWidgetsBinding.instance == null) { InterceptorWidgetsBinding(); } - return WidgetsBinding.instance!; + return InterceptorWidgetsBinding.instance!; } static void runApp(Widget app) { From 0358c329bf7b0ff8ce06c0e093bf713fd8a86c30 Mon Sep 17 00:00:00 2001 From: charafau Date: Wed, 16 Nov 2022 03:21:40 +0900 Subject: [PATCH 20/20] Rebased with master --- compositor_dart/lib/surface.dart | 2 +- demo/lib/main.dart | 26 ++++++++++++-------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/compositor_dart/lib/surface.dart b/compositor_dart/lib/surface.dart index bb36861..9e3ca17 100644 --- a/compositor_dart/lib/surface.dart +++ b/compositor_dart/lib/surface.dart @@ -105,7 +105,7 @@ class _SurfaceViewState extends State { int? keycode = physicalToXkbMap[event.physicalKey.usbHidUsage]; if (keycode != null) { - controller.surface.compositor.platform.surfaceSendKey( + controller.compositor.platform.surfaceSendKey( widget.surface, keycode, status, diff --git a/demo/lib/main.dart b/demo/lib/main.dart index 3957527..dff6588 100644 --- a/demo/lib/main.dart +++ b/demo/lib/main.dart @@ -202,7 +202,10 @@ class _MyHomePageState extends State { if (keycode != null && focusedSurface != null) { compositor.platform.surfaceSendKey( - surface!, keycode, event is KeyDownEvent ? KeyStatus.pressed : KeyStatus.released, event.timeStamp); + surfaces[focusedSurface]!, + keycode, + event is KeyDownEvent ? KeyStatus.pressed : KeyStatus.released, + event.timeStamp); } return KeyEventResult.handled; }, @@ -250,20 +253,15 @@ class _MyHomePageState extends State { ); }).toList(), ), - Container( - color: Colors.amber, - padding: const EdgeInsets.all(8.0), - child: SizedBox(width: 500, height: 500, child: surfaceView), - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. + ), + ); + }), ), + // floatingActionButton: FloatingActionButton( + // onPressed: _incrementCounter, + // tooltip: 'Increment', + // child: const Icon(Icons.add), + // ), // This trailing comma makes auto-formatting nicer for build methods. ); } }