Add cursor warp option

This commit is contained in:
Alexander Rosenberg 2024-11-02 08:00:50 -07:00
parent d66decb7c4
commit 5080f07724
Signed by: Zander671
GPG Key ID: 5FD0394ADBD72730
8 changed files with 55 additions and 18 deletions

View File

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

View File

@ -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=" \

View File

@ -91,10 +91,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"

View File

@ -207,9 +207,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
;;

View File

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

View File

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

View File

@ -1178,10 +1178,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) {

View File

@ -42,6 +42,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 {
@ -57,7 +59,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,
@ -162,6 +164,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),
});
},
}
}
@ -212,6 +221,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);
},
}
}
@ -250,6 +262,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(),
@ -265,13 +278,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| {
@ -283,6 +297,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,
}});
}