diff --git a/flake.nix b/flake.nix index f774d166..89b74d83 100644 --- a/flake.nix +++ b/flake.nix @@ -82,7 +82,11 @@ mkShell = pkg: pkgs.mkShell { inherit (pkg) pname version name; inputsFrom = [ pkg ]; - packages = [ pkgs.flutter pkgs.yq ]; + packages = with pkgs; [ + flutter yq cage + wayland-utils + mesa-demos + ]; }; in { default = mkShell self.packages.${system}.default; diff --git a/lib/logic/display.dart b/lib/logic/display.dart index 7f8e969b..abc8d246 100644 --- a/lib/logic/display.dart +++ b/lib/logic/display.dart @@ -11,6 +11,7 @@ class DisplayManager extends ChangeNotifier { DisplayManager() { channel.setMethodCallHandler((call) async { + print(call.arguments); switch (call.method) { case 'newToplevel': final server = find(call.arguments['name']); @@ -36,8 +37,9 @@ class DisplayManager extends ChangeNotifier { case 'map': toplevel.sync(); break; - case 'commit': + default: toplevel.notifyListeners(); + server.notifyListeners(); break; } break; @@ -130,6 +132,23 @@ class DisplayServer extends ChangeNotifier { } } +class DisplayServerToplevelSize { + const DisplayServerToplevelSize({ + this.width = 0, + this.height = 0, + }); + + final int width; + final int height; + + dynamic toJSON() { + return { + 'width': width, + 'height': height, + }; + } +} + class DisplayServerToplevel extends ChangeNotifier { DisplayServerToplevel._(this._server, this.id); @@ -146,6 +165,8 @@ class DisplayServerToplevel extends ChangeNotifier { return _server._toplevels.firstWhere((item) => item.id == _parent); } + DisplayServerToplevelSize? size; + Future sync() async { final data = await DisplayManager.channel.invokeMethod('getToplevel', { 'name': _server.name, @@ -156,15 +177,20 @@ class DisplayServerToplevel extends ChangeNotifier { title = data['title']; texture = data['texture']; _parent = data['parent']; + size = DisplayServerToplevelSize( + width: data['size']['width'], + height: data['size']['height'], + ); notifyListeners(); } Future setSize(int width, int height) async { - await DisplayManager.channel.invokeMethod('setToplevelSize', { + size = DisplayServerToplevelSize(width: width, height: height); + await DisplayManager.channel.invokeMethod('setToplevel', { 'name': _server.name, 'id': id, - 'width': width, - 'height': height, + 'size': size!.toJSON(), }); + await sync(); } } diff --git a/lib/widgets/system_layout.dart b/lib/widgets/system_layout.dart index bc6f24f5..d8ba331c 100644 --- a/lib/widgets/system_layout.dart +++ b/lib/widgets/system_layout.dart @@ -84,6 +84,7 @@ class SystemLayout extends StatelessWidget { frontLayer: body, bottomSheet: bottomSheet, bottomNavigationBar: bottomNavigationBar, + extendBody: true, ); Widget _buildDesktop(BuildContext context) => @@ -141,6 +142,7 @@ class SystemLayout extends StatelessWidget { body: body, bottomSheet: bottomSheet, bottomNavigationBar: bottomNavigationBar, + extendBody: true, ); @override diff --git a/lib/widgets/toplevel.dart b/lib/widgets/toplevel.dart index ecc7dea7..de1ccdf9 100644 --- a/lib/widgets/toplevel.dart +++ b/lib/widgets/toplevel.dart @@ -20,29 +20,50 @@ class Toplevel extends StatefulWidget { class _ToplevelState extends State { GlobalKey key = GlobalKey(); + void _sendSize() { + if (key.currentContext != null && widget.toplevel.texture != null) { + final box = key.currentContext!.findRenderObject() as RenderBox; + widget.toplevel.setSize(box.size.width.toInt(), box.size.height.toInt()); + } + } + @override void initState() { super.initState(); SchedulerBinding.instance.addPostFrameCallback((_) { - if (key.currentContext != null && widget.toplevel.texture != null) { - final box = key.currentContext!.findRenderObject() as RenderBox; - widget.toplevel.setSize(box.size.width.toInt(), box.size.height.toInt()); - } + _sendSize(); }); } + @override + Widget buildContent(BuildContext context, DisplayServerToplevel toplevel) => + toplevel.texture == null + ? SizedBox() : Texture( + textureId: toplevel.texture!, + ); + @override Widget build(BuildContext context) => - ChangeNotifierProvider( - key: key, - create: (_) => widget.toplevel, - child: Consumer( - builder: (context, toplevel, _) => - toplevel.texture == null - ? SizedBox() : Texture( - textureId: toplevel.texture!, - ), + NotificationListener( + onNotification: (notif) { + _sendSize(); + return true; + }, + child: SizeChangedLayoutNotifier( + child: ChangeNotifierProvider( + create: (_) => widget.toplevel, + child: Consumer( + key: key, + builder: (context, toplevel, _) => + toplevel.size != null + ? SizedBox( + width: toplevel.size!.width.toDouble(), + height: toplevel.size!.height.toDouble(), + child: buildContent(context, toplevel), + ) : buildContent(context, toplevel), + ), + ), ), ); } diff --git a/linux/channels/display.c b/linux/channels/display.c index 22d4d2f2..5ddf004b 100644 --- a/linux/channels/display.c +++ b/linux/channels/display.c @@ -20,6 +20,13 @@ static FlValue* new_string(const gchar* str) { return fl_value_new_string(g_strdup(str)); } +static void toplevel_decor_new(struct wl_listener* listener, void* data) { + DisplayChannelDisplay* self = wl_container_of(listener, self, toplevel_decor_new); + struct wlr_xdg_toplevel_decoration_v1* decor = data; + g_message("%p", decor->toplevel->base->data); + wlr_xdg_toplevel_decoration_v1_set_mode(decor, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); +} + static gboolean display_channel_wl_poll(GIOChannel* src, GIOCondition cond, gpointer user_data) { DisplayChannelDisplay* self = (DisplayChannelDisplay*)user_data; struct wl_display* wl_display = display_channel_backend_get_display(self->backend); @@ -97,6 +104,11 @@ static void method_call_handler(FlMethodChannel* channel, FlMethodCall* method_c disp->seat = wlr_seat_create(wl_display, session_name); disp->xdg_shell = wlr_xdg_shell_create(wl_display, 3); + disp->xdg_decor = wlr_xdg_decoration_manager_v1_create(wl_display); + + disp->toplevel_decor_new.notify = toplevel_decor_new; + wl_signal_add(&disp->xdg_decor->events.new_toplevel_decoration, &disp->toplevel_decor_new); + disp->xdg_surface_new.notify = xdg_surface_new; wl_signal_add(&disp->xdg_shell->events.new_surface, &disp->xdg_surface_new); @@ -206,8 +218,16 @@ static void method_call_handler(FlMethodChannel* channel, FlMethodCall* method_c fl_value_set(value, fl_value_new_string("texture"), fl_value_new_null()); } + struct wlr_box geom; + wlr_xdg_surface_get_geometry(toplevel->xdg->base, &geom); + + FlValue* value_size = fl_value_new_map(); + fl_value_set(value_size, fl_value_new_string("width"), fl_value_new_int(geom.width)); + fl_value_set(value_size, fl_value_new_string("height"), fl_value_new_int(geom.height)); + fl_value_set(value, fl_value_new_string("size"), value_size); + response = FL_METHOD_RESPONSE(fl_method_success_response_new(value)); - } else if (strcmp(fl_method_call_get_name(method_call), "setToplevelSize") == 0) { + } else if (strcmp(fl_method_call_get_name(method_call), "setToplevel") == 0) { FlValue* args = fl_method_call_get_args(method_call); const gchar* name = fl_value_get_string(fl_value_lookup_string(args, "name")); int id = fl_value_get_int(fl_value_lookup_string(args, "id")); @@ -227,7 +247,11 @@ static void method_call_handler(FlMethodChannel* channel, FlMethodCall* method_c DisplayChannelToplevel* toplevel = g_hash_table_lookup(disp->toplevels, &id); g_assert(toplevel->id == id); - wlr_xdg_toplevel_set_size(toplevel->xdg, fl_value_get_int(fl_value_lookup_string(args, "width")), fl_value_get_int(fl_value_lookup_string(args, "height"))); + FlValue* value_size = fl_value_lookup_string(args, "size"); + if (value_size != NULL) { + wlr_xdg_toplevel_set_size(toplevel->xdg, fl_value_get_int(fl_value_lookup_string(value_size, "width")), fl_value_get_int(fl_value_lookup_string(value_size, "height"))); + } + response = FL_METHOD_RESPONSE(fl_method_success_response_new(NULL)); } else { response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); diff --git a/linux/channels/display.h b/linux/channels/display.h index 685d7b45..ed815d58 100644 --- a/linux/channels/display.h +++ b/linux/channels/display.h @@ -19,6 +19,7 @@ extern "C" { #include #include #include +#include #include #include #include @@ -66,12 +67,14 @@ typedef struct _DisplayChannelDisplay { struct wlr_compositor* compositor; struct wlr_seat* seat; struct wlr_xdg_shell* xdg_shell; + struct wlr_xdg_decoration_manager_v1* xdg_decor; const gchar* prev_wl_disp; const char* socket; struct _DisplayChannel* channel; struct wl_listener xdg_surface_new; + struct wl_listener toplevel_decor_new; GList* outputs; GHashTable* toplevels; diff --git a/linux/channels/display/texture.c b/linux/channels/display/texture.c index 3c0d9791..bc22c367 100644 --- a/linux/channels/display/texture.c +++ b/linux/channels/display/texture.c @@ -7,11 +7,13 @@ typedef struct _DisplayChannelTexturePrivate { uint32_t name; uint32_t width; uint32_t height; + bool has_init; } DisplayChannelTexturePrivate; enum { PROP_0 = 0, PROP_GL_CONTEXT, + PROP_HAS_INIT, N_PROPERTIES, }; @@ -42,6 +44,9 @@ static void display_channel_texture_get_property(GObject* obj, guint prop_id, GV case PROP_GL_CONTEXT: g_value_set_object(value, priv->gl_context); break; + case PROP_HAS_INIT: + g_value_set_boolean(value, priv->has_init); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); break; @@ -78,6 +83,7 @@ static void display_channel_texture_class_init(DisplayChannelTextureClass* klass FL_TEXTURE_GL_CLASS(klass)->populate = display_channel_texture_populate; obj_props[PROP_GL_CONTEXT] = g_param_spec_object("gl-context", "GL Context", "The OpenGL context in GDK to use", GDK_TYPE_GL_CONTEXT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + obj_props[PROP_HAS_INIT] = g_param_spec_boolean("has-init", "Has initial update", "Whether the texture has done an initial update", false, G_PARAM_READABLE); g_object_class_install_properties(obj_class, N_PROPERTIES, obj_props); } @@ -92,6 +98,7 @@ DisplayChannelTexture* display_channel_texture_new(GdkGLContext* gl_context, str glGenTextures(1, &priv->name); gdk_gl_context_clear_current(); + priv->has_init = false; display_channel_texture_update(self, buffer); return self; } @@ -106,7 +113,10 @@ void display_channel_texture_update(DisplayChannelTexture* self, struct wlr_buff wlr_buffer_begin_data_ptr_access(buffer, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &fmt, &stride); const struct wlr_gles2_pixel_format* gles2_fmt = get_gles2_format_from_drm(fmt); + g_return_if_fail(gles2_fmt != NULL); + const struct wlr_pixel_format_info* drm_fmt = drm_get_pixel_format_info(fmt); + g_return_if_fail(drm_fmt != NULL); glBindTexture(GL_TEXTURE_2D, priv->name); @@ -127,4 +137,6 @@ void display_channel_texture_update(DisplayChannelTexture* self, struct wlr_buff wlr_buffer_end_data_ptr_access(buffer); gdk_gl_context_clear_current(); + + priv->has_init = true; } diff --git a/linux/channels/display/toplevel.c b/linux/channels/display/toplevel.c index 301c8ebd..efb1e73f 100644 --- a/linux/channels/display/toplevel.c +++ b/linux/channels/display/toplevel.c @@ -45,6 +45,11 @@ static void xdg_toplevel_destroy(struct wl_listener* listener, void* data) { DisplayChannelToplevel* self = wl_container_of(listener, self, destroy); + g_autoptr(FlValue) value = fl_value_new_map(); + fl_value_set(value, fl_value_new_string("name"), fl_value_new_string(self->display->socket)); + fl_value_set(value, fl_value_new_string("id"), fl_value_new_int(self->id)); + invoke_method(self->display->channel->channel, "removeToplevel", value); + wl_list_remove(&self->map.link); wl_list_remove(&self->unmap.link); wl_list_remove(&self->destroy.link); @@ -63,19 +68,20 @@ static void xdg_toplevel_destroy(struct wl_listener* listener, void* data) { g_hash_table_remove(self->display->toplevels, &self->id); if (self->texture != NULL) { - GenesisShellApplication* app = wl_container_of(self->display->channel, app, display); - FlEngine* engine = fl_view_get_engine(app->view); - FlTextureRegistrar* tex_reg = fl_engine_get_texture_registrar(engine); - fl_texture_registrar_unregister_texture(tex_reg, FL_TEXTURE(self->texture)); + bool has_init; + g_object_get(G_OBJECT(self->texture), "has-init", &has_init, NULL); + + if (has_init) { + GenesisShellApplication* app = wl_container_of(self->display->channel, app, display); + FlEngine* engine = fl_view_get_engine(app->view); + FlTextureRegistrar* tex_reg = fl_engine_get_texture_registrar(engine); + // FIXME: Sometimes this causes a SIGSEV + fl_texture_registrar_unregister_texture(tex_reg, FL_TEXTURE(self->texture)); + } } g_clear_object(&self->texture); - g_autoptr(FlValue) value = fl_value_new_map(); - fl_value_set(value, fl_value_new_string("name"), fl_value_new_string(self->display->socket)); - fl_value_set(value, fl_value_new_string("id"), fl_value_new_int(self->id)); - invoke_method(self->display->channel->channel, "removeToplevel", value); - free(self); } @@ -109,13 +115,18 @@ static void xdg_toplevel_commit(struct wl_listener* listener, void* data) { display_channel_texture_update(self->texture, buffer); } - fl_texture_registrar_mark_texture_frame_available(tex_reg, FL_TEXTURE(self->texture)); + bool has_init; + g_object_get(G_OBJECT(self->texture), "has-init", &has_init, NULL); - if (is_new) { - xdg_toplevel_emit_prop(self, "texture", fl_value_new_int((uintptr_t)FL_TEXTURE(self->texture))); - } + if (has_init) { + fl_texture_registrar_mark_texture_frame_available(tex_reg, FL_TEXTURE(self->texture)); - xdg_toplevel_emit_request(self, "commit"); + if (is_new) { + xdg_toplevel_emit_prop(self, "texture", fl_value_new_int((uintptr_t)FL_TEXTURE(self->texture))); + } + + xdg_toplevel_emit_request(self, "commit"); + } } static void xdg_toplevel_request_maximize(struct wl_listener* listener, void* data) { @@ -195,6 +206,8 @@ void xdg_surface_new(struct wl_listener* listener, void* data) { struct wlr_xdg_toplevel* xdg_toplevel = wlr_xdg_toplevel_try_from_wlr_surface(xdg_surface->surface); DisplayChannelToplevel* toplevel = (DisplayChannelToplevel*)malloc(sizeof (DisplayChannelToplevel)); + xdg_surface->data = toplevel; + toplevel->display = self; toplevel->xdg = xdg_toplevel; toplevel->id = self->toplevel_id++;