From aeeae92611683f1330d56dc8cbbda93a6868d3c6 Mon Sep 17 00:00:00 2001 From: Leon Henrik Plickat Date: Tue, 9 Feb 2021 22:53:17 +0100 Subject: [PATCH] riverctl: add mod-option command --- doc/riverctl.1.scd | 3 ++ riverctl/main.zig | 2 ++ riverctl/options.zig | 71 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd index cbb3896..0d96846 100644 --- a/doc/riverctl.1.scd +++ b/doc/riverctl.1.scd @@ -300,6 +300,9 @@ the currently focused output may be targeted with the *-focused-output* flag. *set-option* [*-output* _output_name_|*-focused-output*] _name_ _value_ Set the value of the specified option to _value_. +*mod-option* [*-output* _output_name_|*-focused-output*] _name_ _value_ + Add _value_ to the value of the specified option. _value_ can be negative. + River declares certain default options for all outputs. *output_title* (string) diff --git a/riverctl/main.zig b/riverctl/main.zig index 91e24c4..1f64233 100644 --- a/riverctl/main.zig +++ b/riverctl/main.zig @@ -58,6 +58,8 @@ pub fn main() !void { try options.getOption(display, &globals); } else if (os.argv.len > 2 and mem.eql(u8, "set-option", mem.span(os.argv[1]))) { try options.setOption(display, &globals); + } else if (os.argv.len > 2 and mem.eql(u8, "mod-option", mem.span(os.argv[1]))) { + try options.modOption(display, &globals); } else { const control = globals.control orelse return error.RiverControlNotAdvertised; const seat = globals.seat orelse return error.SeatNotAdverstised; diff --git a/riverctl/options.zig b/riverctl/options.zig index 18df45b..aab9fa0 100644 --- a/riverctl/options.zig +++ b/riverctl/options.zig @@ -93,6 +93,27 @@ fn setFixedValueRaw(handle: *zriver.OptionHandleV1, raw_value: [*:0]const u8) vo root.printErrorExit("{} is not a valid fixed", .{raw_value}))); } +fn modIntValueRaw(handle: *zriver.OptionHandleV1, current: i32, raw_value: [*:0]const u8) void { + const mod = fmt.parseInt(i32, mem.span(raw_value), 10) catch + root.printErrorExit("{} is not a valid int modificator", .{raw_value}); + handle.setIntValue(current + mod); +} + +fn modUintValueRaw(handle: *zriver.OptionHandleV1, current: u32, raw_value: [*:0]const u8) void { + // We need to allow negative mod values, but the value of the option may + // never be below zero. + const mod = fmt.parseInt(i32, mem.span(raw_value), 10) catch + root.printErrorExit("{} is not a valid uint modificator", .{raw_value}); + const new = @intCast(i32, current) + mod; + handle.setUintValue(if (new < 0) 0 else @intCast(u32, new)); +} + +fn modFixedValueRaw(handle: *zriver.OptionHandleV1, current: wl.Fixed, raw_value: [*:0]const u8) void { + const mod = fmt.parseFloat(f64, mem.span(raw_value)) catch + root.printErrorExit("{} is not a valid fixed modificator", .{raw_value}); + handle.setFixedValue(wl.Fixed.fromDouble(current.toDouble() + mod)); +} + pub fn getOption(display: *wl.Display, globals: *Globals) !void { // https://github.com/ziglang/zig/issues/7807 const argv: [][*:0]const u8 = os.argv; @@ -153,6 +174,36 @@ pub fn setOption(display: *wl.Display, globals: *Globals) !void { while (true) _ = try display.dispatch(); } +pub fn modOption(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 }, + .{ .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 = output, + }; + + const options_manager = globals.options_manager orelse return error.RiverOptionsManagerNotAdvertised; + const handle = try options_manager.getOptionHandle(ctx.key, if (ctx.output) |o| o.wl_output else null); + handle.setListener(*const Context, modOptionListener, &ctx) catch unreachable; + + // We always exit when our listener is called + while (true) _ = try display.dispatch(); +} + fn parseOutputName(display: *wl.Display, globals: *Globals, output_name: [*:0]const u8) !*Output { const output_manager = globals.output_manager orelse return error.XdgOutputNotAdvertised; for (globals.outputs.items) |*output| { @@ -237,3 +288,23 @@ fn setOptionListener( _ = ctx.display.flush() catch os.exit(1); os.exit(0); } + +fn modOptionListener( + handle: *zriver.OptionHandleV1, + event: zriver.OptionHandleV1.Event, + ctx: *const Context, +) void { + switch (event) { + .unset => if (ctx.output) |output| { + root.printErrorExit("option '{}' has not been declared on output '{}'", .{ ctx.key, output.name }); + } else { + root.printErrorExit("option '{}' has not been declared globally", .{ctx.key}); + }, + .int_value => |ev| modIntValueRaw(handle, ev.value, ctx.raw_value), + .uint_value => |ev| modUintValueRaw(handle, ev.value, ctx.raw_value), + .fixed_value => |ev| modFixedValueRaw(handle, ev.value, ctx.raw_value), + .string_value => root.printErrorExit("can not modify string options, use set-option to overwrite them", .{}), + } + _ = ctx.display.flush() catch os.exit(1); + os.exit(0); +}