Add cursor warp option
This commit is contained in:
		| @ -18,12 +18,11 @@ | ||||
| const std = @import("std"); | ||||
| const mem = std.mem; | ||||
|  | ||||
| /// Validate a glob, returning error.InvalidGlob if it is empty, "**" or has a | ||||
| /// '*' at any position other than the first and/or last byte. | ||||
| /// Validate a glob, returning error.InvalidGlob if is "**" or has a '*' | ||||
| /// at any position other than the first and/or last byte. | ||||
| pub fn validate(glob: []const u8) error{InvalidGlob}!void { | ||||
|     switch (glob.len) { | ||||
|         0 => return error.InvalidGlob, | ||||
|         1 => {}, | ||||
|         0, 1 => {}, | ||||
|         2 => if (glob[0] == '*' and glob[1] == '*') return error.InvalidGlob, | ||||
|         else => if (mem.indexOfScalar(u8, glob[1 .. glob.len - 1], '*') != null) { | ||||
|             return error.InvalidGlob; | ||||
| @ -34,6 +33,7 @@ pub fn validate(glob: []const u8) error{InvalidGlob}!void { | ||||
| test validate { | ||||
|     const testing = std.testing; | ||||
|  | ||||
|     try validate(""); | ||||
|     try validate("*"); | ||||
|     try validate("a"); | ||||
|     try validate("*a"); | ||||
| @ -48,7 +48,6 @@ test validate { | ||||
|     try validate("abc*"); | ||||
|     try validate("*abc*"); | ||||
|  | ||||
|     try testing.expectError(error.InvalidGlob, validate("")); | ||||
|     try testing.expectError(error.InvalidGlob, validate("**")); | ||||
|     try testing.expectError(error.InvalidGlob, validate("***")); | ||||
|     try testing.expectError(error.InvalidGlob, validate("a*c")); | ||||
| @ -67,7 +66,9 @@ pub fn match(s: []const u8, glob: []const u8) bool { | ||||
|         validate(glob) catch unreachable; | ||||
|     } | ||||
|  | ||||
|     if (glob.len == 1) { | ||||
|     if (glob.len == 0) { | ||||
|         return s.len == 0; | ||||
|     } else if (glob.len == 1) { | ||||
|         return glob[0] == '*' or mem.eql(u8, s, glob); | ||||
|     } | ||||
|  | ||||
| @ -89,6 +90,9 @@ test match { | ||||
|     const testing = std.testing; | ||||
|  | ||||
|     try testing.expect(match("", "*")); | ||||
|     try testing.expect(match("", "")); | ||||
|     try testing.expect(!match("a", "")); | ||||
|     try testing.expect(!match("", "a")); | ||||
|  | ||||
|     try testing.expect(match("a", "*")); | ||||
|     try testing.expect(match("a", "*a*")); | ||||
| @ -165,8 +169,10 @@ pub fn order(a: []const u8, b: []const u8) std.math.Order { | ||||
|         return .lt; | ||||
|     } | ||||
|  | ||||
|     const count_a = @as(u2, @intFromBool(a[0] == '*')) + @intFromBool(a[a.len - 1] == '*'); | ||||
|     const count_b = @as(u2, @intFromBool(b[0] == '*')) + @intFromBool(b[b.len - 1] == '*'); | ||||
|     const count_a = if (a.len != 0) @as(u2, @intFromBool(a[0] == '*')) + | ||||
|         @intFromBool(a[a.len - 1] == '*') else 0; | ||||
|     const count_b = if (b.len != 0) @as(u2, @intFromBool(b[0] == '*')) + | ||||
|         @intFromBool(b[b.len - 1] == '*') else 0; | ||||
|  | ||||
|     if (count_a == 0 and count_b == 0) { | ||||
|         return .eq; | ||||
| @ -182,6 +188,7 @@ test order { | ||||
|     const testing = std.testing; | ||||
|     const Order = std.math.Order; | ||||
|  | ||||
|     try testing.expectEqual(Order.eq, order("", "")); | ||||
|     try testing.expectEqual(Order.eq, order("*", "*")); | ||||
|     try testing.expectEqual(Order.eq, order("*a*", "*b*")); | ||||
|     try testing.expectEqual(Order.eq, order("a*", "*b")); | ||||
| @ -204,6 +211,7 @@ test order { | ||||
|         "bababab", | ||||
|         "b", | ||||
|         "a", | ||||
|         "", | ||||
|     }; | ||||
|  | ||||
|     for (descending, 0..) |a, i| { | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| function __riverctl_completion () | ||||
| { | ||||
| 	local rule_actions="float no-float ssd csd tags output position relative-position dimensions fullscreen no-fullscreen" | ||||
| 	local rule_actions="float no-float ssd csd tags output position relative-position dimensions fullscreen no-fullscreen warp no-warp" | ||||
| 	if [ "${COMP_CWORD}" -eq 1 ] | ||||
| 	then | ||||
| 		OPTS=" \ | ||||
|  | ||||
| @ -86,10 +86,10 @@ complete -c riverctl -n '__fish_seen_subcommand_from default-attach-mode' | ||||
| complete -c riverctl -n '__fish_seen_subcommand_from output-attach-mode'          -n '__fish_riverctl_complete_arg 2' -a 'top bottom above below after' | ||||
| complete -c riverctl -n '__fish_seen_subcommand_from focus-follows-cursor'        -n '__fish_riverctl_complete_arg 2' -a 'disabled normal always' | ||||
| complete -c riverctl -n '__fish_seen_subcommand_from set-cursor-warp'             -n '__fish_riverctl_complete_arg 2' -a 'disabled on-output-change on-focus-change' | ||||
| complete -c riverctl -n '__fish_seen_subcommand_from list-rules'                  -n '__fish_riverctl_complete_arg 2' -a 'float ssd tags output position dimensions fullscreen' | ||||
| complete -c riverctl -n '__fish_seen_subcommand_from list-rules'                  -n '__fish_riverctl_complete_arg 2' -a 'float ssd tags output position dimensions fullscreen warp' | ||||
|  | ||||
| # Options and subcommands for 'rule-add' and 'rule-del' | ||||
| set -l rule_actions float no-float ssd csd tags output position relative-position dimensions fullscreen no-fullscreen | ||||
| set -l rule_actions float no-float ssd csd tags output position relative-position dimensions fullscreen no-fullscreen warp no-warp | ||||
| complete -c riverctl -n '__fish_seen_subcommand_from rule-add rule-del' -n "not __fish_seen_subcommand_from $rule_actions" -n 'not __fish_seen_argument -o app-id' -o 'app-id' -r | ||||
| complete -c riverctl -n '__fish_seen_subcommand_from rule-add rule-del' -n "not __fish_seen_subcommand_from $rule_actions" -n 'not __fish_seen_argument -o title'  -o 'title' -r | ||||
| complete -c riverctl -n '__fish_seen_subcommand_from rule-add rule-del' -n "not __fish_seen_subcommand_from $rule_actions" -n 'test (math (count (commandline -opc)) % 2) -eq 0' -a "$rule_actions" | ||||
|  | ||||
| @ -202,9 +202,9 @@ _riverctl() | ||||
|                     # In case of a new rule added in river, we just need | ||||
|                     # to add it to the third option between '()', | ||||
|                     # i.e (float no-float <new-option>) | ||||
|                     _arguments '1: :(-app-id -title)' '2: : ' ':: :(float no-float ssd csd tags output position relative-position dimensions fullscreen no-fullscreen)' | ||||
|                     _arguments '1: :(-app-id -title)' '2: : ' ':: :(float no-float ssd csd tags output position relative-position dimensions fullscreen no-fullscreen warp no-warp)' | ||||
|                 ;; | ||||
|                 list-rules) _alternative 'arguments:args:(float ssd tags output position dimensions fullscreen)' ;; | ||||
|                 list-rules) _alternative 'arguments:args:(float ssd tags output position dimensions fullscreen warp)' ;; | ||||
|                 *) return 0 ;; | ||||
|             esac | ||||
|         ;; | ||||
|  | ||||
| @ -315,12 +315,16 @@ matches everything while _\*\*_ and the empty string are invalid. | ||||
| 	  view's preference. Applies to new and existing views. | ||||
| 	- *no-tearing*: Disable tearing for the view regardless of the view's | ||||
| 	  preference. Applies to new and existing views. | ||||
|     - *warp*: Always warp the cursor when switching to this view, regardless of | ||||
|       the _set-cursor-warp_ setting. Applies to new and existing views. | ||||
|     - *no-warp*: Never warp the cursor when switching to this view, regardless | ||||
|       of the _set-cursor-warp_ setting. Applies to new and existing views. | ||||
|  | ||||
| 	Both *float* and *no-float* rules are added to the same list, | ||||
| 	which means that adding a *no-float* rule with the same arguments | ||||
| 	as a *float* rule will overwrite it. The same holds for *ssd* and | ||||
| 	*csd*, *fullscreen* and *no-fullscreen*, *tearing* and | ||||
| 	*no-tearing* rules. | ||||
| 	*no-tearing*, *warp* and *no-warp* rules. | ||||
|  | ||||
| 	If multiple rules in a list match a given view the most specific | ||||
| 	rule will be applied. For example with the following rules | ||||
| @ -348,7 +352,7 @@ matches everything while _\*\*_ and the empty string are invalid. | ||||
| *rule-del* [*-app-id* _glob_|*-title* _glob_] _action_ | ||||
| 	Delete a rule created using *rule-add* with the given arguments. | ||||
|  | ||||
| *list-rules* *float*|*ssd*|*tags*|*position*|*dimensions*|*fullscreen* | ||||
| *list-rules* *float*|*ssd*|*tags*|*position*|*dimensions*|*fullscreen*|*warp* | ||||
| 	Print the specified rule list. The output is ordered from most specific | ||||
| 	to least specific, the same order in which views are checked against | ||||
| 	when searching for a match. Only the first matching rule in the list | ||||
|  | ||||
| @ -108,6 +108,7 @@ rules: struct { | ||||
|     dimensions: RuleList(Dimensions) = .{}, | ||||
|     fullscreen: RuleList(bool) = .{}, | ||||
|     tearing: RuleList(bool) = .{}, | ||||
|     warp: RuleList(bool) = .{}, | ||||
| } = .{}, | ||||
|  | ||||
| /// The selected focus_follows_cursor mode | ||||
| @ -192,6 +193,7 @@ pub fn deinit(config: *Config) void { | ||||
|     config.rules.position.deinit(); | ||||
|     config.rules.dimensions.deinit(); | ||||
|     config.rules.fullscreen.deinit(); | ||||
|     config.rules.warp.deinit(); | ||||
|  | ||||
|     util.gpa.free(config.default_layout_namespace); | ||||
|  | ||||
|  | ||||
| @ -1246,10 +1246,18 @@ fn warp(cursor: *Cursor) void { | ||||
|  | ||||
|     const focused_output = cursor.seat.focused_output orelse return; | ||||
|  | ||||
|     var mode = server.config.warp_cursor; | ||||
|     if (cursor.seat.focused == .view) { | ||||
|         const view = cursor.seat.focused.view; | ||||
|         if (server.config.rules.warp.match(view)) |w| { | ||||
|             mode = if (w) .@"on-focus-change" else .disabled; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // 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(focused_output.wlr_output, &output_layout_box); | ||||
|     const target_box = switch (server.config.warp_cursor) { | ||||
|     const target_box = switch (mode) { | ||||
|         .disabled => return, | ||||
|         .@"on-output-change" => output_layout_box, | ||||
|         .@"on-focus-change" => switch (cursor.seat.focused) { | ||||
|  | ||||
| @ -43,6 +43,8 @@ const Action = enum { | ||||
|     @"no-fullscreen", | ||||
|     tearing, | ||||
|     @"no-tearing", | ||||
|     warp, | ||||
|     @"no-warp", | ||||
| }; | ||||
|  | ||||
| pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void { | ||||
| @ -58,7 +60,7 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void | ||||
|     const action = std.meta.stringToEnum(Action, result.args[0]) orelse return Error.UnknownOption; | ||||
|  | ||||
|     const positional_arguments_count: u8 = switch (action) { | ||||
|         .float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen", .tearing, .@"no-tearing" => 1, | ||||
|         .float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen", .tearing, .@"no-tearing", .warp, .@"no-warp" => 1, | ||||
|         .tags, .output => 2, | ||||
|         .position, .dimensions => 3, | ||||
|         .@"relative-position" => 4, | ||||
| @ -163,6 +165,13 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void | ||||
|                 .value = (action == .fullscreen), | ||||
|             }); | ||||
|         }, | ||||
|         .warp, .@"no-warp" => { | ||||
|             try server.config.rules.warp.add(.{ | ||||
|                 .app_id_glob = app_id_glob, | ||||
|                 .title_glob = title_glob, | ||||
|                 .value = (action == .warp), | ||||
|             }); | ||||
|         }, | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -213,6 +222,9 @@ pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void | ||||
|             _ = server.config.rules.tearing.del(rule); | ||||
|             apply_tearing_rules(); | ||||
|         }, | ||||
|         .warp, .@"no-warp" => { | ||||
|             _ = server.config.rules.warp.del(rule); | ||||
|         }, | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -251,6 +263,7 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error! | ||||
|         dimensions, | ||||
|         fullscreen, | ||||
|         tearing, | ||||
|         warp, | ||||
|     }, args[1]) orelse return Error.UnknownOption; | ||||
|     const max_glob_len = switch (rule_list) { | ||||
|         inline else => |list| @field(server.config.rules, @tagName(list)).getMaxGlobLen(), | ||||
| @ -266,13 +279,14 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error! | ||||
|     try writer.writeAll("action\n"); | ||||
|  | ||||
|     switch (rule_list) { | ||||
|         inline .float, .ssd, .output, .fullscreen, .tearing => |list| { | ||||
|         inline .float, .ssd, .output, .fullscreen, .tearing, .warp => |list| { | ||||
|             const rules = switch (list) { | ||||
|                 .float => server.config.rules.float.rules.items, | ||||
|                 .ssd => server.config.rules.ssd.rules.items, | ||||
|                 .output => server.config.rules.output.rules.items, | ||||
|                 .fullscreen => server.config.rules.fullscreen.rules.items, | ||||
|                 .tearing => server.config.rules.tearing.rules.items, | ||||
|                 .warp => server.config.rules.warp.rules.items, | ||||
|                 else => unreachable, | ||||
|             }; | ||||
|             for (rules) |rule| { | ||||
| @ -284,6 +298,7 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error! | ||||
|                     .output => rule.value, | ||||
|                     .fullscreen => if (rule.value) "fullscreen" else "no-fullscreen", | ||||
|                     .tearing => if (rule.value) "tearing" else "no-tearing", | ||||
|                     .warp => if (rule.value) "warp" else "no-warp", | ||||
|                     else => unreachable, | ||||
|                 }}); | ||||
|             } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user