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 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 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 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. 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 Declare a new option with the given _type_ and inital _value_. If
the option already exists with the given _type_, it is still set the option already exists with the given _type_, it is still set
to _value_. If the option already exists with a different type, to _value_. If the option already exists with a different type,
nothing happens. 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. 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_. Set the value of the specified option to _value_.
River declares certain default options for all outputs. 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 arg_idx: usize = 0;
var positional_idx: usize = 0; var positional_idx: usize = 0;
outer: while (arg_idx < argv.len) : (arg_idx += 1) { outer: while (arg_idx < argv.len) : (arg_idx += 1) {
var should_continue = false;
inline for (flag_defs) |flag_def, flag_idx| { inline for (flag_defs) |flag_def, flag_idx| {
if (cstr.cmp(flag_def.name, argv[arg_idx]) == 0) { if (cstr.cmp(flag_def.name, argv[arg_idx]) == 0) {
switch (flag_def.kind) { 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!", .{}); "' 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) { if (positional_idx == num_positionals) {
root.printErrorExit( root.printErrorExit(

View file

@ -18,6 +18,7 @@
const std = @import("std"); const std = @import("std");
const mem = std.mem; const mem = std.mem;
const os = std.os; const os = std.os;
const assert = std.debug.assert;
const wayland = @import("wayland"); const wayland = @import("wayland");
const wl = wayland.client.wl; const wl = wayland.client.wl;
@ -36,6 +37,7 @@ pub const Output = struct {
pub const Globals = struct { pub const Globals = struct {
control: ?*zriver.ControlV1 = null, control: ?*zriver.ControlV1 = null,
options_manager: ?*zriver.OptionsManagerV1 = null, options_manager: ?*zriver.OptionsManagerV1 = null,
status_manager: ?*zriver.StatusManagerV1 = null,
seat: ?*wl.Seat = null, seat: ?*wl.Seat = null,
output_manager: ?*zxdg.OutputManagerV1 = null, output_manager: ?*zxdg.OutputManagerV1 = null,
outputs: std.ArrayList(Output) = std.ArrayList(Output).init(gpa), 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 { fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, globals: *Globals) void {
switch (event) { switch (event) {
.global => |global| { .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"); 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) { } 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"); 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) { } 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"); 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) { } 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"); 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) { } 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 { pub fn declareOption(display: *wl.Display, globals: *Globals) !void {
// https://github.com/ziglang/zig/issues/7807 // https://github.com/ziglang/zig/issues/7807
const argv: [][*:0]const u8 = os.argv; 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 key = args.positionals[0];
const value_type = std.meta.stringToEnum(ValueType, mem.span(args.positionals[1])) orelse 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]}); root.printErrorExit("'{}' is not a valid type, must be int, uint, fixed, or string", .{args.positionals[1]});
const raw_value = args.positionals[2]; 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 options_manager = globals.options_manager orelse return error.RiverOptionsManagerNotAdvertised;
const handle = try options_manager.getOptionHandle(key, if (output) |o| o.wl_output else null); 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 { pub fn getOption(display: *wl.Display, globals: *Globals) !void {
// https://github.com/ziglang/zig/issues/7807 // https://github.com/ziglang/zig/issues/7807
const argv: [][*:0]const u8 = os.argv; 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{ const ctx = Context{
.display = display, .display = display,
.key = args.positionals[0], .key = args.positionals[0],
.raw_value = undefined, .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; 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 { pub fn setOption(display: *wl.Display, globals: *Globals) !void {
// https://github.com/ziglang/zig/issues/7807 // https://github.com/ziglang/zig/issues/7807
const argv: [][*:0]const u8 = os.argv; 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{ const ctx = Context{
.display = display, .display = display,
.key = args.positionals[0], .key = args.positionals[0],
.raw_value = args.positionals[1], .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; 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( fn getOptionListener(
handle: *zriver.OptionHandleV1, handle: *zriver.OptionHandleV1,
event: zriver.OptionHandleV1.Event, event: zriver.OptionHandleV1.Event,