mirror of
https://github.com/swaywm/sway.git
synced 2026-02-04 04:06:18 -05:00
Merge ed931b955f into 845cdb190f
This commit is contained in:
commit
f4f6f7a367
9 changed files with 245 additions and 0 deletions
|
|
@ -74,10 +74,22 @@ struct sway_seat_node {
|
|||
struct wl_listener destroy;
|
||||
};
|
||||
|
||||
struct wlr_xdg_toplevel_drag_v1;
|
||||
|
||||
struct sway_drag {
|
||||
struct sway_seat *seat;
|
||||
struct wlr_drag *wlr_drag;
|
||||
struct wlr_xdg_toplevel_drag_v1 *toplevel_drag; // may be NULL
|
||||
struct wlr_surface *origin; // surface where drag started
|
||||
|
||||
// For xdg-toplevel-drag: track the dragged surface ourselves rather than
|
||||
// trusting wlroots' toplevel pointer, which may not be NULLed promptly
|
||||
// during destruction sequences. This mirrors Mutter's approach.
|
||||
struct wlr_surface *toplevel_surface; // the attached toplevel's surface
|
||||
struct wl_listener toplevel_surface_destroy;
|
||||
|
||||
struct wl_listener destroy;
|
||||
struct wl_listener motion;
|
||||
};
|
||||
|
||||
struct sway_seat {
|
||||
|
|
@ -93,6 +105,14 @@ struct sway_seat {
|
|||
struct wlr_scene_tree *scene_tree;
|
||||
struct wlr_scene_tree *drag_icons;
|
||||
|
||||
// Container being dragged via xdg-toplevel-drag protocol.
|
||||
// This container is skipped during hit testing so that
|
||||
// drop targets underneath can receive pointer focus.
|
||||
struct sway_container *toplevel_drag_container;
|
||||
|
||||
// Origin surface of pending drag (set in request_start_drag, used in start_drag)
|
||||
struct wlr_surface *pending_drag_origin;
|
||||
|
||||
bool has_focus;
|
||||
struct wl_list focus_stack; // list of containers in focus order
|
||||
struct sway_workspace *workspace;
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ struct sway_server {
|
|||
|
||||
struct wl_listener request_set_cursor_shape;
|
||||
|
||||
struct wlr_xdg_toplevel_drag_manager_v1 *xdg_toplevel_drag_manager;
|
||||
|
||||
struct wlr_tearing_control_manager_v1 *tearing_control_v1;
|
||||
struct wl_listener tearing_control_new_object;
|
||||
struct wl_list tearing_controllers; // sway_tearing_controller::link
|
||||
|
|
|
|||
|
|
@ -233,6 +233,12 @@ void container_get_box(struct sway_container *container, struct wlr_box *box);
|
|||
void container_floating_translate(struct sway_container *con,
|
||||
double x_amount, double y_amount);
|
||||
|
||||
/**
|
||||
* Update scene graph position immediately for a floating container.
|
||||
* Used during drag operations for smoother visual feedback.
|
||||
*/
|
||||
void container_floating_update_scene_position(struct sway_container *con);
|
||||
|
||||
/**
|
||||
* Choose an output for the floating container's new position.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_data_device.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
#include <wlr/types/wlr_xdg_toplevel_drag_v1.h>
|
||||
#include <wlr/types/wlr_xdg_toplevel_tag_v1.h>
|
||||
#include <wlr/util/edges.h>
|
||||
#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))
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
#include <wlr/types/wlr_tablet_v2.h>
|
||||
#include <wlr/types/wlr_touch.h>
|
||||
#include <wlr/types/wlr_xcursor_manager.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
#include <wlr/types/wlr_xdg_toplevel_drag_v1.h>
|
||||
#include "config.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
|
|
@ -386,12 +388,118 @@ void drag_icons_update_position(struct sway_seat *seat) {
|
|||
}
|
||||
}
|
||||
|
||||
static void toplevel_drag_surface_handle_destroy(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct sway_drag *drag = wl_container_of(listener, drag, toplevel_surface_destroy);
|
||||
// Surface is being destroyed - clear our tracking. This is analogous to
|
||||
// Mutter's on_dragged_window_unmanaging callback.
|
||||
wl_list_remove(&drag->toplevel_surface_destroy.link);
|
||||
wl_list_init(&drag->toplevel_surface_destroy.link);
|
||||
drag->toplevel_surface = NULL;
|
||||
drag->seat->toplevel_drag_container = NULL;
|
||||
}
|
||||
|
||||
// Find a floating XDG view that has a toplevel drag attached to it.
|
||||
// This is safer than accessing toplevel_drag->toplevel which may be stale.
|
||||
static struct sway_view *find_xdg_view_with_toplevel_drag(
|
||||
struct wlr_xdg_toplevel_drag_v1 *toplevel_drag) {
|
||||
// Search all workspaces for floating containers
|
||||
for (int i = 0; i < root->outputs->length; i++) {
|
||||
struct sway_output *output = root->outputs->items[i];
|
||||
for (int j = 0; j < output->workspaces->length; j++) {
|
||||
struct sway_workspace *ws = output->workspaces->items[j];
|
||||
for (int k = 0; k < ws->floating->length; k++) {
|
||||
struct sway_container *con = ws->floating->items[k];
|
||||
if (con->view == NULL || con->view->type != SWAY_VIEW_XDG_SHELL) {
|
||||
continue;
|
||||
}
|
||||
struct wlr_xdg_toplevel *wlr_toplevel = con->view->wlr_xdg_toplevel;
|
||||
if (wlr_toplevel == NULL) {
|
||||
continue;
|
||||
}
|
||||
// Use wlroots' safe lookup function to check if this toplevel
|
||||
// has the drag attached
|
||||
struct wlr_xdg_toplevel_drag_v1 *found =
|
||||
wlr_xdg_toplevel_drag_v1_from_wlr_xdg_toplevel(
|
||||
server.xdg_toplevel_drag_manager,
|
||||
wlr_toplevel);
|
||||
if (found == toplevel_drag) {
|
||||
return con->view;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void toplevel_drag_handle_motion(struct wl_listener *listener, void *data) {
|
||||
struct sway_drag *drag = wl_container_of(listener, drag, motion);
|
||||
struct wlr_xdg_toplevel_drag_v1 *toplevel_drag = drag->toplevel_drag;
|
||||
struct sway_seat *seat = drag->seat;
|
||||
|
||||
// If we have a tracked surface being dragged, move its container.
|
||||
// We track the surface ourselves rather than trusting wlroots' toplevel
|
||||
// pointer, which may not be NULLed promptly during destruction.
|
||||
if (drag->toplevel_surface != NULL) {
|
||||
struct sway_container *con = seat->toplevel_drag_container;
|
||||
// Verify container is still valid and floating
|
||||
if (con != NULL && con->view != NULL && con->view->surface != NULL &&
|
||||
container_is_floating(con)) {
|
||||
// Account for XDG surface geometry offset. The protocol's
|
||||
// x_offset/y_offset are in surface coordinates, but container
|
||||
// position is relative to window content.
|
||||
struct wlr_box *geo = &con->view->geometry;
|
||||
double x = seat->cursor->cursor->x - toplevel_drag->x_offset - geo->x;
|
||||
double y = seat->cursor->cursor->y - toplevel_drag->y_offset - geo->y;
|
||||
container_floating_move_to(con, x, y);
|
||||
// Update scene position immediately for smooth visual feedback.
|
||||
container_floating_update_scene_position(con);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// No surface tracked yet - search for a floating XDG view that has this
|
||||
// toplevel drag attached. This avoids accessing toplevel_drag->toplevel
|
||||
// which may point to freed memory.
|
||||
struct sway_view *view = find_xdg_view_with_toplevel_drag(toplevel_drag);
|
||||
if (view == NULL || view->container == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!container_is_floating(view->container)) {
|
||||
// Tiled container being torn out - float it at cursor position
|
||||
container_set_floating(view->container, true);
|
||||
}
|
||||
|
||||
struct wlr_surface *surface = view->surface;
|
||||
if (surface == NULL || !surface->mapped) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start tracking this surface and container for movement.
|
||||
// Listen for surface destruction so we can clear our pointer.
|
||||
drag->toplevel_surface = surface;
|
||||
drag->toplevel_surface_destroy.notify = toplevel_drag_surface_handle_destroy;
|
||||
wl_signal_add(&surface->events.destroy, &drag->toplevel_surface_destroy);
|
||||
seat->toplevel_drag_container = view->container;
|
||||
|
||||
// Account for XDG surface geometry offset
|
||||
struct wlr_box *geo = &view->geometry;
|
||||
double x = seat->cursor->cursor->x - toplevel_drag->x_offset - geo->x;
|
||||
double y = seat->cursor->cursor->y - toplevel_drag->y_offset - geo->y;
|
||||
container_floating_move_to(view->container, x, y);
|
||||
container_floating_update_scene_position(view->container);
|
||||
}
|
||||
|
||||
static void drag_handle_destroy(struct wl_listener *listener, void *data) {
|
||||
struct sway_drag *drag = wl_container_of(listener, drag, destroy);
|
||||
|
||||
// Focus enter isn't sent during drag, so refocus the focused node, layer
|
||||
// surface or unmanaged surface.
|
||||
struct sway_seat *seat = drag->seat;
|
||||
|
||||
// Clear toplevel drag container tracking
|
||||
seat->toplevel_drag_container = NULL;
|
||||
struct sway_node *focus = seat_get_focus(seat);
|
||||
if (focus) {
|
||||
seat_set_focus(seat, NULL);
|
||||
|
|
@ -408,6 +516,15 @@ static void drag_handle_destroy(struct wl_listener *listener, void *data) {
|
|||
|
||||
drag->wlr_drag->data = NULL;
|
||||
wl_list_remove(&drag->destroy.link);
|
||||
if (drag->toplevel_drag != NULL) {
|
||||
// Mark drag as ended so client can safely destroy xdg_toplevel_drag
|
||||
wlr_xdg_toplevel_drag_v1_finish(drag->toplevel_drag);
|
||||
wl_list_remove(&drag->motion.link);
|
||||
// Clean up our surface tracking listener if active
|
||||
if (drag->toplevel_surface != NULL) {
|
||||
wl_list_remove(&drag->toplevel_surface_destroy.link);
|
||||
}
|
||||
}
|
||||
free(drag);
|
||||
}
|
||||
|
||||
|
|
@ -418,6 +535,7 @@ static void handle_request_start_drag(struct wl_listener *listener,
|
|||
|
||||
if (wlr_seat_validate_pointer_grab_serial(seat->wlr_seat,
|
||||
event->origin, event->serial)) {
|
||||
seat->pending_drag_origin = event->origin;
|
||||
wlr_seat_start_pointer_drag(seat->wlr_seat, event->drag, event->serial);
|
||||
return;
|
||||
}
|
||||
|
|
@ -425,6 +543,7 @@ static void handle_request_start_drag(struct wl_listener *listener,
|
|||
struct wlr_touch_point *point;
|
||||
if (wlr_seat_validate_touch_grab_serial(seat->wlr_seat,
|
||||
event->origin, event->serial, &point)) {
|
||||
seat->pending_drag_origin = event->origin;
|
||||
wlr_seat_start_touch_drag(seat->wlr_seat,
|
||||
event->drag, event->serial, point);
|
||||
return;
|
||||
|
|
@ -448,11 +567,27 @@ static void handle_start_drag(struct wl_listener *listener, void *data) {
|
|||
}
|
||||
drag->seat = seat;
|
||||
drag->wlr_drag = wlr_drag;
|
||||
drag->origin = seat->pending_drag_origin;
|
||||
seat->pending_drag_origin = NULL;
|
||||
wlr_drag->data = drag;
|
||||
|
||||
drag->destroy.notify = drag_handle_destroy;
|
||||
wl_signal_add(&wlr_drag->events.destroy, &drag->destroy);
|
||||
|
||||
// Check if this drag has a toplevel_drag associated with it
|
||||
if (wlr_drag->source != NULL) {
|
||||
drag->toplevel_drag = wlr_xdg_toplevel_drag_v1_from_wlr_data_source(
|
||||
server.xdg_toplevel_drag_manager, wlr_drag->source);
|
||||
if (drag->toplevel_drag != NULL) {
|
||||
wlr_xdg_toplevel_drag_v1_start(drag->toplevel_drag);
|
||||
drag->motion.notify = toplevel_drag_handle_motion;
|
||||
wl_signal_add(&wlr_drag->events.motion, &drag->motion);
|
||||
// Initialize surface tracking (will be set on first identification)
|
||||
drag->toplevel_surface = NULL;
|
||||
wl_list_init(&drag->toplevel_surface_destroy.link);
|
||||
}
|
||||
}
|
||||
|
||||
struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon;
|
||||
if (wlr_drag_icon != NULL) {
|
||||
struct wlr_scene_tree *tree = wlr_scene_drag_icon_create(seat->drag_icons, wlr_drag_icon);
|
||||
|
|
@ -1555,6 +1690,10 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) {
|
|||
}
|
||||
|
||||
void seatop_unref(struct sway_seat *seat, struct sway_container *con) {
|
||||
// Clear toplevel drag tracking if this container is being destroyed
|
||||
if (seat->toplevel_drag_container == con) {
|
||||
seat->toplevel_drag_container = NULL;
|
||||
}
|
||||
if (seat->seatop_impl->unref) {
|
||||
seat->seatop_impl->unref(seat, con);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
#include <float.h>
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <wlr/types/wlr_compositor.h>
|
||||
#include <wlr/types/wlr_cursor.h>
|
||||
#include <wlr/types/wlr_data_device.h>
|
||||
#include <wlr/types/wlr_subcompositor.h>
|
||||
#include <wlr/types/wlr_tablet_v2.h>
|
||||
#include <wlr/types/wlr_xcursor_manager.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
#include <wlr/types/wlr_xdg_toplevel_drag_v1.h>
|
||||
#include "gesture.h"
|
||||
#include "sway/desktop/transaction.h"
|
||||
#include "sway/input/cursor.h"
|
||||
|
|
@ -612,11 +616,44 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
|
|||
check_focus_follows_mouse(seat, e, node);
|
||||
}
|
||||
|
||||
// Check for active xdg-toplevel-drag
|
||||
struct wlr_drag *wlr_drag = seat->wlr_seat->drag;
|
||||
struct sway_drag *drag = wlr_drag ? wlr_drag->data : NULL;
|
||||
bool toplevel_drag_active = drag && drag->toplevel_drag && drag->origin;
|
||||
|
||||
// Per xdg-toplevel-drag protocol: "The attached window does not participate
|
||||
// in the selection of the drag target." If cursor is over the dragged
|
||||
// toplevel, do a second hit test with it disabled to find what's underneath.
|
||||
// Use seat->toplevel_drag_container which is safely managed by the motion handler.
|
||||
struct wlr_scene_tree *disabled_tree = NULL;
|
||||
struct sway_container *dragged_con = seat->toplevel_drag_container;
|
||||
if (toplevel_drag_active && surface != NULL && dragged_con != NULL &&
|
||||
dragged_con->view != NULL && dragged_con->view->surface != NULL) {
|
||||
// Check if cursor is over the dragged container's surface
|
||||
if (dragged_con->view->surface == wlr_surface_get_root_surface(surface)) {
|
||||
// Cursor is over the dragged toplevel - find what's underneath
|
||||
if (dragged_con->scene_tree != NULL) {
|
||||
disabled_tree = dragged_con->scene_tree;
|
||||
wlr_scene_node_set_enabled(&disabled_tree->node, false);
|
||||
node = node_at_coords(seat, cursor->cursor->x, cursor->cursor->y,
|
||||
&surface, &sx, &sy);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Re-enable after all processing that might destroy the container
|
||||
if (disabled_tree != NULL) {
|
||||
wlr_scene_node_set_enabled(&disabled_tree->node, true);
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
if (seat_is_input_allowed(seat, surface)) {
|
||||
wlr_seat_pointer_notify_enter(seat->wlr_seat, surface, sx, sy);
|
||||
wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);
|
||||
}
|
||||
} else if (toplevel_drag_active) {
|
||||
// Cursor is outside any window - keep focus on origin surface
|
||||
wlr_seat_pointer_notify_enter(seat->wlr_seat, drag->origin, 0, 0);
|
||||
wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, 0, 0);
|
||||
} else {
|
||||
cursor_update_image(cursor, node);
|
||||
wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
#include <wlr/types/wlr_xdg_foreign_v1.h>
|
||||
#include <wlr/types/wlr_xdg_foreign_v2.h>
|
||||
#include <wlr/types/wlr_xdg_output_v1.h>
|
||||
#include <wlr/types/wlr_xdg_toplevel_drag_v1.h>
|
||||
#include <wlr/types/wlr_xdg_toplevel_tag_v1.h>
|
||||
#include <xf86drm.h>
|
||||
#include "config.h"
|
||||
|
|
@ -439,6 +440,9 @@ bool server_init(struct sway_server *server) {
|
|||
wl_signal_add(&xdg_toplevel_tag_manager_v1->events.set_tag,
|
||||
&server->xdg_toplevel_tag_manager_v1_set_tag);
|
||||
|
||||
server->xdg_toplevel_drag_manager =
|
||||
wlr_xdg_toplevel_drag_manager_v1_create(server->wl_display, 1);
|
||||
|
||||
struct wlr_cursor_shape_manager_v1 *cursor_shape_manager =
|
||||
wlr_cursor_shape_manager_v1_create(server->wl_display, 1);
|
||||
server->request_set_cursor_shape.notify = handle_request_set_cursor_shape;
|
||||
|
|
|
|||
|
|
@ -1139,6 +1139,17 @@ void container_floating_translate(struct sway_container *con,
|
|||
node_set_dirty(&con->node);
|
||||
}
|
||||
|
||||
void container_floating_update_scene_position(struct sway_container *con) {
|
||||
if (!container_is_floating(con) || con->scene_tree == NULL) {
|
||||
return;
|
||||
}
|
||||
// Update scene position immediately using pending coordinates.
|
||||
// This provides smoother visual feedback during drag operations
|
||||
// by bypassing the transaction system's batching delay.
|
||||
wlr_scene_node_set_position(&con->scene_tree->node,
|
||||
con->pending.x, con->pending.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose an output for the floating container's new position.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -8,10 +8,13 @@
|
|||
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
|
||||
#include <wlr/types/wlr_fractional_scale_v1.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_cursor.h>
|
||||
#include <wlr/types/wlr_security_context_v1.h>
|
||||
#include <wlr/types/wlr_server_decoration.h>
|
||||
#include <wlr/types/wlr_subcompositor.h>
|
||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
#include <wlr/types/wlr_xdg_toplevel_drag_v1.h>
|
||||
#include <wlr/types/wlr_session_lock_v1.h>
|
||||
#if WLR_HAS_XWAYLAND
|
||||
#include <wlr/xwayland.h>
|
||||
|
|
@ -863,6 +866,19 @@ 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) {
|
||||
struct wlr_box *geo = &view->geometry;
|
||||
double x = seat->cursor->cursor->x - toplevel_drag->x_offset - geo->x;
|
||||
double y = seat->cursor->cursor->y - toplevel_drag->y_offset - geo->y;
|
||||
container_floating_move_to(view->container, x, y);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
view->container->pending.border = config->border;
|
||||
view->container->pending.border_thickness = config->border_thickness;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue