riverctl: add -focused-output for option commands

This is more convenient for interactive usage and makes using the same
bindings across multiple outputs easy.
This commit is contained in:
Isaac Freund 2021-02-07 19:11:54 +01:00
parent 96d460c477
commit a8a70a3b04
No known key found for this signature in database
GPG key ID: 86DED400DDFD7A11
4 changed files with 75 additions and 13 deletions

View file

@ -284,19 +284,20 @@ A complete list may be found in _/usr/include/linux/input-event-codes.h_
River has various options that are saved in a typed key-value store. It also
allows users to store arbitrary custom options in the store. Options are
scoped either globally or per-output if the -output flag is passed with the
name of the output as obtained from the xdg-output protocol.
scoped either globally or per-output if the *-output* flag is passed with the
name of the output as obtained from the xdg-output protocol. Alternatively,
the currently focused output may be targeted with the *-focused-output* flag.
*declare-option* [-output _output_name_] _name_ _type_ _value_
*declare-option* [*-output* _output_name_|*-focused-output*] _name_ _type_ _value_
Declare a new option with the given _type_ and inital _value_. If
the option already exists with the given _type_, it is still set
to _value_. If the option already exists with a different type,
nothing happens.
*get-option* [-output _output_name_] _name_
*get-option* [*-output* _output_name_|*-focused-output*] _name_
Print the current value of the given option to stdout.
*set-option* [-output _output_name_] _name_ _value_
*set-option* [*-output* _output_name_|*-focused-output*] _name_ _value_
Set the value of the specified option to _value_.
River declares certain default options for all outputs.

View file

@ -60,6 +60,7 @@ pub fn Args(comptime num_positionals: comptime_int, comptime flag_defs: []const
var arg_idx: usize = 0;
var positional_idx: usize = 0;
outer: while (arg_idx < argv.len) : (arg_idx += 1) {
var should_continue = false;
inline for (flag_defs) |flag_def, flag_idx| {
if (cstr.cmp(flag_def.name, argv[arg_idx]) == 0) {
switch (flag_def.kind) {
@ -73,9 +74,12 @@ pub fn Args(comptime num_positionals: comptime_int, comptime flag_defs: []const
"' requires an argument but none was provided!", .{});
},
}
continue :outer;
// TODO: this variable exists as a workaround for the fact that
// using continue :outer here crashes the stage1 compiler.
should_continue = true;
}
}
if (should_continue) continue;
if (positional_idx == num_positionals) {
root.printErrorExit(

View file

@ -18,6 +18,7 @@
const std = @import("std");
const mem = std.mem;
const os = std.os;
const assert = std.debug.assert;
const wayland = @import("wayland");
const wl = wayland.client.wl;
@ -36,6 +37,7 @@ pub const Output = struct {
pub const Globals = struct {
control: ?*zriver.ControlV1 = null,
options_manager: ?*zriver.OptionsManagerV1 = null,
status_manager: ?*zriver.StatusManagerV1 = null,
seat: ?*wl.Seat = null,
output_manager: ?*zxdg.OutputManagerV1 = null,
outputs: std.ArrayList(Output) = std.ArrayList(Output).init(gpa),
@ -77,12 +79,15 @@ pub fn main() !void {
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, globals: *Globals) void {
switch (event) {
.global => |global| {
if (globals.seat == null and std.cstr.cmp(global.interface, wl.Seat.getInterface().name) == 0) {
if (std.cstr.cmp(global.interface, wl.Seat.getInterface().name) == 0) {
assert(globals.seat == null); // TODO: support multiple seats
globals.seat = registry.bind(global.name, wl.Seat, 1) catch @panic("out of memory");
} else if (std.cstr.cmp(global.interface, zriver.ControlV1.getInterface().name) == 0) {
globals.control = registry.bind(global.name, zriver.ControlV1, 1) catch @panic("out of memory");
} else if (std.cstr.cmp(global.interface, zriver.OptionsManagerV1.getInterface().name) == 0) {
globals.options_manager = registry.bind(global.name, zriver.OptionsManagerV1, 1) catch @panic("out of memory");
} else if (std.cstr.cmp(global.interface, zriver.StatusManagerV1.getInterface().name) == 0) {
globals.status_manager = registry.bind(global.name, zriver.StatusManagerV1, 1) catch @panic("out of memory");
} else if (std.cstr.cmp(global.interface, zxdg.OutputManagerV1.getInterface().name) == 0 and global.version >= 2) {
globals.output_manager = registry.bind(global.name, zxdg.OutputManagerV1, 2) catch @panic("out of memory");
} else if (std.cstr.cmp(global.interface, wl.Output.getInterface().name) == 0) {

View file

@ -49,12 +49,22 @@ const Context = struct {
pub fn declareOption(display: *wl.Display, globals: *Globals) !void {
// https://github.com/ziglang/zig/issues/7807
const argv: [][*:0]const u8 = os.argv;
const args = Args(3, &[_]FlagDef{.{ .name = "-output", .kind = .arg }}).parse(argv[2..]);
const args = Args(3, &[_]FlagDef{
.{ .name = "-output", .kind = .arg },
.{ .name = "-focused-output", .kind = .boolean },
}).parse(argv[2..]);
const key = args.positionals[0];
const value_type = std.meta.stringToEnum(ValueType, mem.span(args.positionals[1])) orelse
root.printErrorExit("'{}' is not a valid type, must be int, uint, fixed, or string", .{args.positionals[1]});
const raw_value = args.positionals[2];
const output = if (args.argFlag("-output")) |o| try parseOutputName(display, globals, o) else null;
const output = if (args.argFlag("-output")) |o|
try parseOutputName(display, globals, o)
else if (args.boolFlag("-focused-output"))
try getFocusedOutput(display, globals)
else
null;
const options_manager = globals.options_manager orelse return error.RiverOptionsManagerNotAdvertised;
const handle = try options_manager.getOptionHandle(key, if (output) |o| o.wl_output else null);
@ -86,12 +96,23 @@ fn setFixedValueRaw(handle: *zriver.OptionHandleV1, raw_value: [*:0]const u8) vo
pub fn getOption(display: *wl.Display, globals: *Globals) !void {
// https://github.com/ziglang/zig/issues/7807
const argv: [][*:0]const u8 = os.argv;
const args = Args(1, &[_]FlagDef{.{ .name = "-output", .kind = .arg }}).parse(argv[2..]);
const args = Args(1, &[_]FlagDef{
.{ .name = "-output", .kind = .arg },
.{ .name = "-focused-output", .kind = .boolean },
}).parse(argv[2..]);
const output = if (args.argFlag("-output")) |o|
try parseOutputName(display, globals, o)
else if (args.boolFlag("-focused-output"))
try getFocusedOutput(display, globals)
else
null;
const ctx = Context{
.display = display,
.key = args.positionals[0],
.raw_value = undefined,
.output = if (args.argFlag("-output")) |o| try parseOutputName(display, globals, o) else null,
.output = output,
};
const options_manager = globals.options_manager orelse return error.RiverOptionsManagerNotAdvertised;
@ -105,12 +126,23 @@ pub fn getOption(display: *wl.Display, globals: *Globals) !void {
pub fn setOption(display: *wl.Display, globals: *Globals) !void {
// https://github.com/ziglang/zig/issues/7807
const argv: [][*:0]const u8 = os.argv;
const args = Args(2, &[_]FlagDef{.{ .name = "-output", .kind = .arg }}).parse(argv[2..]);
const args = Args(2, &[_]FlagDef{
.{ .name = "-output", .kind = .arg },
.{ .name = "-focused-output", .kind = .boolean },
}).parse(argv[2..]);
const output = if (args.argFlag("-output")) |o|
try parseOutputName(display, globals, o)
else if (args.boolFlag("-focused-output"))
try getFocusedOutput(display, globals)
else
null;
const ctx = Context{
.display = display,
.key = args.positionals[0],
.raw_value = args.positionals[1],
.output = if (args.argFlag("-output")) |o| try parseOutputName(display, globals, o) else null,
.output = output,
};
const options_manager = globals.options_manager orelse return error.RiverOptionsManagerNotAdvertised;
@ -142,6 +174,26 @@ fn xdgOutputListener(xdg_output: *zxdg.OutputV1, event: zxdg.OutputV1.Event, out
}
}
fn getFocusedOutput(display: *wl.Display, globals: *Globals) !*Output {
const status_manager = globals.status_manager orelse return error.RiverStatusManagerNotAdvertised;
const seat = globals.seat orelse return error.SeatNotAdverstised;
const seat_status = try status_manager.getRiverSeatStatus(seat);
var result: ?*wl.Output = null;
seat_status.setListener(*?*wl.Output, seatStatusListener, &result) catch unreachable;
_ = try display.roundtrip();
const wl_output = if (result) |output| output else return error.NoOutputFocused;
for (globals.outputs.items) |*output| {
if (output.wl_output == wl_output) return output;
} else unreachable;
}
fn seatStatusListener(seat_status: *zriver.SeatStatusV1, event: zriver.SeatStatusV1.Event, result: *?*wl.Output) void {
switch (event) {
.focused_output => |ev| result.* = ev.output,
.unfocused_output, .focused_view => {},
}
}
fn getOptionListener(
handle: *zriver.OptionHandleV1,
event: zriver.OptionHandleV1.Event,