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:
@ -36,6 +36,7 @@ pub const FocusFollowsCursorMode = enum {
|
||||
pub const WarpCursorMode = enum {
|
||||
disabled,
|
||||
@"on-output-change",
|
||||
@"on-focus-change",
|
||||
};
|
||||
|
||||
pub const HideCursorWhenTypingMode = enum {
|
||||
|
@ -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", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user