git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit807e68473d90036ee11651d36627f704b31c13d3
parent2e17940dd3
authorIsaac Freund <[email protected]>
date2024-12-30 14:05
rwm: rework interactive move/resize

The new approach has the following advantages:

1. xdg-toplevel-requested move/resize by touch and tablet tool is now
   supported through the exact same mechanism as requested pointer
   move/resize.

2. It is now possible for the window manager to start/end move/resize
   with a keyboard binding; move/resize no longer require a pointer
   button to be held.

I think it should also be easier to extend this new approach with
support for window-manager-initiated touch/tablet tool move/resize.

 protocol/river-window-management-v1.xml | 183 +++++++++++++++++++++++++++-----
 river/Seat.zig                          |  15 ++-
 rivercompat/Window.zig                  |   4 +-
 3 files changed, 172 insertions(+), 30 deletions(-)

diff --git a/protocol/river-window-management-v1.xml b/protocol/river-window-management-v1.xml
index 2268a7b..c98c28c 100644
--- a/protocol/river-window-management-v1.xml
+++ b/protocol/river-window-management-v1.xml
@@ -511,39 +511,52 @@
       <arg name="surface" type="object" interface="wl_surface"/>
     </request>
 
-    <event name="pointer_move_requested">
+    <event name="move_requested">
       <description summary="the window requested interactive move">
+        This event informs the window manager that the window has requested to
+        be interactively moved using the pointer. The seat argument indicates
+        the seat for the move and the serial argument identifies the input event
+        (e.g. pointer button press or touch) that started the move.
+
         The xdg-shell protocol for example allows windows to request that an
         interactive move be started, perhaps when a client-side rendered
         titlebar is dragged.
 
-        This event informs the window manager that the window has requested to
-        be interactively moved using the pointer. The window manager is free to
-        honor this request using river_seat_v1.pointer_move_window or ignore it.
+        The window manager may use the river_seat_v1.move_add_window and
+        river_seat_v1.move_start_serial requests to start a move or ignore this
+        event entirely.
 
         This event is double-buffered state and will be followed by a
         river_window_manager_v1.update event.
       </description>
+      <arg name="seat" type="object" interface="river_seat_v1"/>
+      <arg name="serial" type="uint"/>
     </event>
 
-    <event name="pointer_resize_requested">
+    <event name="resize_requested">
       <description summary="the window requested interactive resize">
-        The xdg-shell protocol for example allows windows to request that an
-        interactive resize be started, perhaps when the corner of client-side
-        rendered decorations is dragged.
-
         This event informs the window manager that the window has requested to
-        be interactively resized using the pointer. The window manager is free
-        to honor this request using river_seat_v1.pointer_resize_window or
-        ignore it.
+        be interactively resized using the pointer. The seat argument indicates
+        the seat for the resize and the serial argument identifies the input
+        event (e.g. pointer button press or touch) that started the resize.
 
         The edges argument indicates which edges the window has requested to be
         resized from. The edges argument will never be none and will never have
         both top and bottom or both left and right edges set.
 
+        The xdg-shell protocol for example allows windows to request that an
+        interactive resize be started, perhaps when the corner of client-side
+        rendered decorations is dragged.
+
+        The window manager may use the river_seat_v1.resize_add_window and
+        river_seat_v1.resize_start_serial requests to start a resize or ignore
+        this event entirely.
+
         This event is double-buffered state and will be followed by a
         river_window_manager_v1.update event.
       </description>
+      <arg name="seat" type="object" interface="river_seat_v1"/>
+      <arg name="serial" type="uint"/>
       <arg name="edges" type="uint" enum="edges"/>
     </event>
 
@@ -1107,14 +1120,61 @@
       <arg name="window" type="object" interface="river_window_v1"/>
     </event>
 
-    <request name="pointer_move_window">
-      <description summary="start interactive pointer move of a window">
-        This request causes the position of a window to be updated while the
-        pointer is moved. Multiple windows may be interactively moved at the same
-        time.
+    <request name="move_start_serial">
+      <description summary="start an interactive move">
+        Start an interactive move with a river_window_v1.move_requested event
+        serial. Windows can be added to the move with the move_add_window
+        request.
+
+        All windows must be added after this request and before the next
+        river_window_manager_v1.commit request. Other move_add_window requests
+        will have no effect.
+
+        No windows are implicitly included in the move, even the window that
+        requested the move must be explicitly added with the move_add_window
+        request.
+
+        The move continues until the pointer button, touch point or similar
+        corresponding to the given serial is released or the move_end request is
+        made and applied with a river_window_management_v1.commit request.
+
+        This request is ignored if a move or resize is already in progress.
+
+        This request is double-buffered state and will not be applied until the
+        next river_window_manager_v1.commit request.
+      </description>
+      <arg name="serial" type="uint"/>
+    </request>
+
+    <request name="move_start_pointer">
+      <description summary="start an interactive pointer move">
+        Start an interactive pointer move. Windows can be added to the move with
+        the move_add_window request.
+
+        All windows must be added after this request and before the next
+        river_window_manager_v1.commit request. Other move_add_window requests
+        will have no effect.
+
+        The pointer move continues until the move_end request is made and
+        applied with a river_window_management_v1.commit request.
+
+        This request is ignored if a move or resize is already in progress.
+
+        This request is double-buffered state and will not be applied until the
+        next river_window_manager_v1.commit request.
+      </description>
+    </request>
+
+    <request name="move_add_window">
+      <description summary="add a window to an interactive move">
+        This request causes the position of a window to be updated by an in
+        progress interactive move. Multiple windows may be added to a single
+        interactive move and all added windows will have their position updated.
 
-        The interactive move is ended when all pointer buttons are released.
-        This request is ignored if no pointer buttons are held.
+        All windows must be added after the move_start_serial or
+        move_start_pointer request and before the next
+        river_window_manager_v1.commit request. Other move_add_window requests
+        will have no effect.
 
         This request is double-buffered state and will not be applied until the
         next river_window_manager_v1.commit request.
@@ -1122,14 +1182,74 @@
       <arg name="window" type="object" interface="river_window_v1"/>
     </request>
 
-    <request name="pointer_resize_window">
-      <description summary="start interactive pointer resize of a window">
-        This request causes the window to be resized from the specified edges,
-        updating the position/dimensions of the window as the pointer is moved.
-        Multiple windows may be interactively resized at the same time.
+    <request name="move_end">
+      <description summary="end an interactive move">
+        End an interactive move.
 
-        The interactive resize is ended when all pointer buttons are released.
-        This request is ignored if no pointer buttons are held.
+        This request is ignored if there is no move in progress.
+
+        This request is double-buffered state and will not be applied until the
+        next river_window_manager_v1.commit request.
+      </description>
+    </request>
+
+    <request name="resize_start_serial">
+      <description summary="start an interactive resize">
+        Start an interactive resize with a river_window_v1.resize_requested
+        event serial. Windows can be added to the resize with the
+        resize_add_window request.
+
+        All windows must be added after this request and before the next
+        river_window_manager_v1.commit request. Other resize_add_window requests
+        will have no effect.
+
+        No windows are implicitly included in the resize, even the window that
+        requested the resize must be explicitly added with the resize_add_window
+        request.
+
+        The resize continues until the pointer button, touch point or similar
+        corresponding to the given serial is released or the resize_end request
+        is made and applied with a river_window_management_v1.commit request.
+
+        This request is ignored if a move or resize is already in progress.
+
+        This request is double-buffered state and will not be applied until the
+        next river_window_manager_v1.commit request.
+      </description>
+      <arg name="serial" type="uint"/>
+    </request>
+
+    <request name="resize_start_pointer">
+      <description summary="start an interactive pointer resize">
+        Start an interactive pointer resize. Windows can be added to the resize
+        with the resize_add_window request.
+
+        All windows must be added after this request and before the next
+        river_window_manager_v1.commit request. Other resize_add_window requests
+        will have no effect.
+
+        The pointer resize continues until the resize_end request is made and
+        applied with a river_window_management_v1.commit request.
+
+        This request is ignored if a pointer resize or resize is already in
+        progress.
+
+        This request is double-buffered state and will not be applied until the
+        next river_window_manager_v1.commit request.
+      </description>
+    </request>
+
+    <request name="resize_add_window">
+      <description summary="add a window to an interactive resize">
+        This request causes the position/dimensions of a window to be updated by
+        an in progress interactive resize. Multiple windows may be added to a
+        single interactive resize and all added windows will have their position
+        updated.
+
+        All windows must be added after the resize_start_serial or
+        resize_start_pointer request and before the next
+        river_window_manager_v1.commit request. Other resize_add_window requests
+        will have no effect.
 
         It is a protocol error to set the edges argument to none. It is also an
         error to set both the top and bottom edges or both the left and right
@@ -1143,6 +1263,17 @@
       <arg name="edges" type="uint" enum="river_window_v1.edges"/>
     </request>
 
+    <request name="resize_end">
+      <description summary="end an interactive resize">
+        End an interactive resize.
+
+        This request is ignored if there is no interactive resize in progress.
+
+        This request is double-buffered state and will not be applied until the
+        next river_window_manager_v1.commit request.
+      </description>
+    </request>
+
     <request name="pointer_confine_to_region">
       <description summary="prevent the pointer from leaving a region">
         This request tells the compositor to confine the movement of the pointer
diff --git a/river/Seat.zig b/river/Seat.zig
index 1bab0d3..36862f7 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -395,6 +395,7 @@ fn handleRequest(
             // XXX send protocol error
             seat_v1.destroy();
         },
+
         .focus_window => |args| {
             const data = args.window.getUserData() orelse return;
             const window: *Window = @ptrCast(@alignCast(data));
@@ -402,10 +403,20 @@ fn handleRequest(
         },
         .focus_shell_surface => {},
         .clear_focus => seat.uncommitted.focus = .none,
-        .pointer_move_window => {},
-        .pointer_resize_window => {},
+
+        .move_start_serial => {},
+        .move_start_pointer => {},
+        .move_add_window => {},
+        .move_end => {},
+
+        .resize_start_serial => {},
+        .resize_start_pointer => {},
+        .resize_add_window => {},
+        .resize_end => {},
+
         .pointer_confine_to_region => {},
         .pointer_warp => {},
+
         .get_xkb_binding => |args| {
             XkbBinding.create(
                 seat,
diff --git a/rivercompat/Window.zig b/rivercompat/Window.zig
index dc3df1f..75c8ff7 100644
--- a/rivercompat/Window.zig
+++ b/rivercompat/Window.zig
@@ -69,8 +69,8 @@ fn handleEvent(window_v1: *river.WindowV1, event: river.WindowV1.Event, window:
         .title => {},
         .parent => {},
         .decoration_hint => {},
-        .pointer_move_requested => {},
-        .pointer_resize_requested => {},
+        .move_requested => {},
+        .resize_requested => {},
         .show_window_menu_requested => {},
         .maximize_requested => {},
         .unmaximize_requested => {},