From 8c0776f30934807c8b355f83854bd1e5b1568909 Mon Sep 17 00:00:00 2001 From: GroobleDierne Date: Tue, 5 Aug 2025 20:32:59 +0200 Subject: [PATCH 1/8] config, server: handle opening ninbot in separate toplevel --- include/config/config.h | 1 + include/server/ui.h | 9 +++++ waywall/config/config.c | 1 + waywall/server/ui.c | 78 ++++++++++++++++++++++++++++++++++++++-- waywall/server/wl_seat.c | 7 +++- waywall/wrap.c | 11 +++++- 6 files changed, 103 insertions(+), 4 deletions(-) diff --git a/include/config/config.h b/include/config/config.h index 337e6e48..72af9107 100644 --- a/include/config/config.h +++ b/include/config/config.h @@ -52,6 +52,7 @@ struct config { ANCHOR_RIGHT, ANCHOR_BOTTOMLEFT, ANCHOR_BOTTOMRIGHT, + ANCHOR_SEPARATE, ANCHOR_NONE, } ninb_anchor; diff --git a/include/server/ui.h b/include/server/ui.h index 151b09ad..4d09884a 100644 --- a/include/server/ui.h +++ b/include/server/ui.h @@ -28,6 +28,14 @@ struct server_ui { struct wl_subsurface *subsurface; } tree; + struct { + struct wl_surface *surface; + struct xdg_toplevel *top_level; + bool is_focused; + int32_t width; + int32_t height; + } ninbot; + struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; struct zxdg_toplevel_decoration_v1 *xdg_decoration; @@ -101,6 +109,7 @@ void server_ui_destroy(struct server_ui *ui); void server_ui_hide(struct server_ui *ui); void server_ui_set_fullscreen(struct server_ui *ui, bool fullscreen); void server_ui_show(struct server_ui *ui); +void ninbot_toplevel_show(struct server_ui *ui); void server_ui_use_config(struct server_ui *ui, struct server_ui_config *config); struct server_ui_config *server_ui_config_create(struct server_ui *ui, struct config *cfg); diff --git a/waywall/config/config.c b/waywall/config/config.c index bb9c41ea..40b76915 100644 --- a/waywall/config/config.c +++ b/waywall/config/config.c @@ -645,6 +645,7 @@ process_config_theme(struct config *cfg) { [ANCHOR_RIGHT] = "right", [ANCHOR_BOTTOMLEFT] = "bottomleft", [ANCHOR_BOTTOMRIGHT] = "bottomright", + [ANCHOR_SEPARATE] = "separate", }; for (size_t i = 0; i < STATIC_ARRLEN(anchor_names); i++) { diff --git a/waywall/server/ui.c b/waywall/server/ui.c index b99afd8b..b736776f 100644 --- a/waywall/server/ui.c +++ b/waywall/server/ui.c @@ -140,6 +140,34 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = { .wm_capabilities = on_xdg_toplevel_wm_capabilities, }; +void ninbot_toplevel_configure_handler +( + void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states +) { + struct server_ui *ui = data; + ui->ninbot.width = width; + ui->ninbot.height = height; +} + +void ninbot_toplevel_close_handler +( + void *data, + struct xdg_toplevel *xdg_toplevel +) { + struct server_ui *ui = data; + // TODO: only close ninbot and other floating windows + wl_signal_emit_mutable(&ui->events.close, NULL); +} + +static const struct xdg_toplevel_listener ninbot_toplevel_listener = { + .configure = ninbot_toplevel_configure_handler, + .close = ninbot_toplevel_close_handler +}; + static void on_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { struct server_ui *ui = data; @@ -163,6 +191,17 @@ static const struct xdg_surface_listener xdg_surface_listener = { .configure = on_xdg_surface_configure, }; +static void xdg_surface_configure_handler (void *data, struct xdg_surface *xdg_surface, uint32_t serial) { + struct server_ui *ui = data; + xdg_surface_set_window_geometry(xdg_surface, 0, 0, ui->ninbot.width, ui->ninbot.height); + xdg_surface_ack_configure(xdg_surface, serial); + wl_surface_commit(ui->ninbot.surface); +} + +static const struct xdg_surface_listener ninbot_surface_listener = { + .configure = xdg_surface_configure_handler, +}; + static void on_view_surface_commit(struct wl_listener *listener, void *data) { struct server_view *view = wl_container_of(listener, view, on_surface_commit); @@ -203,6 +242,19 @@ server_ui_create(struct server *server, struct config *cfg) { ui->root.viewport = wp_viewporter_get_viewport(server->backend->viewporter, ui->root.surface); check_alloc(ui->root.viewport); + if (cfg->theme.ninb_anchor == ANCHOR_SEPARATE) { + ui->ninbot.surface = wl_compositor_create_surface(server->backend->compositor); + check_alloc(ui->ninbot.surface); + + struct xdg_surface *xdg_ninbot = xdg_wm_base_get_xdg_surface(server->backend->xdg_wm_base, ui->ninbot.surface); + check_alloc(xdg_ninbot); + xdg_surface_add_listener(xdg_ninbot, &ninbot_surface_listener, ui); + + ui->ninbot.top_level = xdg_surface_get_toplevel(xdg_ninbot); + check_alloc(ui->ninbot.top_level); + xdg_toplevel_add_listener(ui->ninbot.top_level, &ninbot_toplevel_listener, ui); + } + if (server->backend->tearing_control) { ui->root.tearing_control = wp_tearing_control_manager_v1_get_tearing_control( server->backend->tearing_control, ui->root.surface); @@ -349,6 +401,21 @@ server_ui_show(struct server_ui *ui) { wl_signal_emit_mutable(&ui->server->events.map_status, &ui->mapped); } +void ninbot_toplevel_show(struct server_ui *ui) { + struct wl_display *display = ui->server->backend->display; + + wl_surface_attach(ui->ninbot.surface, NULL, 0, 0); + wl_surface_commit(ui->ninbot.surface); + wl_display_roundtrip(display); + + wl_surface_attach(ui->ninbot.surface, ui->config->background, 0, 0); + wl_surface_commit(ui->ninbot.surface); + wl_display_roundtrip(display); + + xdg_toplevel_set_title(ui->ninbot.top_level, "NinjabrainBot Wrapper"); + xdg_toplevel_set_app_id(ui->ninbot.top_level, "NinjabrainBot Wrapper"); +} + void server_ui_use_config(struct server_ui *ui, struct server_ui_config *config) { if (ui->config) { @@ -452,10 +519,17 @@ server_view_commit(struct server_view *view) { if (visibility_changed && view->current.visible) { ww_assert(!view->subsurface); - view->subsurface = + if (view->current.centered) { + view->subsurface = wl_subcompositor_get_subsurface(view->ui->server->backend->subcompositor, view->surface->remote, view->ui->tree.surface); - check_alloc(view->subsurface); + check_alloc(view->subsurface); + } else { + view->subsurface = + wl_subcompositor_get_subsurface(view->ui->server->backend->subcompositor, view->surface->remote, view->ui->ninbot.surface); + check_alloc(view->subsurface); + wl_surface_commit(view->ui->ninbot.surface); + } wl_subsurface_set_desync(view->subsurface); } else if (visibility_changed && !view->current.visible) { diff --git a/waywall/server/wl_seat.c b/waywall/server/wl_seat.c index a4c9fafb..8d66efbb 100644 --- a/waywall/server/wl_seat.c +++ b/waywall/server/wl_seat.c @@ -744,9 +744,14 @@ on_pointer_enter(void *data, struct wl_pointer *wl, uint32_t serial, struct wl_s wl_fixed_t surface_x, wl_fixed_t surface_y) { struct server_seat *seat = data; seat->last_serial = serial; + seat->server->ui->ninbot.is_focused = false; + + if (surface == seat->server->ui->ninbot.surface) { + seat->server->ui->ninbot.is_focused = true; + return; + } if (surface != seat->server->ui->root.surface) { - ww_log(LOG_WARN, "received wl_pointer.enter for unknown surface"); return; } diff --git a/waywall/wrap.c b/waywall/wrap.c index 42be82c9..7dccd09e 100644 --- a/waywall/wrap.c +++ b/waywall/wrap.c @@ -153,6 +153,11 @@ floating_update_anchored(struct wrap *wrap) { x = 0; y = wrap->height - win_height; break; + case ANCHOR_SEPARATE: + x = 0; + y = 0; + server_view_set_visible(fview->view, true); + break; case ANCHOR_BOTTOMRIGHT: x = wrap->width - win_width; y = wrap->height - win_height; @@ -217,6 +222,10 @@ floating_view_create(struct wrap *wrap, struct server_view *view) { server_view_set_centered(view, false); floating_update_anchored(wrap); + if (wrap->cfg->theme.ninb_anchor == ANCHOR_SEPARATE) { + ninbot_toplevel_show(wrap->server->ui); + floating_set_visible(wrap, true); + } } } @@ -448,7 +457,7 @@ on_button(void *data, uint32_t button, bool pressed) { // Check to see if the input focus should be changed to a new window. If the user did not click // on any floating window, then input focus should be given back to the Minecraft instance. struct floating_view *fview = floating_view_at(wrap, wrap->input.x, wrap->input.y); - if (!fview) { + if (!fview || (wrap->cfg->theme.ninb_anchor == ANCHOR_SEPARATE && !wrap->server->ui->ninbot.is_focused)) { ww_assert(wrap->view); server_set_input_focus(wrap->server, wrap->view); return false; From c40f76a3f42325f73ff215c0b7e43c8da9218bc8 Mon Sep 17 00:00:00 2001 From: GroobleDierne Date: Mon, 25 Aug 2025 10:54:57 +0200 Subject: [PATCH 2/8] server: only open wrapper window once, commit to ninbot surface on floating layout change --- include/server/ui.h | 1 + waywall/server/ui.c | 9 +++++++-- waywall/wrap.c | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/server/ui.h b/include/server/ui.h index 4d09884a..d0b6601c 100644 --- a/include/server/ui.h +++ b/include/server/ui.h @@ -32,6 +32,7 @@ struct server_ui { struct wl_surface *surface; struct xdg_toplevel *top_level; bool is_focused; + bool window_opened; int32_t width; int32_t height; } ninbot; diff --git a/waywall/server/ui.c b/waywall/server/ui.c index b736776f..60d9b875 100644 --- a/waywall/server/ui.c +++ b/waywall/server/ui.c @@ -69,7 +69,11 @@ layout_floating(struct server_view *view) { wl_fixed_from_int(-1), wl_fixed_from_int(-1)); wp_viewport_set_destination(view->viewport, -1, -1); - wl_surface_commit(view->ui->tree.surface); + if (view->ui->ninbot.surface == NULL) { + wl_surface_commit(view->ui->tree.surface); + } else { + wl_surface_commit(view->ui->ninbot.surface); + } } static void @@ -414,6 +418,7 @@ void ninbot_toplevel_show(struct server_ui *ui) { xdg_toplevel_set_title(ui->ninbot.top_level, "NinjabrainBot Wrapper"); xdg_toplevel_set_app_id(ui->ninbot.top_level, "NinjabrainBot Wrapper"); + ui->ninbot.window_opened = true; } void @@ -519,7 +524,7 @@ server_view_commit(struct server_view *view) { if (visibility_changed && view->current.visible) { ww_assert(!view->subsurface); - if (view->current.centered) { + if (view->current.centered || view->ui->ninbot.surface == NULL) { view->subsurface = wl_subcompositor_get_subsurface(view->ui->server->backend->subcompositor, view->surface->remote, view->ui->tree.surface); diff --git a/waywall/wrap.c b/waywall/wrap.c index 7dccd09e..b4745f69 100644 --- a/waywall/wrap.c +++ b/waywall/wrap.c @@ -223,7 +223,9 @@ floating_view_create(struct wrap *wrap, struct server_view *view) { server_view_set_centered(view, false); floating_update_anchored(wrap); if (wrap->cfg->theme.ninb_anchor == ANCHOR_SEPARATE) { - ninbot_toplevel_show(wrap->server->ui); + if (!wrap->server->ui->ninbot.window_opened) { + ninbot_toplevel_show(wrap->server->ui); + } floating_set_visible(wrap, true); } } From 62bf7a61a34f47a146e7e8c815857e67f466dc6c Mon Sep 17 00:00:00 2001 From: GroobleDierne Date: Mon, 15 Sep 2025 16:30:33 +0200 Subject: [PATCH 3/8] ui: separate ninbot toplevel, start reload support --- include/server/ui.h | 2 +- waywall/server/ui.c | 45 ++++++++++++++++++++++++++++++++------------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/include/server/ui.h b/include/server/ui.h index d0b6601c..fedcd17c 100644 --- a/include/server/ui.h +++ b/include/server/ui.h @@ -57,7 +57,7 @@ struct server_ui { struct server_ui_config { struct wl_buffer *background; bool tearing; - + bool xwayland_toplevel; uint32_t ninb_opacity; }; diff --git a/waywall/server/ui.c b/waywall/server/ui.c index 60d9b875..f4799538 100644 --- a/waywall/server/ui.c +++ b/waywall/server/ui.c @@ -231,6 +231,26 @@ on_view_surface_commit(struct wl_listener *listener, void *data) { } } +void xwayland_toplevel_create(struct server_ui *ui) { + ui->ninbot.surface = wl_compositor_create_surface(ui->server->backend->compositor); + check_alloc(ui->ninbot.surface); + + struct xdg_surface *xdg_ninbot = xdg_wm_base_get_xdg_surface(ui->server->backend->xdg_wm_base, ui->ninbot.surface); + check_alloc(xdg_ninbot); + xdg_surface_add_listener(xdg_ninbot, &ninbot_surface_listener, ui); + + ui->ninbot.top_level = xdg_surface_get_toplevel(xdg_ninbot); + check_alloc(ui->ninbot.top_level); + xdg_toplevel_add_listener(ui->ninbot.top_level, &ninbot_toplevel_listener, ui); +} + +void xwayland_toplevel_destroy(struct server_ui *ui) { + xdg_toplevel_destroy(ui->ninbot.top_level); + wl_surface_destroy(ui->ninbot.surface); + ui->ninbot.top_level = NULL; + ui->ninbot.surface = NULL; +} + struct server_ui * server_ui_create(struct server *server, struct config *cfg) { struct server_ui *ui = zalloc(1, sizeof(*ui)); @@ -246,19 +266,6 @@ server_ui_create(struct server *server, struct config *cfg) { ui->root.viewport = wp_viewporter_get_viewport(server->backend->viewporter, ui->root.surface); check_alloc(ui->root.viewport); - if (cfg->theme.ninb_anchor == ANCHOR_SEPARATE) { - ui->ninbot.surface = wl_compositor_create_surface(server->backend->compositor); - check_alloc(ui->ninbot.surface); - - struct xdg_surface *xdg_ninbot = xdg_wm_base_get_xdg_surface(server->backend->xdg_wm_base, ui->ninbot.surface); - check_alloc(xdg_ninbot); - xdg_surface_add_listener(xdg_ninbot, &ninbot_surface_listener, ui); - - ui->ninbot.top_level = xdg_surface_get_toplevel(xdg_ninbot); - check_alloc(ui->ninbot.top_level); - xdg_toplevel_add_listener(ui->ninbot.top_level, &ninbot_toplevel_listener, ui); - } - if (server->backend->tearing_control) { ui->root.tearing_control = wp_tearing_control_manager_v1_get_tearing_control( server->backend->tearing_control, ui->root.surface); @@ -439,6 +446,12 @@ server_ui_use_config(struct server_ui *ui, struct server_ui_config *config) { wl_surface_damage_buffer(ui->root.surface, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(ui->root.surface); } + + if (config->xwayland_toplevel && ui->ninbot.surface == NULL) { + xwayland_toplevel_create(ui); + }else if (ui->ninbot.surface != NULL && !config->xwayland_toplevel) { + xwayland_toplevel_destroy(ui); + } } struct server_ui_config * @@ -458,6 +471,12 @@ server_ui_config_create(struct server_ui *ui, struct config *cfg) { return NULL; } + if (cfg->theme.ninb_anchor == ANCHOR_SEPARATE) { + config->xwayland_toplevel = true; + } else { + config->xwayland_toplevel = false; + } + config->tearing = cfg->experimental.tearing; config->ninb_opacity = cfg->theme.ninb_opacity * UINT32_MAX; From d310a0fc06e6913e2c721c79b575baa0ebdfcb4b Mon Sep 17 00:00:00 2001 From: GroobleDierne Date: Tue, 16 Sep 2025 16:51:06 +0200 Subject: [PATCH 4/8] ui, wrap: complete ninbot toplevel reload --- include/server/ui.h | 5 ++++- waywall/server/ui.c | 27 ++++++++++++++++++--------- waywall/wrap.c | 20 +++++++++++++++++++- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/include/server/ui.h b/include/server/ui.h index fedcd17c..dcf7ad3c 100644 --- a/include/server/ui.h +++ b/include/server/ui.h @@ -110,7 +110,7 @@ void server_ui_destroy(struct server_ui *ui); void server_ui_hide(struct server_ui *ui); void server_ui_set_fullscreen(struct server_ui *ui, bool fullscreen); void server_ui_show(struct server_ui *ui); -void ninbot_toplevel_show(struct server_ui *ui); +void xwayland_toplevel_show(struct server_ui *ui); void server_ui_use_config(struct server_ui *ui, struct server_ui_config *config); struct server_ui_config *server_ui_config_create(struct server_ui *ui, struct config *cfg); @@ -127,6 +127,9 @@ void server_view_set_pos(struct server_view *view, uint32_t x, uint32_t y); void server_view_set_size(struct server_view *view, uint32_t width, uint32_t height); void server_view_set_visible(struct server_view *view, bool visible); +void xwayland_server_view_attach(struct server_view *view); +void xwayland_server_view_detach(struct server_view *view); + struct server_view *server_view_create(struct server_ui *ui, struct server_surface *surface, const struct server_view_impl *impl, void *impl_data); void server_view_destroy(struct server_view *view); diff --git a/waywall/server/ui.c b/waywall/server/ui.c index f4799538..d223c9b6 100644 --- a/waywall/server/ui.c +++ b/waywall/server/ui.c @@ -412,7 +412,7 @@ server_ui_show(struct server_ui *ui) { wl_signal_emit_mutable(&ui->server->events.map_status, &ui->mapped); } -void ninbot_toplevel_show(struct server_ui *ui) { +void xwayland_toplevel_show(struct server_ui *ui) { struct wl_display *display = ui->server->backend->display; wl_surface_attach(ui->ninbot.surface, NULL, 0, 0); @@ -544,15 +544,9 @@ server_view_commit(struct server_view *view) { ww_assert(!view->subsurface); if (view->current.centered || view->ui->ninbot.surface == NULL) { - view->subsurface = - wl_subcompositor_get_subsurface(view->ui->server->backend->subcompositor, - view->surface->remote, view->ui->tree.surface); - check_alloc(view->subsurface); + xwayland_server_view_detach(view); } else { - view->subsurface = - wl_subcompositor_get_subsurface(view->ui->server->backend->subcompositor, view->surface->remote, view->ui->ninbot.surface); - check_alloc(view->subsurface); - wl_surface_commit(view->ui->ninbot.surface); + xwayland_server_view_attach(view); } wl_subsurface_set_desync(view->subsurface); @@ -574,6 +568,21 @@ server_view_commit(struct server_view *view) { view_state_reset(&view->pending); } +void +xwayland_server_view_attach(struct server_view *view) { + view->subsurface = + wl_subcompositor_get_subsurface(view->ui->server->backend->subcompositor, view->surface->remote, view->ui->ninbot.surface); + check_alloc(view->subsurface); + wl_surface_commit(view->ui->ninbot.surface); +} + +void +xwayland_server_view_detach(struct server_view *view) { + view->subsurface = + wl_subcompositor_get_subsurface(view->ui->server->backend->subcompositor, view->surface->remote, view->ui->tree.surface); + check_alloc(view->subsurface); +} + void server_view_refresh(struct server_view *view) { if (view->subsurface && view->current.centered) { diff --git a/waywall/wrap.c b/waywall/wrap.c index b4745f69..a2d7596c 100644 --- a/waywall/wrap.c +++ b/waywall/wrap.c @@ -224,7 +224,7 @@ floating_view_create(struct wrap *wrap, struct server_view *view) { floating_update_anchored(wrap); if (wrap->cfg->theme.ninb_anchor == ANCHOR_SEPARATE) { if (!wrap->server->ui->ninbot.window_opened) { - ninbot_toplevel_show(wrap->server->ui); + xwayland_toplevel_show(wrap->server->ui); } floating_set_visible(wrap, true); } @@ -643,6 +643,24 @@ wrap_set_config(struct wrap *wrap, struct config *cfg) { config_vm_set_wrap(cfg->vm, wrap); wrap->cfg = cfg; + + if (wrap->cfg->theme.ninb_anchor == ANCHOR_SEPARATE) { + struct floating_view *fview; + wl_list_for_each(fview, &wrap->floating.views, link) { + wl_subsurface_destroy(fview->view->subsurface); + xwayland_server_view_attach(fview->view); + } + if (wl_list_length(&wrap->floating.views) > 0) { + xwayland_toplevel_show(wrap->server->ui); + } + } else { + struct floating_view *fview; + wl_list_for_each(fview, &wrap->floating.views, link) { + wl_subsurface_destroy(fview->view->subsurface); + xwayland_server_view_detach(fview->view); + } + } + if (wrap->cfg->theme.ninb_anchor == ANCHOR_NONE) { // If anchoring has been disabled, ensure there is no anchored view. if (wrap->floating.anchored) { From 5b6e00c500f783173298333cae15743d5a5d3b25 Mon Sep 17 00:00:00 2001 From: GroobleDierne Date: Wed, 17 Sep 2025 10:31:40 +0200 Subject: [PATCH 5/8] server: only close x11 apps when xwayland_toplevel is closed --- waywall/server/ui.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/waywall/server/ui.c b/waywall/server/ui.c index d223c9b6..2d50713d 100644 --- a/waywall/server/ui.c +++ b/waywall/server/ui.c @@ -163,8 +163,15 @@ void ninbot_toplevel_close_handler struct xdg_toplevel *xdg_toplevel ) { struct server_ui *ui = data; - // TODO: only close ninbot and other floating windows - wl_signal_emit_mutable(&ui->events.close, NULL); + + struct server_view *view; + struct server_view *tmp_view; + wl_list_for_each_safe(view, tmp_view, &ui->views, link) { + // Can we nest native Wayland apps except Minecraft? If so this is problematic + if (strcmp(view->impl->name, "xwayland") == 0) { + server_view_destroy(view); + } + } } static const struct xdg_toplevel_listener ninbot_toplevel_listener = { From 590850b93c6d7b58915d1911030fb541ba30d25a Mon Sep 17 00:00:00 2001 From: GroobleDierne Date: Wed, 17 Sep 2025 20:02:21 +0200 Subject: [PATCH 6/8] server: correctly destroy xdg_surface of xwayland toplevel --- include/server/ui.h | 1 + waywall/server/ui.c | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/server/ui.h b/include/server/ui.h index dcf7ad3c..f041f9df 100644 --- a/include/server/ui.h +++ b/include/server/ui.h @@ -31,6 +31,7 @@ struct server_ui { struct { struct wl_surface *surface; struct xdg_toplevel *top_level; + struct xdg_surface *xdg_surface; bool is_focused; bool window_opened; int32_t width; diff --git a/waywall/server/ui.c b/waywall/server/ui.c index 2d50713d..c7f7b816 100644 --- a/waywall/server/ui.c +++ b/waywall/server/ui.c @@ -242,19 +242,21 @@ void xwayland_toplevel_create(struct server_ui *ui) { ui->ninbot.surface = wl_compositor_create_surface(ui->server->backend->compositor); check_alloc(ui->ninbot.surface); - struct xdg_surface *xdg_ninbot = xdg_wm_base_get_xdg_surface(ui->server->backend->xdg_wm_base, ui->ninbot.surface); - check_alloc(xdg_ninbot); - xdg_surface_add_listener(xdg_ninbot, &ninbot_surface_listener, ui); + ui->ninbot.xdg_surface = xdg_wm_base_get_xdg_surface(ui->server->backend->xdg_wm_base, ui->ninbot.surface); + check_alloc(ui->ninbot.xdg_surface); + xdg_surface_add_listener(ui->ninbot.xdg_surface, &ninbot_surface_listener, ui); - ui->ninbot.top_level = xdg_surface_get_toplevel(xdg_ninbot); + ui->ninbot.top_level = xdg_surface_get_toplevel(ui->ninbot.xdg_surface); check_alloc(ui->ninbot.top_level); xdg_toplevel_add_listener(ui->ninbot.top_level, &ninbot_toplevel_listener, ui); } void xwayland_toplevel_destroy(struct server_ui *ui) { xdg_toplevel_destroy(ui->ninbot.top_level); + xdg_surface_destroy(ui->ninbot.xdg_surface); wl_surface_destroy(ui->ninbot.surface); ui->ninbot.top_level = NULL; + ui->ninbot.xdg_surface = NULL; ui->ninbot.surface = NULL; } From d59bba3509319ab9fcfebf722db3d58dfcf06932 Mon Sep 17 00:00:00 2001 From: GroobleDierne Date: Wed, 8 Oct 2025 14:42:43 +0200 Subject: [PATCH 7/8] properly hide xwayland toplevel --- waywall/server/ui.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/waywall/server/ui.c b/waywall/server/ui.c index c7f7b816..d12c5fa9 100644 --- a/waywall/server/ui.c +++ b/waywall/server/ui.c @@ -157,6 +157,13 @@ void ninbot_toplevel_configure_handler ui->ninbot.height = height; } +void xwayland_toplevel_hide(struct server_ui *ui) { + wl_surface_attach(ui->ninbot.surface, NULL, 0, 0); + wl_surface_commit(ui->ninbot.surface); + ui->ninbot.window_opened = false; +} + + void ninbot_toplevel_close_handler ( void *data, @@ -172,6 +179,7 @@ void ninbot_toplevel_close_handler server_view_destroy(view); } } + xwayland_toplevel_hide(ui); } static const struct xdg_toplevel_listener ninbot_toplevel_listener = { @@ -258,6 +266,7 @@ void xwayland_toplevel_destroy(struct server_ui *ui) { ui->ninbot.top_level = NULL; ui->ninbot.xdg_surface = NULL; ui->ninbot.surface = NULL; + ui->ninbot.window_opened = false; } struct server_ui * From e8d8062d759ece3bd4dd769af223bd0c97dbfa9a Mon Sep 17 00:00:00 2001 From: GroobleDierne Date: Sat, 11 Oct 2025 13:15:59 +0200 Subject: [PATCH 8/8] fix some crashes --- include/server/ui.h | 1 + waywall/server/ui.c | 8 ++++++-- waywall/wrap.c | 8 ++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/server/ui.h b/include/server/ui.h index f041f9df..9024df6c 100644 --- a/include/server/ui.h +++ b/include/server/ui.h @@ -112,6 +112,7 @@ void server_ui_hide(struct server_ui *ui); void server_ui_set_fullscreen(struct server_ui *ui, bool fullscreen); void server_ui_show(struct server_ui *ui); void xwayland_toplevel_show(struct server_ui *ui); +void xwayland_toplevel_hide(struct server_ui *ui); void server_ui_use_config(struct server_ui *ui, struct server_ui_config *config); struct server_ui_config *server_ui_config_create(struct server_ui *ui, struct config *cfg); diff --git a/waywall/server/ui.c b/waywall/server/ui.c index d12c5fa9..beb15af8 100644 --- a/waywall/server/ui.c +++ b/waywall/server/ui.c @@ -176,7 +176,7 @@ void ninbot_toplevel_close_handler wl_list_for_each_safe(view, tmp_view, &ui->views, link) { // Can we nest native Wayland apps except Minecraft? If so this is problematic if (strcmp(view->impl->name, "xwayland") == 0) { - server_view_destroy(view); + wl_resource_destroy(view->surface->resource); } } xwayland_toplevel_hide(ui); @@ -212,7 +212,9 @@ static const struct xdg_surface_listener xdg_surface_listener = { static void xdg_surface_configure_handler (void *data, struct xdg_surface *xdg_surface, uint32_t serial) { struct server_ui *ui = data; - xdg_surface_set_window_geometry(xdg_surface, 0, 0, ui->ninbot.width, ui->ninbot.height); + if (ui->ninbot.width != 0 && ui->ninbot.height != 0) { + xdg_surface_set_window_geometry(xdg_surface, 0, 0, ui->ninbot.width, ui->ninbot.height); + } xdg_surface_ack_configure(xdg_surface, serial); wl_surface_commit(ui->ninbot.surface); } @@ -370,6 +372,7 @@ server_ui_destroy(struct server_ui *ui) { zxdg_toplevel_decoration_v1_destroy(ui->xdg_decoration); } + xwayland_toplevel_destroy(ui); xdg_toplevel_destroy(ui->xdg_toplevel); xdg_surface_destroy(ui->xdg_surface); wl_subsurface_destroy(ui->tree.subsurface); @@ -391,6 +394,7 @@ server_ui_hide(struct server_ui *ui) { ui->mapped = false; wl_signal_emit_mutable(&ui->server->events.map_status, &ui->mapped); + xwayland_toplevel_hide(ui); } void diff --git a/waywall/wrap.c b/waywall/wrap.c index a2d7596c..d2d38faa 100644 --- a/waywall/wrap.c +++ b/waywall/wrap.c @@ -262,6 +262,14 @@ floating_view_destroy(struct wrap *wrap, struct server_view *view) { floating_find_anchored(wrap); floating_update_anchored(wrap); } + + if (wl_list_length(&wrap->floating.views) == 0 && view->ui->ninbot.window_opened) { + xwayland_toplevel_hide(view->ui); + if (view->ui->ninbot.is_focused) { + server_set_input_focus(wrap->server, wrap->view); + view->ui->ninbot.is_focused = false; + } + } return; }