From 7029a5cd3e90ed5c23e8e5039745f92caeca4a99 Mon Sep 17 00:00:00 2001 From: Leon Henrik Plickat Date: Mon, 1 Feb 2021 14:16:55 +0100 Subject: [PATCH] output: add output_title default option Outputs now have a default option, "output_title". If this changes, the outputs title is set to the option value. This title is currently only relevant when run nested in a wayland/X11 session. Co-authored-by: Isaac Freund --- deps/zig-wlroots | 2 +- doc/riverctl.1.scd | 8 +++++++- river/Option.zig | 7 +++++++ river/Output.zig | 36 ++++++++++++++++++++++++++++++++++++ river/Server.zig | 2 +- 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/deps/zig-wlroots b/deps/zig-wlroots index 4db6b92..5bc7fcf 160000 --- a/deps/zig-wlroots +++ b/deps/zig-wlroots @@ -1 +1 @@ -Subproject commit 4db6b92146f22054b9a9363d0999cf3fb359a4fb +Subproject commit 5bc7fcf94e1d488a8e2316ff7ebdd0ca193d0309 diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd index 05eafdf..3424ea4 100644 --- a/doc/riverctl.1.scd +++ b/doc/riverctl.1.scd @@ -280,7 +280,7 @@ A complete list may be found in _/usr/include/linux/input-event-codes.h_ and is made available through the _XCURSOR_THEME_ and _XCURSOR_SIZE_ environment variables. -# OPTIONS +## OPTIONS 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 @@ -299,6 +299,12 @@ name of the output as obtained from the xdg-output protocol. *set-option* [-output _output_name_] _name_ _value_ Set the value of the specified option to _value_. +River declares certain default options for all outputs. + +*output_title* (string) + Changing this option changes the title of the wayland and X11 backend + outputs. + # EXAMPLES Bind bemenu-run to Super+P in normal mode: diff --git a/river/Option.zig b/river/Option.zig index a6366bc..db9a731 100644 --- a/river/Option.zig +++ b/river/Option.zig @@ -45,6 +45,9 @@ output: ?*Output, key: [*:0]const u8, value: Value = .unset, +/// Emitted whenever the value of the option changes. +update: wl.Signal(*Self) = undefined, + handles: wl.list.Head(zriver.OptionHandleV1, null) = undefined, pub fn create(options_manager: *OptionsManager, output: ?*Output, key: [*:0]const u8) !*Self { @@ -57,6 +60,7 @@ pub fn create(options_manager: *OptionsManager, output: ?*Output, key: [*:0]cons .key = try util.gpa.dupeZ(u8, mem.span(key)), }; self.handles.init(); + self.update.init(); options_manager.options.append(self); @@ -101,6 +105,9 @@ pub fn set(self: *Self, value: Value) !void { var it = self.handles.iterator(.forward); while (it.next()) |handle| self.sendValue(handle); + + // Call listeners, if any. + self.update.emit(self); } fn sendValue(self: Self, handle: *zriver.OptionHandleV1) void { diff --git a/river/Output.zig b/river/Output.zig index 1b599ef..bbedcc6 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -18,6 +18,8 @@ const Self = @This(); const std = @import("std"); +const mem = std.mem; +const fmt = std.fmt; const wlr = @import("wlroots"); const wayland = @import("wayland"); const wl = wayland.server.wl; @@ -35,6 +37,7 @@ const View = @import("View.zig"); const ViewStack = @import("view_stack.zig").ViewStack; const AttachMode = @import("view_stack.zig").AttachMode; const OutputStatus = @import("OutputStatus.zig"); +const Option = @import("Option.zig"); const State = struct { /// A bit field of focused tags @@ -84,6 +87,9 @@ enable: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleEnable), frame: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleFrame), mode: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleMode), +// Listeners for options +output_title: wl.Listener(*Option) = wl.Listener(*Option).init(handleTitleChange), + pub fn init(self: *Self, root: *Root, wlr_output: *wlr.Output) !void { // Some backends don't have modes. DRM+KMS does, and we need to set a mode // before we can use the output. The mode is a tuple of (width, height, @@ -138,6 +144,10 @@ pub fn init(self: *Self, root: *Root, wlr_output: *wlr.Output) !void { .height = effective_resolution.height, }; } + + var buf: ["river - ".len + wlr_output.name.len + 1]u8 = undefined; + const default_title = fmt.bufPrintZ(&buf, "river - {}", .{mem.spanZ(&wlr_output.name)}) catch unreachable; + try self.defaultOption("output_title", .{ .string = default_title.ptr }, &self.output_title); } pub fn getLayer(self: *Self, layer: zwlr.LayerShellV1.Layer) *std.TailQueue(LayerSurface) { @@ -545,3 +555,29 @@ pub fn getEffectiveResolution(self: *Self) struct { width: u32, height: u32 } { .height = @intCast(u32, height), }; } + +pub fn setTitle(self: *Self, title: [*:0]const u8) void { + if (self.wlr_output.isWl()) { + self.wlr_output.wlSetTitle(title); + } else if (self.wlr_output.isX11()) { + self.wlr_output.x11SetTitle(title); + } +} + +/// Create an option for this output, attach a listener which is called when +/// the option changed and initialize with a default value. Note that the +/// listener is called once through this function. +fn defaultOption( + self: *Self, + key: [*:0]const u8, + value: Option.Value, + listener: *wl.Listener(*Option), +) !void { + const option = try Option.create(&self.root.server.options_manager, self, key); + option.update.add(listener); + try option.set(value); +} + +fn handleTitleChange(listener: *wl.Listener(*Option), option: *Option) void { + if (option.value.string) |title| option.output.?.setTitle(title); +} diff --git a/river/Server.zig b/river/Server.zig index 7923065..5ea699b 100644 --- a/river/Server.zig +++ b/river/Server.zig @@ -112,12 +112,12 @@ pub fn init(self: *Self) !void { self.config = try Config.init(); try self.decoration_manager.init(self); + try self.options_manager.init(self); try self.root.init(self); // Must be called after root is initialized try self.input_manager.init(self); try self.control.init(self); try self.status_manager.init(self); - try self.options_manager.init(self); // These all free themselves when the wl_server is destroyed _ = try wlr.DataDeviceManager.create(self.wl_server);