From 6ac37faaa461cdfe09c88ef5e8194cbc4e932827 Mon Sep 17 00:00:00 2001 From: Ryan Walklin Date: Fri, 12 Dec 2025 21:04:15 +1300 Subject: [PATCH] xdg_shell: float and position dragged toplevels Make toplevels attached to an xdg-toplevel-drag automatically float, and position them at the cursor when first mapped. This enables the typical tab tear-off workflow: 1. User starts dragging a tab in a browser 2. Browser creates a new toplevel attached to the drag 3. Sway floats the toplevel and positions it at the cursor 4. The motion handler (from previous commit) keeps it moving with cursor --- sway/desktop/xdg_shell.c | 10 ++++++++++ sway/tree/view.c | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 48638817c..5428b9f6b 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -2,10 +2,13 @@ #include #include #include +#include #include +#include #include #include #include "log.h" +#include "sway/server.h" #include "sway/decoration.h" #include "sway/scene_descriptor.h" #include "sway/desktop/transaction.h" @@ -228,6 +231,13 @@ static void set_resizing(struct sway_view *view, bool resizing) { static bool wants_floating(struct sway_view *view) { struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; struct wlr_xdg_toplevel_state *state = &toplevel->current; + + // Check if this toplevel is attached to a toplevel drag + if (wlr_xdg_toplevel_drag_v1_from_wlr_xdg_toplevel( + server.xdg_toplevel_drag_manager, toplevel) != NULL) { + return true; + } + return (state->min_width != 0 && state->min_height != 0 && (state->min_width == state->max_width || state->min_height == state->max_height)) diff --git a/sway/tree/view.c b/sway/tree/view.c index be813be98..71eb5bc68 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -8,10 +8,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #if WLR_HAS_XWAYLAND #include @@ -861,6 +864,18 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, view->container->pending.border = config->floating_border; view->container->pending.border_thickness = config->floating_border_thickness; container_set_floating(view->container, true); + + // If this is a toplevel attached to a drag, position it at the cursor + if (view->wlr_xdg_toplevel != NULL) { + struct wlr_xdg_toplevel_drag_v1 *toplevel_drag = + wlr_xdg_toplevel_drag_v1_from_wlr_xdg_toplevel( + server.xdg_toplevel_drag_manager, view->wlr_xdg_toplevel); + if (toplevel_drag != NULL) { + double x = seat->cursor->cursor->x - toplevel_drag->x_offset; + double y = seat->cursor->cursor->y - toplevel_drag->y_offset; + container_floating_move_to(view->container, x, y); + } + } } else { view->container->pending.border = config->border; view->container->pending.border_thickness = config->border_thickness;