diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd index 3dc8330..cebac70 100644 --- a/doc/riverctl.1.scd +++ b/doc/riverctl.1.scd @@ -228,7 +228,7 @@ A complete list may be found in _/usr/include/linux/input-event-codes.h_ There are three available modes: - _disabled_: Moving the cursor does not affect focus. This is - the default + the default. - _normal_: Moving the cursor over a view will focus that view. Moving the cursor within a view will not re-focus that view if focus has moved elsewhere. @@ -238,6 +238,13 @@ A complete list may be found in _/usr/include/linux/input-event-codes.h_ If the view to be focused is on an output that does not have focus, focus is switched to that output. +*set-cursor-warp* *disabled*|*on-output-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. + *opacity* _focused_ _unfocused_ _initial_ _step-size_ _delta-t_ Configure server-side opacity of views, including transition animations. A value of 0.0 is fully transparent while 1.0 is fully diff --git a/river/Config.zig b/river/Config.zig index 91e62e5..606dc6b 100644 --- a/river/Config.zig +++ b/river/Config.zig @@ -33,6 +33,11 @@ pub const FocusFollowsCursorMode = enum { strict, }; +pub const WarpCursorMode = enum { + disabled, + @"on-output-change", +}; + /// Color of background in RGBA (alpha should only affect nested sessions) background_color: [4]f32 = [_]f32{ 0.0, 0.16862745, 0.21176471, 1.0 }, // Solarized base03 @@ -60,6 +65,9 @@ csd_filter: std.ArrayList([]const u8), /// The selected focus_follows_cursor mode focus_follows_cursor: FocusFollowsCursorMode = .disabled, +/// If true, the cursor warps to the center of the focused output +warp_cursor: WarpCursorMode = .disabled, + /// The default layout namespace for outputs which have never had a per-output /// value set. Call Output.handleLayoutNamespaceChange() on setting this if /// Output.layout_namespace is null. diff --git a/river/Seat.zig b/river/Seat.zig index b91a309..e741617 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -266,6 +266,23 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void { pub fn focusOutput(self: *Self, output: *Output) void { if (self.focused_output == 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" => { + const layout_box = server.root.output_layout.getBox(output.wlr_output).?; + if (!layout_box.containsPoint(self.cursor.wlr_cursor.x, self.cursor.wlr_cursor.y)) { + const eff_res = output.getEffectiveResolution(); + const lx = @intToFloat(f32, layout_box.x + @intCast(i32, eff_res.width / 2)); + const ly = @intToFloat(f32, layout_box.y + @intCast(i32, eff_res.height / 2)); + if (!self.cursor.wlr_cursor.warp(null, lx, ly)) { + log.err("failed to warp cursor on output change", .{}); + } + } + }, + } + var it = self.status_trackers.first; while (it) |node| : (it = node.next) node.data.sendOutput(.unfocused); diff --git a/river/command.zig b/river/command.zig index 313e11b..eb55343 100644 --- a/river/command.zig +++ b/river/command.zig @@ -58,8 +58,8 @@ const str_to_impl_fn = [_]struct { .{ .name = "focus-output", .impl = @import("command/output.zig").focusOutput }, .{ .name = "focus-view", .impl = @import("command/focus_view.zig").focusView }, .{ .name = "input", .impl = @import("command/input.zig").input }, - .{ .name = "list-inputs", .impl = @import("command/input.zig").listInputs }, .{ .name = "list-input-configs", .impl = @import("command/input.zig").listInputConfigs}, + .{ .name = "list-inputs", .impl = @import("command/input.zig").listInputs }, .{ .name = "map", .impl = @import("command/map.zig").map }, .{ .name = "map-pointer", .impl = @import("command/map.zig").mapPointer }, .{ .name = "mod-layout-value", .impl = @import("command/layout.zig").modLayoutValue }, @@ -68,6 +68,7 @@ const str_to_impl_fn = [_]struct { .{ .name = "output-layout", .impl = @import("command/layout.zig").outputLayout }, .{ .name = "resize", .impl = @import("command/move.zig").resize }, .{ .name = "send-to-output", .impl = @import("command/output.zig").sendToOutput }, + .{ .name = "set-cursor-warp", .impl = @import("command/config.zig").setCursorWarp }, .{ .name = "set-focused-tags", .impl = @import("command/tags.zig").setFocusedTags }, .{ .name = "set-layout-value", .impl = @import("command/layout.zig").setLayoutValue }, .{ .name = "set-repeat", .impl = @import("command/set_repeat.zig").setRepeat }, diff --git a/river/command/config.zig b/river/command/config.zig index 1ae0c5c..80539d4 100644 --- a/river/command/config.zig +++ b/river/command/config.zig @@ -21,6 +21,7 @@ const server = &@import("../main.zig").server; const Error = @import("../command.zig").Error; const Seat = @import("../Seat.zig"); +const Config = @import("../Config.zig"); pub fn borderWidth( allocator: *std.mem.Allocator, @@ -81,6 +82,18 @@ pub fn borderColorUnfocused( while (it) |node| : (it = node.next) node.data.damage.addWhole(); } +pub fn setCursorWarp( + allocator: *std.mem.Allocator, + seat: *Seat, + args: []const []const u8, + out: *?[]const u8, +) Error!void { + if (args.len < 2) return Error.NotEnoughArguments; + if (args.len > 2) return Error.TooManyArguments; + server.config.warp_cursor = std.meta.stringToEnum(Config.WarpCursorMode, args[1]) orelse + return Error.UnknownOption; +} + /// Parse a color in the format #RRGGBB or #RRGGBBAA fn parseRgba(string: []const u8) ![4]f32 { if (string[0] != '#' or (string.len != 7 and string.len != 9)) return error.InvalidRgba;