Cursor: add on-focus-change option

Warp the cursor to the center of the focused view if the cursor is not
in the bounding box of that view already. This helps the user to keep
track of their cursor when they mostly use the keyboard and the cursor
becomes hidden most of the time, also helps trackpad/trackpoint users.
This commit is contained in:
shironeko 2022-08-01 20:31:50 -04:00 committed by Isaac Freund
parent 3141940efb
commit 8036ae2bd1
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
7 changed files with 65 additions and 26 deletions

View File

@ -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

View File

@ -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)"

View File

@ -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

View File

@ -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

View File

@ -36,6 +36,7 @@ pub const FocusFollowsCursorMode = enum {
pub const WarpCursorMode = enum {
disabled,
@"on-output-change",
@"on-focus-change",
};
pub const HideCursorWhenTypingMode = enum {

View File

@ -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", .{});
}
}
}

View File

@ -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 {