aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/riverctl.1.scd11
-rw-r--r--riverctl/args.zig6
-rw-r--r--riverctl/main.zig7
-rw-r--r--riverctl/options.zig64
4 files changed, 75 insertions, 13 deletions
diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd
index 3424ea4..cbb3896 100644
--- a/doc/riverctl.1.scd
+++ b/doc/riverctl.1.scd
@@ -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.
diff --git a/riverctl/args.zig b/riverctl/args.zig
index 3240a97..29ca0f9 100644
--- a/riverctl/args.zig
+++ b/riverctl/args.zig
@@ -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(
diff --git a/riverctl/main.zig b/riverctl/main.zig
index 4c3f1c8..91e24c4 100644
--- a/riverctl/main.zig
+++ b/riverctl/main.zig
@@ -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) {
diff --git a/riverctl/options.zig b/riverctl/options.zig
index 41e3223..18df45b 100644
--- a/riverctl/options.zig
+++ b/riverctl/options.zig
@@ -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,