Compare commits
33
Commits
main
...
050b9976f1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
050b9976f1
|
||
|
|
451eca54be
|
||
|
|
f17b9d7e71
|
||
|
|
0a148369aa
|
||
|
|
96b54bde86
|
||
|
|
f6fd7655e2
|
||
|
|
242b62a9d4
|
||
|
|
82613a56db
|
||
|
|
936d1157b4
|
||
|
|
7ce21a44d8
|
||
|
|
52b312d08d
|
||
|
|
b3b79944cc | ||
|
|
e97a63f0fa
|
||
|
|
9cc6c1e09b
|
||
|
|
82f2f02c00
|
||
|
|
dc1d5c8418
|
||
|
|
0c095f0c93
|
||
|
|
ed9e9c4d16
|
||
|
|
930dcb7dcd
|
||
|
|
929db0098a
|
||
|
|
b5a80c7b9b | ||
|
|
bed50f0dd2 | ||
|
|
ad0b71c310 | ||
|
|
1e3ef88bd5 | ||
|
|
f66eec9248 | ||
|
|
16d76ae86c
|
||
|
|
e29d66f1de | ||
|
|
5262a4c5a6 | ||
|
|
bd5da261eb | ||
|
|
5fbf174c36 | ||
|
|
9bbd34a0e3 | ||
|
|
d1dc873408 | ||
|
|
6b86af4f85 |
@@ -0,0 +1,18 @@
|
||||
name: Close Pull Request
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: superbrothers/close-pull-request@v3
|
||||
with:
|
||||
comment: |
|
||||
Thanks for your interest in contributing to river!
|
||||
|
||||
Unfortunately, you are in the wrong place. As stated in the README, this github repo is a read-only mirror and development happens on codeberg: https://codeberg.org/river/river.
|
||||
|
||||
Please open a pull request on codeberg instead.
|
||||
+16
-8
@@ -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 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 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 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
|
||||
;;
|
||||
|
||||
+13
-5
@@ -298,9 +298,13 @@ matches everything while _\*\*_ and the empty string are invalid.
|
||||
with make: _HP Inc._, model: _HP 22w_, and serial: _CNC93720WF_, the
|
||||
identifier would be: _HP Inc. HP 22w CNC93720WF_. If the make, model, or
|
||||
serial is unknown, the word "Unknown" is used instead.
|
||||
- *position*: Set the initial position of the view, clamping to the
|
||||
bounds of the output. Requires x and y coordinates of the view as
|
||||
arguments, both of which must be non-negative. Applies only to new views.
|
||||
- *position*: Set the initial position of the view, clamping to the bounds
|
||||
of the output. Requires x and y coordinates of the view as arguments, both
|
||||
of which must be non-negative. Applies only to new views.
|
||||
- *relative-position*: Set the position of the view relative to
|
||||
something. Requires the anchor and the x and y coordinates of the
|
||||
view. The coordinates are either positive or negative numbers that are
|
||||
relative to the anchor. Applies only to new views.
|
||||
- *dimensions*: Set the initial dimensions of the view, clamping to the
|
||||
constraints of the view. Requires width and height of the view as
|
||||
arguments, both of which must be non-negative. Applies only to new views.
|
||||
@@ -311,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
|
||||
@@ -344,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
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zriver_seat_status_v1" version="3">
|
||||
<interface name="zriver_seat_status_v1" version="4">
|
||||
<description summary="track seat focus">
|
||||
This interface allows clients to receive information about the current
|
||||
focus of a seat. Note that (un)focused_output events will only be sent
|
||||
@@ -128,13 +128,14 @@
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</event>
|
||||
|
||||
<event name="focused_view">
|
||||
<event name="focused_view" since="4">
|
||||
<description summary="information on the focused view">
|
||||
Sent once on binding the interface and again whenever the focused
|
||||
view or a property thereof changes. The title may be an empty string
|
||||
if no view is focused or the focused view did not set a title.
|
||||
</description>
|
||||
<arg name="title" type="string" summary="title of the focused view"/>
|
||||
<arg name="tags" type="uint" summary="32-bit bitfield"/>
|
||||
</event>
|
||||
|
||||
<event name="mode" since="3">
|
||||
|
||||
+10
-2
@@ -58,9 +58,15 @@ pub const HideCursorWhenTypingMode = enum {
|
||||
enabled,
|
||||
};
|
||||
|
||||
pub const Anchor = enum {
|
||||
absolute,
|
||||
mouse,
|
||||
};
|
||||
|
||||
pub const Position = struct {
|
||||
x: u31,
|
||||
y: u31,
|
||||
anchor: Anchor,
|
||||
x: i31,
|
||||
y: i31,
|
||||
};
|
||||
|
||||
pub const Dimensions = struct {
|
||||
@@ -102,6 +108,7 @@ rules: struct {
|
||||
dimensions: RuleList(Dimensions) = .{},
|
||||
fullscreen: RuleList(bool) = .{},
|
||||
tearing: RuleList(bool) = .{},
|
||||
warp: RuleList(bool) = .{},
|
||||
} = .{},
|
||||
|
||||
/// The selected focus_follows_cursor mode
|
||||
@@ -186,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);
|
||||
|
||||
|
||||
+9
-1
@@ -1236,10 +1236,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) {
|
||||
|
||||
+17
-1
@@ -707,7 +707,10 @@ fn commitTransaction(root: *Root) void {
|
||||
|
||||
{
|
||||
var it = server.input_manager.seats.iterator(.forward);
|
||||
while (it.next()) |seat| seat.cursor.updateState();
|
||||
while (it.next()) |seat| {
|
||||
seat.cursor.updateState();
|
||||
seat.sendFocusedView();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
@@ -803,6 +806,19 @@ fn processOutputConfig(
|
||||
var proposed_state = wlr.Output.State.init();
|
||||
head.state.apply(&proposed_state);
|
||||
|
||||
// Negative output coordinates currently cause Xwayland clients to not receive click events.
|
||||
// See: https://gitlab.freedesktop.org/xorg/xserver/-/issues/899
|
||||
if (build_options.xwayland and server.xwayland != null and
|
||||
(head.state.x < 0 or head.state.y < 0))
|
||||
{
|
||||
std.log.scoped(.output_manager).err(
|
||||
\\Attempted to set negative coordinates for output {s}.
|
||||
\\Negative output coordinates are disallowed if Xwayland is enabled due to a limitation of Xwayland.
|
||||
, .{output.wlr_output.name});
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
.test_only => {
|
||||
if (!wlr_output.testState(&proposed_state)) success = false;
|
||||
|
||||
@@ -242,6 +242,11 @@ pub fn focus(seat: *Seat, _target: ?*View) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sendFocusedView(seat: *Seat) void {
|
||||
var it = seat.status_trackers.iterator(.forward);
|
||||
while (it.next()) |tracker| tracker.sendFocusedView();
|
||||
}
|
||||
|
||||
/// Switch focus to the target, handling unfocus and input inhibition
|
||||
/// properly. This should only be called directly if dealing with layers or
|
||||
/// override redirect xwayland views.
|
||||
|
||||
+15
-5
@@ -17,6 +17,8 @@
|
||||
const SeatStatus = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const wayland = @import("wayland");
|
||||
const wl = wayland.server.wl;
|
||||
const zriver = wayland.server.zriver;
|
||||
@@ -76,11 +78,19 @@ pub fn sendOutput(seat_status: SeatStatus, output: *Output, state: enum { focuse
|
||||
}
|
||||
|
||||
pub fn sendFocusedView(seat_status: SeatStatus) void {
|
||||
const title: [*:0]const u8 = if (seat_status.seat.focused == .view)
|
||||
seat_status.seat.focused.view.getTitle() orelse ""
|
||||
else
|
||||
"";
|
||||
seat_status.seat_status_v1.sendFocusedView(title);
|
||||
if (seat_status.seat_status_v1.getVersion() >= 4) {
|
||||
switch (seat_status.seat.focused) {
|
||||
.view => |view| {
|
||||
if (view.current.tags != 0) {
|
||||
// A view can't be on no tags, so we need to wait for the
|
||||
// layout to update. There is probably a better way to do
|
||||
// this, but this way seems to work.
|
||||
seat_status.seat_status_v1.sendFocusedView(view.getTitle() orelse "", view.current.tags);
|
||||
}
|
||||
},
|
||||
else => seat_status.seat_status_v1.sendFocusedView("", 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sendMode(seat_status: SeatStatus, mode: [*:0]const u8) void {
|
||||
|
||||
+15
-6
@@ -703,8 +703,18 @@ pub fn map(view: *View) !void {
|
||||
server.input_manager.defaultSeat().focused_output;
|
||||
|
||||
if (server.config.rules.position.match(view)) |position| {
|
||||
view.pending.box.x = position.x;
|
||||
view.pending.box.y = position.y;
|
||||
var base_x: i31 = 0;
|
||||
var base_y: i31 = 0;
|
||||
switch (position.anchor) {
|
||||
.absolute => {},
|
||||
.mouse => {
|
||||
const cursor = server.input_manager.defaultSeat().wlr_seat.pointer_state;
|
||||
base_x = @intCast(@as(i31, @intFromFloat(cursor.sx)));
|
||||
base_y = @intCast(@as(i31, @intFromFloat(cursor.sy)));
|
||||
},
|
||||
}
|
||||
view.pending.box.x = base_x + position.x;
|
||||
view.pending.box.y = base_y + position.y;
|
||||
} else if (output) |o| {
|
||||
// Center the initial pending box on the output
|
||||
view.pending.box.x = @divTrunc(@max(0, o.usable_box.width - view.pending.box.width), 2);
|
||||
@@ -776,19 +786,18 @@ pub fn unmap(view: *View) void {
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
pub fn notifyTitle(view: *const View) void {
|
||||
pub fn notifyState(view: *const View) void {
|
||||
if (view.foreign_toplevel_handle.wlr_handle) |wlr_handle| {
|
||||
if (view.getTitle()) |title| wlr_handle.setTitle(title);
|
||||
}
|
||||
|
||||
// Send title to all status listeners attached to a seat which focuses this view
|
||||
if (view.ext_foreign_toplevel_handle) |handle| {
|
||||
handle.updateState(&.{
|
||||
.title = view.getTitle(),
|
||||
.app_id = view.getAppId(),
|
||||
});
|
||||
}
|
||||
|
||||
// Send title to all status listeners attached to a seat which focuses this view
|
||||
// Send title and tags to all status listeners attached to a seat which focuses this view
|
||||
var seat_it = server.input_manager.seats.iterator(.forward);
|
||||
while (seat_it.next()) |seat| {
|
||||
if (seat.focused == .view and seat.focused.view == view) {
|
||||
|
||||
@@ -479,7 +479,7 @@ fn handleRequestResize(listener: *wl.Listener(*wlr.XdgToplevel.event.Resize), ev
|
||||
/// Called when the client sets / updates its title
|
||||
fn handleSetTitle(listener: *wl.Listener(void)) void {
|
||||
const toplevel: *XdgToplevel = @fieldParentPtr("set_title", listener);
|
||||
toplevel.view.notifyTitle();
|
||||
toplevel.view.notifyState();
|
||||
}
|
||||
|
||||
/// Called when the client sets / updates its app_id
|
||||
|
||||
@@ -284,7 +284,7 @@ fn handleSetOverrideRedirect(listener: *wl.Listener(void)) void {
|
||||
|
||||
fn handleSetTitle(listener: *wl.Listener(void)) void {
|
||||
const xwayland_view: *XwaylandView = @fieldParentPtr("set_title", listener);
|
||||
xwayland_view.view.notifyTitle();
|
||||
xwayland_view.view.notifyState();
|
||||
}
|
||||
|
||||
fn handleSetClass(listener: *wl.Listener(void)) void {
|
||||
|
||||
+44
-8
@@ -27,6 +27,7 @@ const util = @import("../util.zig");
|
||||
const Error = @import("../command.zig").Error;
|
||||
const Seat = @import("../Seat.zig");
|
||||
const View = @import("../View.zig");
|
||||
const Anchor = @import("../Config.zig").Anchor;
|
||||
const RuleGlobs = @import("../rule_list.zig").RuleGlobs;
|
||||
|
||||
const Action = enum {
|
||||
@@ -37,11 +38,14 @@ const Action = enum {
|
||||
tags,
|
||||
output,
|
||||
position,
|
||||
@"relative-position",
|
||||
dimensions,
|
||||
fullscreen,
|
||||
@"no-fullscreen",
|
||||
tearing,
|
||||
@"no-tearing",
|
||||
warp,
|
||||
@"no-warp",
|
||||
};
|
||||
|
||||
pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void {
|
||||
@@ -57,9 +61,10 @@ 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,
|
||||
};
|
||||
if (result.args.len > positional_arguments_count) return Error.TooManyArguments;
|
||||
if (result.args.len < positional_arguments_count) return Error.NotEnoughArguments;
|
||||
@@ -113,14 +118,32 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
|
||||
});
|
||||
},
|
||||
.position => {
|
||||
const x = try fmt.parseInt(u31, result.args[1], 10);
|
||||
const y = try fmt.parseInt(u31, result.args[2], 10);
|
||||
const x = try fmt.parseInt(i31, result.args[1], 10);
|
||||
const y = try fmt.parseInt(i31, result.args[2], 10);
|
||||
if (x < 0 or y < 0) return Error.OutOfBounds;
|
||||
try server.config.rules.position.add(.{
|
||||
.app_id_glob = app_id_glob,
|
||||
.title_glob = title_glob,
|
||||
.value = .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
.anchor = .absolute,
|
||||
.x = @intCast(x),
|
||||
.y = @intCast(y),
|
||||
},
|
||||
});
|
||||
},
|
||||
.@"relative-position" => {
|
||||
const anchor = std.meta.stringToEnum(Anchor, result.args[1]) orelse return Error.UnknownOption;
|
||||
// force the use of the normal position command for absolute positions
|
||||
if (anchor == .absolute) return Error.UnknownOption;
|
||||
const x_off = try fmt.parseInt(i31, result.args[2], 10);
|
||||
const y_off = try fmt.parseInt(i31, result.args[3], 10);
|
||||
try server.config.rules.position.add(.{
|
||||
.app_id_glob = app_id_glob,
|
||||
.title_glob = title_glob,
|
||||
.value = .{
|
||||
.anchor = anchor,
|
||||
.x = x_off,
|
||||
.y = y_off,
|
||||
},
|
||||
});
|
||||
},
|
||||
@@ -143,6 +166,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),
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +210,7 @@ pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
|
||||
util.gpa.free(output_rule);
|
||||
}
|
||||
},
|
||||
.position => {
|
||||
.position, .@"relative-position" => {
|
||||
_ = server.config.rules.position.del(rule);
|
||||
},
|
||||
.dimensions => {
|
||||
@@ -193,6 +223,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);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,6 +270,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(),
|
||||
@@ -253,13 +287,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| {
|
||||
@@ -271,6 +306,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,
|
||||
}});
|
||||
}
|
||||
@@ -286,7 +322,7 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!
|
||||
for (server.config.rules.position.rules.items) |rule| {
|
||||
try alignLeft(rule.title_glob, title_column_max, writer);
|
||||
try alignLeft(rule.app_id_glob, app_id_column_max, writer);
|
||||
try writer.print("{d},{d}\n", .{ rule.value.x, rule.value.y });
|
||||
try writer.print("{s},{d},{d}\n", .{ @tagName(rule.value.anchor), rule.value.x, rule.value.y });
|
||||
}
|
||||
},
|
||||
.dimensions => {
|
||||
|
||||
Reference in New Issue
Block a user