river: add outputs rule
This commit is contained in:
		| @ -1,6 +1,6 @@ | ||||
| function __riverctl_completion () | ||||
| { | ||||
| 	local rule_actions="float no-float ssd csd tag" | ||||
| 	local rule_actions="float no-float ssd csd tag output" | ||||
| 	if [ "${COMP_CWORD}" -eq 1 ] | ||||
| 	then | ||||
| 		OPTS=" \ | ||||
| @ -65,7 +65,7 @@ function __riverctl_completion () | ||||
| 			"move"|"snap") OPTS="up down left right" ;; | ||||
| 			"resize") OPTS="horizontal vertical" ;; | ||||
| 			"rule-add"|"rule-del") OPTS="-app-id -title $rule_actions" ;; | ||||
| 			"list-rules") OPTS="float ssd tag" ;; | ||||
| 			"list-rules") OPTS="float ssd tag output" ;; | ||||
| 			"map") OPTS="-release -repeat -layout" ;; | ||||
| 			"unmap") OPTS="-release" ;; | ||||
| 			"attach-mode") OPTS="top bottom" ;; | ||||
|  | ||||
| @ -83,9 +83,9 @@ complete -c riverctl -x -n '__fish_seen_subcommand_from unmap'                -a | ||||
| complete -c riverctl -x -n '__fish_seen_subcommand_from attach-mode'          -a 'top bottom' | ||||
| complete -c riverctl -x -n '__fish_seen_subcommand_from focus-follows-cursor' -a 'disabled normal always' | ||||
| complete -c riverctl -x -n '__fish_seen_subcommand_from set-cursor-warp'      -a 'disabled on-output-change on-focus-change' | ||||
| complete -c riverctl -x -n '__fish_seen_subcommand_from rule-add'             -a 'float no-float ssd csd tag' | ||||
| complete -c riverctl -x -n '__fish_seen_subcommand_from rule-del'             -a 'float no-float ssd csd tag' | ||||
| complete -c riverctl -x -n '__fish_seen_subcommand_from list-rules'           -a 'float ssd tag' | ||||
| complete -c riverctl -x -n '__fish_seen_subcommand_from rule-add'             -a 'float no-float ssd csd tag output' | ||||
| complete -c riverctl -x -n '__fish_seen_subcommand_from rule-del'             -a 'float no-float ssd csd tag output' | ||||
| complete -c riverctl -x -n '__fish_seen_subcommand_from list-rules'           -a 'float ssd tag output' | ||||
|  | ||||
| # Subcommands for 'input' | ||||
| complete -c riverctl -x -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 2' -a "(__riverctl_list_input_devices)" | ||||
|  | ||||
| @ -183,9 +183,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 tag)' | ||||
|                     _arguments '1: :(-app-id -title)' '2: : ' ':: :(float no-float ssd csd tag output)' | ||||
|                 ;; | ||||
|                 list-rules) _alternative 'arguments:args:(float ssd tag)' ;; | ||||
|                 list-rules) _alternative 'arguments:args:(float ssd tag output)' ;; | ||||
|                 *) return 0 ;; | ||||
|             esac | ||||
|         ;; | ||||
|  | ||||
							
								
								
									
										2
									
								
								deps/zig-wlroots
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								deps/zig-wlroots
									
									
									
									
										vendored
									
									
								
							 Submodule deps/zig-wlroots updated: 021fb4bbae...0f07b2c666
									
								
							| @ -276,6 +276,12 @@ matches everything while _\*\*_ and the empty string are invalid. | ||||
| 	  and existing views. | ||||
| 	- *tag*: Set the initial tags of the view. Requires the tags as | ||||
| 	  an argument. Applies only to new views. | ||||
| 	- *output*: Set the initial output of the view. Requires the output | ||||
| 	  as an argument. Applies only to new views. The output can be specified | ||||
| 	  either by connector name (such as _HDMI-A-1_, or _DP-2_), or by | ||||
| 	  identifier in the form of _MAKE MODEL SERIAL_, for example for an output | ||||
| 	  with make: _HP Inc._, model: _HP 22w_, and serial: _CNC93720WF_, the | ||||
| 	  identifier would be: _HP Inc. HP 22w CNC93720WF_. | ||||
|  | ||||
| 	Both *float* and *no-float* rules are added to the same list, | ||||
| 	which means that adding a *no-float* rule with the same arguments | ||||
|  | ||||
| @ -17,15 +17,19 @@ | ||||
| const Self = @This(); | ||||
|  | ||||
| const std = @import("std"); | ||||
| const fmt = std.fmt; | ||||
| const mem = std.mem; | ||||
| const globber = @import("globber"); | ||||
| const xkb = @import("xkbcommon"); | ||||
|  | ||||
| const server = &@import("main.zig").server; | ||||
| const util = @import("util.zig"); | ||||
|  | ||||
| const Server = @import("Server.zig"); | ||||
| const Output = @import("Output.zig"); | ||||
| const Mode = @import("Mode.zig"); | ||||
| const RuleList = @import("rule_list.zig").RuleList; | ||||
| const View = @import("View.zig"); | ||||
|  | ||||
| pub const AttachMode = enum { | ||||
|     top, | ||||
| @ -76,6 +80,7 @@ modes: std.ArrayListUnmanaged(Mode), | ||||
| float_rules: RuleList(bool) = .{}, | ||||
| ssd_rules: RuleList(bool) = .{}, | ||||
| tag_rules: RuleList(u32) = .{}, | ||||
| output_rules: RuleList([]const u8) = .{}, | ||||
|  | ||||
| /// The selected focus_follows_cursor mode | ||||
| focus_follows_cursor: FocusFollowsCursorMode = .disabled, | ||||
| @ -152,9 +157,33 @@ pub fn deinit(self: *Self) void { | ||||
|     self.float_rules.deinit(); | ||||
|     self.ssd_rules.deinit(); | ||||
|     self.tag_rules.deinit(); | ||||
|     for (self.output_rules.rules.items) |rule| { | ||||
|         util.gpa.free(rule.value); | ||||
|     } | ||||
|     self.output_rules.deinit(); | ||||
|  | ||||
|     util.gpa.free(self.default_layout_namespace); | ||||
|  | ||||
|     self.keymap.unref(); | ||||
|     self.xkb_context.unref(); | ||||
| } | ||||
|  | ||||
| pub fn outputRuleMatch(self: *Self, view: *View) !?*Output { | ||||
|     const output_name = self.output_rules.match(view) orelse return null; | ||||
|     var it = server.root.active_outputs.iterator(.forward); | ||||
|     while (it.next()) |output| { | ||||
|         const wlr_output = output.wlr_output; | ||||
|         if (mem.eql(u8, output_name, mem.span(wlr_output.name))) return output; | ||||
|  | ||||
|         // This allows matching with "Maker Model Serial" instead of "Connector" | ||||
|         const maker = wlr_output.make orelse "Unknown"; | ||||
|         const model = wlr_output.model orelse "Unknown"; | ||||
|         const serial = wlr_output.serial orelse "Unknown"; | ||||
|         const identifier = try fmt.allocPrint(util.gpa, "{s} {s} {s}", .{ maker, model, serial }); | ||||
|         defer util.gpa.free(identifier); | ||||
|  | ||||
|         if (mem.eql(u8, output_name, identifier)) return output; | ||||
|     } | ||||
|  | ||||
|     return null; | ||||
| } | ||||
|  | ||||
| @ -492,7 +492,8 @@ pub fn map(view: *Self) !void { | ||||
|         view.pending.ssd = ssd; | ||||
|     } | ||||
|  | ||||
|     if (server.input_manager.defaultSeat().focused_output) |output| { | ||||
|     const focused_output = server.input_manager.defaultSeat().focused_output; | ||||
|     if (try server.config.outputRuleMatch(view) orelse focused_output) |output| { | ||||
|         // Center the initial pending box on the output | ||||
|         view.pending.box.x = @divTrunc(@max(0, output.usable_box.width - view.pending.box.width), 2); | ||||
|         view.pending.box.y = @divTrunc(@max(0, output.usable_box.height - view.pending.box.height), 2); | ||||
|  | ||||
| @ -33,6 +33,7 @@ const Action = enum { | ||||
|     ssd, | ||||
|     csd, | ||||
|     tag, | ||||
|     output, | ||||
| }; | ||||
|  | ||||
| pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void { | ||||
| @ -49,7 +50,7 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void | ||||
|  | ||||
|     const positional_arguments_count: u8 = switch (action) { | ||||
|         .float, .@"no-float", .ssd, .csd => 1, | ||||
|         .tag => 2, | ||||
|         .tag, .output => 2, | ||||
|     }; | ||||
|     if (result.args.len > positional_arguments_count) return Error.TooManyArguments; | ||||
|     if (result.args.len < positional_arguments_count) return Error.NotEnoughArguments; | ||||
| @ -85,6 +86,15 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void | ||||
|                 .value = tag, | ||||
|             }); | ||||
|         }, | ||||
|         .output => { | ||||
|             const output_name = try util.gpa.dupe(u8, result.args[1]); | ||||
|             errdefer util.gpa.free(output_name); | ||||
|             try server.config.output_rules.add(.{ | ||||
|                 .app_id_glob = app_id_glob, | ||||
|                 .title_glob = title_glob, | ||||
|                 .value = output_name, | ||||
|             }); | ||||
|         }, | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -100,29 +110,27 @@ pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void | ||||
|     if (result.args.len < 1) return Error.NotEnoughArguments; | ||||
|  | ||||
|     const action = std.meta.stringToEnum(Action, result.args[0]) orelse return Error.UnknownOption; | ||||
|     const app_id_glob = result.flags.@"app-id" orelse "*"; | ||||
|     const title_glob = result.flags.title orelse "*"; | ||||
|  | ||||
|     const rule = .{ | ||||
|         .app_id_glob = result.flags.@"app-id" orelse "*", | ||||
|         .title_glob = result.flags.title orelse "*", | ||||
|     }; | ||||
|     switch (action) { | ||||
|         .float, .@"no-float" => { | ||||
|             _ = server.config.float_rules.del(.{ | ||||
|                 .app_id_glob = app_id_glob, | ||||
|                 .title_glob = title_glob, | ||||
|             }); | ||||
|             _ = server.config.float_rules.del(rule); | ||||
|         }, | ||||
|         .ssd, .csd => { | ||||
|             _ = server.config.ssd_rules.del(.{ | ||||
|                 .app_id_glob = app_id_glob, | ||||
|                 .title_glob = title_glob, | ||||
|             }); | ||||
|             _ = server.config.ssd_rules.del(rule); | ||||
|             apply_ssd_rules(); | ||||
|             server.root.applyPending(); | ||||
|         }, | ||||
|         .tag => { | ||||
|             _ = server.config.tag_rules.del(.{ | ||||
|                 .app_id_glob = app_id_glob, | ||||
|                 .title_glob = title_glob, | ||||
|             }); | ||||
|             _ = server.config.tag_rules.del(rule); | ||||
|         }, | ||||
|         .output => { | ||||
|             if (server.config.output_rules.del(rule)) |output_rule| { | ||||
|                 util.gpa.free(output_rule); | ||||
|             } | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| @ -144,11 +152,13 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error! | ||||
|         float, | ||||
|         ssd, | ||||
|         tag, | ||||
|         output, | ||||
|     }, args[1]) orelse return Error.UnknownOption; | ||||
|     const max_glob_len = switch (list) { | ||||
|         .float => server.config.float_rules.getMaxGlobLen(), | ||||
|         .ssd => server.config.ssd_rules.getMaxGlobLen(), | ||||
|         .tag => server.config.tag_rules.getMaxGlobLen(), | ||||
|         .output => server.config.output_rules.getMaxGlobLen(), | ||||
|     }; | ||||
|     const app_id_column_max = 2 + @max("app-id".len, max_glob_len.app_id); | ||||
|     const title_column_max = 2 + @max("title".len, max_glob_len.title); | ||||
| @ -185,6 +195,14 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error! | ||||
|                 try writer.print("{b}\n", .{rule.value}); | ||||
|             } | ||||
|         }, | ||||
|         .output => { | ||||
|             const rules = server.config.output_rules.rules.items; | ||||
|             for (rules) |rule| { | ||||
|                 try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); | ||||
|                 try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); | ||||
|                 try writer.print("{s}\n", .{rule.value}); | ||||
|             } | ||||
|         }, | ||||
|     } | ||||
|  | ||||
|     out.* = try buffer.toOwnedSlice(); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user