aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--completions/bash/riverctl2
-rw-r--r--completions/fish/riverctl.fish2
-rw-r--r--completions/zsh/_riverctl2
-rw-r--r--doc/riverctl.1.scd4
-rw-r--r--river/Config.zig1
-rw-r--r--river/Cursor.zig50
-rw-r--r--river/Seat.zig30
7 files changed, 65 insertions, 26 deletions
diff --git a/completions/bash/riverctl b/completions/bash/riverctl
index 9793b6f..03ed4f4 100644
--- a/completions/bash/riverctl
+++ b/completions/bash/riverctl
@@ -64,7 +64,7 @@ function __riverctl_completion ()
"unmap") OPTS="-release" ;;
"attach-mode") OPTS="top bottom" ;;
"focus-follows-cursor") OPTS="disabled normal always" ;;
- "set-cursor-warp") OPTS="disabled on-output-change" ;;
+ "set-cursor-warp") OPTS="disabled on-output-change on-focus-change" ;;
"hide-cursor") OPTS="timeout when-typing" ;;
*) return ;;
esac
diff --git a/completions/fish/riverctl.fish b/completions/fish/riverctl.fish
index f24f32a..39ebc7d 100644
--- a/completions/fish/riverctl.fish
+++ b/completions/fish/riverctl.fish
@@ -79,7 +79,7 @@ complete -c riverctl -x -n '__fish_seen_subcommand_from map' -a
complete -c riverctl -x -n '__fish_seen_subcommand_from unmap' -a '-release'
complete -c riverctl -x -n '__fish_seen_subcommand_from attach-mode' -a 'top bottom'
complete -c riverctl -x -n '__fish_seen_subcommand_from focus-follows-cursor' -a 'disabled normal always'
-complete -c riverctl -x -n '__fish_seen_subcommand_from set-cursor-warp' -a 'disabled on-output-change'
+complete -c riverctl -x -n '__fish_seen_subcommand_from set-cursor-warp' -a 'disabled on-output-change on-focus-change'
# Subcommands for 'input'
complete -c riverctl -x -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 2' -a "(__riverctl_list_input_devices)"
diff --git a/completions/zsh/_riverctl b/completions/zsh/_riverctl
index f303e5e..8ed1fe5 100644
--- a/completions/zsh/_riverctl
+++ b/completions/zsh/_riverctl
@@ -178,7 +178,7 @@ _riverctl()
unmap) _alternative 'arguments:optional:(-release)' ;;
attach-mode) _alternative 'arguments:args:(top bottom)' ;;
focus-follows-cursor) _alternative 'arguments:args:(disabled normal always)' ;;
- set-cursor-warp) _alternative 'arguments:args:(disabled on-output-change)' ;;
+ set-cursor-warp) _alternative 'arguments:args:(disabled on-output-change on-focus-change)' ;;
hide-cursor) _riverctl_hide_cursor ;;
*) return 0 ;;
esac
diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd
index b75fcae..aaddd9b 100644
--- a/doc/riverctl.1.scd
+++ b/doc/riverctl.1.scd
@@ -307,12 +307,14 @@ A complete list may be found in _/usr/include/linux/input-event-codes.h_
Hide the cursor when pressing any non-modifier key. Show the cursor
again on any movement.
-*set-cursor-warp* *disabled*|*on-output-change*
+*set-cursor-warp* *disabled*|*on-output-change*|*on-focus-change*
Set the cursor warp mode. There are two available modes:
- _disabled_: Cursor will not be warped. This is the default.
- _on-output-change_: When a different output is focused, the cursor will be
warped to its center.
+ - _on-focus-change_: When a different view/output is focused, the cursor will be
+ warped to its center.
*set-repeat* _rate_ _delay_
Set the keyboard repeat rate to _rate_ key repeats per second and
diff --git a/river/Config.zig b/river/Config.zig
index f654f99..b105a4e 100644
--- a/river/Config.zig
+++ b/river/Config.zig
@@ -36,6 +36,7 @@ pub const FocusFollowsCursorMode = enum {
pub const WarpCursorMode = enum {
disabled,
@"on-output-change",
+ @"on-focus-change",
};
pub const HideCursorWhenTypingMode = enum {
diff --git a/river/Cursor.zig b/river/Cursor.zig
index 1d83444..3f15f3b 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -105,6 +105,7 @@ pressed_count: u32 = 0,
hide_cursor_timer: *wl.EventSource,
hidden: bool = false,
+may_need_warp: bool = false,
axis: wl.Listener(*wlr.Pointer.event.Axis) = wl.Listener(*wlr.Pointer.event.Axis).init(handleAxis),
frame: wl.Listener(*wlr.Cursor) = wl.Listener(*wlr.Cursor).init(handleFrame),
@@ -1050,6 +1051,9 @@ pub fn checkFocusFollowsCursor(self: *Self) void {
/// the target view of a cursor operation potentially being moved to a non-visible tag,
/// becoming fullscreen, etc.
pub fn updateState(self: *Self) void {
+ if (self.may_need_warp) {
+ self.warp();
+ }
if (self.shouldPassthrough()) {
self.mode = .passthrough;
var now: os.timespec = undefined;
@@ -1103,3 +1107,49 @@ fn passthrough(self: *Self, time: u32) void {
self.clearFocus();
}
}
+
+fn warp(self: *Self) void {
+ self.may_need_warp = false;
+ if (self.seat.focused_output == &server.root.noop_output) return;
+ // Warp pointer to center of the focused view/output (In layout coordinates) if enabled.
+ var output_layout_box: wlr.Box = undefined;
+ server.root.output_layout.getBox(self.seat.focused_output.wlr_output, &output_layout_box);
+ const target_box = switch (server.config.warp_cursor) {
+ .disabled => return,
+ .@"on-output-change" => output_layout_box,
+ .@"on-focus-change" => switch (self.seat.focused) {
+ .layer, .lock_surface, .none => output_layout_box,
+ .view => |view| wlr.Box{
+ .x = output_layout_box.x + view.current.box.x,
+ .y = output_layout_box.y + view.current.box.y,
+ .width = view.current.box.width,
+ .height = view.current.box.height,
+ },
+ .xwayland_override_redirect => |or_window| wlr.Box{
+ .x = or_window.xwayland_surface.x,
+ .y = or_window.xwayland_surface.y,
+ .width = or_window.xwayland_surface.width,
+ .height = or_window.xwayland_surface.height,
+ },
+ },
+ };
+ // Checking against the usable box here gives much better UX when, for example,
+ // a status bar allows using the pointer to change tag/view focus.
+ const usable_box = self.seat.focused_output.usable_box;
+ const usable_layout_box = wlr.Box{
+ .x = output_layout_box.x + usable_box.x,
+ .y = output_layout_box.y + usable_box.y,
+ .width = usable_box.width,
+ .height = usable_box.height,
+ };
+ if (!output_layout_box.containsPoint(self.wlr_cursor.x, self.wlr_cursor.y) or
+ (usable_layout_box.containsPoint(self.wlr_cursor.x, self.wlr_cursor.y) and
+ !target_box.containsPoint(self.wlr_cursor.x, self.wlr_cursor.y)))
+ {
+ const lx = @intToFloat(f64, target_box.x + @divTrunc(target_box.width, 2));
+ const ly = @intToFloat(f64, target_box.y + @divTrunc(target_box.height, 2));
+ if (!self.wlr_cursor.warp(null, lx, ly)) {
+ log.err("failed to warp cursor on focus change", .{});
+ }
+ }
+}
diff --git a/river/Seat.zig b/river/Seat.zig
index 870744b..d678e83 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -265,6 +265,10 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
PointerConstraint.warpToHint(&self.cursor);
constraint.sendDeactivated();
self.cursor.constraint = null;
+ } else {
+ // Depending on configuration and cursor position, changing keyboard focus
+ // may cause the cursor to be warped.
+ self.cursor.may_need_warp = true;
}
} else {
self.wlr_seat.keyboardClearFocus();
@@ -273,6 +277,10 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
PointerConstraint.warpToHint(&self.cursor);
constraint.sendDeactivated();
self.cursor.constraint = null;
+ } else {
+ // Depending on configuration and cursor position, changing keyboard focus
+ // may cause the cursor to be warped.
+ self.cursor.may_need_warp = true;
}
}
@@ -313,28 +321,6 @@ pub fn focusOutput(self: *Self, output: *Output) void {
it = self.status_trackers.first;
while (it) |node| : (it = node.next) node.data.sendOutput(.focused);
-
- if (self.focused_output == &server.root.noop_output) return;
-
- // Warp pointer to center of newly focused output (In layout coordinates),
- // but only if cursor is not already on the output and this feature is enabled.
- switch (server.config.warp_cursor) {
- .disabled => {},
- .@"on-output-change" => {
- var layout_box: wlr.Box = undefined;
- server.root.output_layout.getBox(output.wlr_output, &layout_box);
- if (!layout_box.containsPoint(self.cursor.wlr_cursor.x, self.cursor.wlr_cursor.y)) {
- var output_width: i32 = undefined;
- var output_height: i32 = undefined;
- output.wlr_output.effectiveResolution(&output_width, &output_height);
- const lx = @intToFloat(f64, layout_box.x + @divTrunc(output_width, 2));
- const ly = @intToFloat(f64, layout_box.y + @divTrunc(output_height, 2));
- if (!self.cursor.wlr_cursor.warp(null, lx, ly)) {
- log.err("failed to warp cursor on output change", .{});
- }
- }
- },
- }
}
pub fn handleActivity(self: Self) void {