aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.zig3
-rw-r--r--completions/bash/riverctl10
-rw-r--r--completions/fish/riverctl.fish11
-rw-r--r--completions/zsh/_riverctl9
-rw-r--r--doc/riverctl.1.scd32
-rw-r--r--doc/rivertile.1.scd23
-rwxr-xr-xexample/init20
-rw-r--r--protocol/river-options-v2.xml209
-rw-r--r--river/Option.zig184
-rw-r--r--river/OptionsManager.zig188
-rw-r--r--river/Output.zig8
-rw-r--r--river/OutputOption.zig145
-rw-r--r--river/Server.zig3
-rw-r--r--riverctl/args.zig119
-rw-r--r--riverctl/main.zig78
-rw-r--r--riverctl/options.zig320
-rw-r--r--rivertile/main.zig160
17 files changed, 69 insertions, 1453 deletions
diff --git a/build.zig b/build.zig
index dc09c77..04ab4e9 100644
--- a/build.zig
+++ b/build.zig
@@ -64,7 +64,6 @@ pub fn build(b: *zbs.Builder) !void {
scanner.addSystemProtocol("unstable/xdg-output/xdg-output-unstable-v1.xml");
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
scanner.addProtocolPath("protocol/river-control-unstable-v1.xml");
- scanner.addProtocolPath("protocol/river-options-v2.xml");
scanner.addProtocolPath("protocol/river-status-unstable-v1.xml");
scanner.addProtocolPath("protocol/river-layout-v1.xml");
scanner.addProtocolPath("protocol/wlr-layer-shell-unstable-v1.xml");
@@ -139,7 +138,7 @@ pub fn build(b: *zbs.Builder) !void {
}
if (examples) {
- inline for (.{ "status", "options" }) |example_name| {
+ inline for (.{"status"}) |example_name| {
const example = b.addExecutable(example_name, "example/" ++ example_name ++ ".zig");
example.setTarget(target);
example.setBuildMode(mode);
diff --git a/completions/bash/riverctl b/completions/bash/riverctl
index e747516..576ca75 100644
--- a/completions/bash/riverctl
+++ b/completions/bash/riverctl
@@ -17,6 +17,8 @@ function __riverctl_completion ()
toggle-float \
toggle-fullscreen \
zoom \
+ default-layout \
+ output-layout \
set-focused-tags \
set-view-tags \
toggle-focused-tags \
@@ -36,12 +38,7 @@ function __riverctl_completion ()
focus-follow-cursor \
opacity \
set-repeat \
- xcursor-theme \
- declare-option \
- get-option \
- set-option \
- unset-option \
- mod-option"
+ xcursor-theme
COMPREPLY=($(compgen -W "${OPTS}" -- "${COMP_WORDS[1]}"))
elif [ "${COMP_CWORD}" -eq 2 ]
then
@@ -52,7 +49,6 @@ function __riverctl_completion ()
"map"|"unmap") OPTS="-release" ;;
"attach-mode") OPTS="top bottom" ;;
"focus-follows-cursor") OPTS="disabled normal strict" ;;
- "get-option"|"set-option"|"unset-option"|"mod-option") OPTS="-output -focused-output" ;;
*) return ;;
esac
COMPREPLY=($(compgen -W "${OPTS}" -- "${COMP_WORDS[2]}"))
diff --git a/completions/fish/riverctl.fish b/completions/fish/riverctl.fish
index e893ce4..9b9d09b 100644
--- a/completions/fish/riverctl.fish
+++ b/completions/fish/riverctl.fish
@@ -1,6 +1,6 @@
function __fish_riverctl_complete_no_subcommand
for i in (commandline -opc)
- if contains -- $i close csd-filter-add exit float-filter-add focus-output focus-view move resize snap send-to-output spawn swap toggle-float toggle-fullscreen zoom set-focused-tags set-view-tags toggle-focused-tags toggle-view-tags spawn-tagmask declare-mode enter-mode map map-pointer unmap unmap-pointer attach-mode background-color border-color-focused border-color-unfocused border-width focus-follows-cursor opacity set-repeat xcursor-theme declare-option get-option set-option unset-option mod-option output_title
+ if contains -- $i close csd-filter-add exit float-filter-add focus-output focus-view move resize snap send-to-output spawn swap toggle-float toggle-fullscreen zoom default-layout output-layout set-focused-tags set-view-tags toggle-focused-tags toggle-view-tags spawn-tagmask declare-mode enter-mode map map-pointer unmap unmap-pointer attach-mode background-color border-color-focused border-color-unfocused border-width focus-follows-cursor opacity set-repeat xcursor-theme
return 1
end
end
@@ -23,6 +23,8 @@ complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a swap
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a toggle-float -d 'Toggle the floating state of the focused view'
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a toggle-fullscreen -d 'Toggle the fullscreen state of the focused view'
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a zoom -d 'Bump the focused view to the top of the layout stack'
+complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a default-layout -d 'Set the layout namespace to be used by all outputs by default.'
+complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a output-layout -d 'Set the layout namespace of currently focused output.'
# Tag managements
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-focused-tags -d 'Show views with tags corresponding to the set bits of tags'
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-view-tags -d 'Assign the currently focused view the tags corresponding to the set bits of tags'
@@ -46,13 +48,6 @@ complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a focus-fol
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a opacity -d 'Configure server-side opacity of views'
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-repeat -d 'Set the keyboard repeat rate and repeat delay'
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a xcursor-theme -d 'Set the xcursor theme'
-# Options
-complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a declare-option -d 'Declare a new option with the given type and initial value'
-complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a get-option -d 'Print the current value of the given option to stdout'
-complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-option -d 'Set the value of the specified option'
-complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a unset-option -d 'Unset the value of the specified option for the given output'
-complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a mod-option -d 'Add value to the value of the specified option'
-complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a output_title -d 'Changing this option changes the title of the Wayland and X11 backend outputs'
# Subcommands
complete -c riverctl -x -n '__fish_seen_subcommand_from focus-output' -a 'next previous'
diff --git a/completions/zsh/_riverctl b/completions/zsh/_riverctl
index dc60f39..feac372 100644
--- a/completions/zsh/_riverctl
+++ b/completions/zsh/_riverctl
@@ -29,6 +29,8 @@ _riverctl() {
'toggle-float:Toggle the floating state of the focused view'
'toggle-fullscreen:Toggle the fullscreen state of the focused view'
'zoom:Bump the focused view to the top of the layout stack'
+ 'default-layout:Set the layout namespace to be used by all outputs by default.'
+ 'output-layout:Set the layout namespace of currently focused output.'
# Tag management
'set-focused-tags:Show views with tags corresponding to the set bits of tags'
'set-view-tags:Assign the currently focused view the tags corresponding to the set bits of tags'
@@ -52,13 +54,6 @@ _riverctl() {
'opacity:Configure server-side opacity of views'
'set-repeat:Set the keyboard repeat rate and repeat delay'
'xcursor-theme:Set the xcursor theme'
- # Options
- 'declare-option:Declare a new option with the given type and initial value'
- 'get-option:Print the current value of the given option to stdout'
- 'set-option:Set the value of the specified option'
- 'unset-option:Unset the value of the specified option for the given output'
- 'mod-option:Add value to the value of the specified option'
- 'output_title:Changing this option changes the title of the Wayland and X11 backend outputs'
)
local -A opt_args
diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd
index 8219af3..7c9c3d5 100644
--- a/doc/riverctl.1.scd
+++ b/doc/riverctl.1.scd
@@ -258,38 +258,6 @@ 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
-
-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. Alternatively,
-the currently focused output may be targeted with the *-focused-output* flag.
-
-*declare-option* _name_ _type_ _value_
- Declare a new option with the given _type_ and initial _value_. If
- the option already exists, this command does nothing. The following
- types are available:
-
- - _int_: a signed 32-bit integer
- - _uint_: an unsigned 32-bit integer
- - _fixed_: a signed 24.8 bit fixed point number
- - _string_: a string of bytes, may be null
-
-*get-option* [*-output* _output_name_|*-focused-output*] _name_
- Print the current value of the given option to stdout.
-
-*set-option* [*-output* _output_name_|*-focused-output*] _name_ _value_
- Set the value of the specified option to _value_.
-
-*unset-option* (*-output* _output_name_|*-focused-output*) _name_
- Unset the value of the specified option for the given output and
- cause it to fall back to the global value. Either the *-output* or
- *-focused-output* flag is required.
-
-*mod-option* [*-output* _output_name_|*-focused-output*] _name_ _value_
- Add _value_ to the value of the specified option. _value_ can be negative.
-
# EXAMPLES
Bind bemenu-run to Super+P in normal mode:
diff --git a/doc/rivertile.1.scd b/doc/rivertile.1.scd
index b099a21..fdab055 100644
--- a/doc/rivertile.1.scd
+++ b/doc/rivertile.1.scd
@@ -13,29 +13,6 @@ rivertile - Tiled layout generator for river
*rivertile* is a layout client for river. It provides a simple tiled layout
split main/secondary stacks.
-# OPTIONS
-
-These options may be set using *riverctl*(1) or another river-options
-wayland client. *rivertile* declares these options on startup, so setting
-these options before starting rivertile requires them to be declared manually.
-
-*main_location* (string, default "top")
- The location of the main area. Vaild locations are "top", "bottom",
- "left", and "right".
-
-*main_count* (uint, default 1)
- The number of main views.
-
-*main_factor* (fixed, default 0.6)
- The percentage of the layout area reserved for main views. *rivertle*
- clamps this to the range `[0.1, 0.9]`.
-
-*view_padding* (uint, default 6)
- Padding around every view in pixels.
-
-*outer_padding* (uint, default 6)
- Padding around the edge of the layout area in pixels.
-
# AUTHORS
Maintained by Isaac Freund <ifreund@ifreund.xyz> who is assisted by open
diff --git a/example/init b/example/init
index 0a43af2..7afc9bb 100755
--- a/example/init
+++ b/example/init
@@ -42,13 +42,13 @@ riverctl map normal $mod Return zoom
# Mod+H and Mod+L to decrease/increase the main_factor option by 0.05
# rivertile(1) uses this option to determine the width of the main stack.
-riverctl map normal $mod H spawn riverctl mod-option -focused-output main_factor -0.05
-riverctl map normal $mod L spawn riverctl mod-option -focused-output main_factor +0.05
+#riverctl map normal $mod H spawn riverctl mod-option -focused-output main_factor -0.05
+#riverctl map normal $mod L spawn riverctl mod-option -focused-output main_factor +0.05
# Mod+Shift+H and Mod+Shift+L to increment/decrement the main_count option.
# rivertile(1) uses this option to determine the number of "main" views in the layout.
-riverctl map normal $mod+Shift H spawn riverctl mod-option -focused-output main_count +1
-riverctl map normal $mod+Shift L spawn riverctl mod-option -focused-output main_count -1
+#riverctl map normal $mod+Shift H spawn riverctl mod-option -focused-output main_count +1
+#riverctl map normal $mod+Shift L spawn riverctl mod-option -focused-output main_count -1
# Mod+Alt+{H,J,K,L} to move views
riverctl map normal $mod+Mod1 H move left 100
@@ -104,10 +104,10 @@ riverctl map normal $mod Space toggle-float
riverctl map normal $mod F toggle-fullscreen
# Mod+{Up,Right,Down,Left} to change layout orientation
-riverctl map normal $mod Up spawn riverctl set-option -focused-output main_location top
-riverctl map normal $mod Right spawn riverctl set-option -focused-output main_location right
-riverctl map normal $mod Down spawn riverctl set-option -focused-output main_location bottom
-riverctl map normal $mod Left spawn riverctl set-option -focused-output main_location left
+#riverctl map normal $mod Up spawn riverctl set-option -focused-output main_location top
+#riverctl map normal $mod Right spawn riverctl set-option -focused-output main_location right
+#riverctl map normal $mod Down spawn riverctl set-option -focused-output main_location bottom
+#riverctl map normal $mod Left spawn riverctl set-option -focused-output main_location left
# Declare a passthrough mode. This mode has only a single mapping to return to
# normal mode. This makes it useful for testing a nested wayland compositor
@@ -155,7 +155,7 @@ riverctl csd-filter-add "gedit"
# Set opacity and fade effect
# riverctl opacity 1.0 0.75 0.0 0.1 20
-# Exec into the default layout generator, rivertile.
+# Set and exec into the default layout generator, rivertile.
# River will send the process group of the init executable SIGTERM on exit.
-riverctl set-option layout rivertile
+riverctl default-layout rivertile
exec rivertile
diff --git a/protocol/river-options-v2.xml b/protocol/river-options-v2.xml
deleted file mode 100644
index cb9f1d0..0000000
--- a/protocol/river-options-v2.xml
+++ /dev/null
@@ -1,209 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="river_options_v2">
- <copyright>
- Copyright 2020-2021 The River Developers
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- </copyright>
-
- <description summary="set and retrieve options">
- This protocol allows clients to access a typed key-value store of
- options. These options are global but may be overridden using a handle
- scoped to a wl_output. If no output scoped value has been set, then the
- global value is provided to this handle.
-
- This protocol does not define any semantic meaning of the options,
- that is left up to compositors.
-
- Compositors are free to set or declare options themselves at any time,
- though the type of any given option is immutable once set.
-
- Options are never removed once declared.
-
- Warning! The protocol described in this file is currently in the
- testing phase. Backward compatible changes may be added together with
- the corresponding interface version bump. Backward incompatible changes
- can only be done by creating a new major version of the extension.
- </description>
-
- <interface name="river_options_manager_v2" version="1">
- <description summary="declare options and get handles">
- This interface allows clients to declare new options and create
- river_option_v2 handle objects in order to retrieve the current
- value or set a new one.
- </description>
-
- <request name="destroy" type="destructor">
- <description summary="destroy the river_options_manager_v2 object">
- This request indicates that the client will not use the manager object
- any more. Objects that have been created through this instance are
- not affected.
- </description>
- </request>
-
- <request name="declare_int_option">
- <description summary="declare a new option">
- The option is created in the global scope and is initialized with the
- provided value. This request is ignored if the option already exists.
- </description>
- <arg name="key" type="string"/>
- <arg name="value" type="int"/>
- </request>
-
- <request name="declare_uint_option">
- <description summary="declare a new option">
- The option is created in the global scope and is initialized with the
- provided value. This request is ignored if the option already exists.
- </description>
- <arg name="key" type="string"/>
- <arg name="value" type="uint"/>
- </request>
-
- <request name="declare_string_option">
- <description summary="declare a new option">
- The option is created in the global scope and is initialized with the
- provided value. This request is ignored if the option already exists.
- </description>
- <arg name="key" type="string"/>
- <arg name="value" type="string" allow-null="true"/>
- </request>
-
- <request name="declare_fixed_option">
- <description summary="declare a new option">
- The option is created in the global scope and is initialized with the
- provided value. This request is ignored if the option already exists.
- </description>
- <arg name="key" type="string"/>
- <arg name="value" type="fixed"/>
- </request>
-
- <request name="get_option_handle">
- <description summary="get an option handle for the given key">
- If the output argument is non-null, the option is local to the given
- output. Otherwise it is considered global.
- </description>
- <arg name="key" type="string"/>
- <arg name="output" type="object" interface="wl_output" allow-null="true"/>
- <arg name="handle" type="new_id" interface="river_option_handle_v2"/>
- </request>
-
- <request name="unset_option">
- <description summary="unset an output-local value if any">
- This causes the value of the option for the given output to fall
- back to the global value.
- </description>
- <arg name="key" type="string"/>
- <arg name="output" type="object" interface="wl_output"/>
- </request>
- </interface>
-
- <interface name="river_option_handle_v2" version="1">
- <description summary="handle to an option">
- On binding this object, one of the events will immediately be sent by
- the server to inform the client of the current state of the option,
- including its type. Making one of the 4 set requests before receiving
- this first event would be a bug as the client would not yet know the
- type of the option. New events will be sent as the state changes.
- </description>
-
- <request name="destroy" type="destructor">
- <description summary="destroy the handle">
- This request indicates that the client will not use the
- river_option_handle_v2 any more and that it may be safely destroyed.
- </description>
- </request>
-
- <enum name="error">
- <entry name="request_while_undeclared" value="0" summary="a request other
- than destroy was made after receiving the undeclared event"/>
- <entry name="type_mismatch" value="1"
- summary="a set request of the wrong type was made"/>
- </enum>
-
- <event name="undeclared">
- <description summary="the option has never been declared">
- No option with the the given name has ever been declared. All requests
- on this object aside from the destroy request are a protocol error and
- no further events will be sent.
- </description>
- </event>
-
- <event name="int_value">
- <description summary="the current value of the int option">
- This indicates to the client that the option is of type int as well
- as the current value of the option. Once set the type of the option
- can never change.
- </description>
- <arg name="value" type="int"/>
- </event>
-
- <event name="uint_value">
- <description summary="the current value of the uint option">
- This indicates to the client that the option is of type uint as well
- as the current value of the option. Once set the type of the option
- can never change.
- </description>
- <arg name="value" type="uint"/>
- </event>
-
- <event name="string_value">
- <description summary="the current value of the string option">
- This indicates to the client that the option is of type string as well
- as the current value of the option. Once set the type of the option
- can never change.
- </description>
- <arg name="value" type="string" allow-null="true"/>
- </event>
-
- <event name="fixed_value">
- <description summary="the current value of the fixed option">
- This indicates to the client that the option is of type fixed as well
- as the current value of the option. Once set the type of the option
- can never change.
- </description>
- <arg name="value" type="fixed"/>
- </event>
-
- <request name="set_int_value">
- <description summary="set the value of the option">
- If the option is of type int, set the value of the option.
- Otherwise, this request is a protocol error.
- </description>
- <arg name="value" type="int"/>
- </request>
-
- <request name="set_uint_value">
- <description summary="set the value of the option">
- If the option is of type uint, set the value of the option.
- Otherwise, this request is a protocol error.
- </description>
- <arg name="value" type="uint"/>
- </request>
-
- <request name="set_string_value">
- <description summary="set the value of the option">
- If the option is of type string, set the value of the option.
- Otherwise, this request is a protocol error.
- </description>
- <arg name="value" type="string" allow-null="true"/>
- </request>
-
- <request name="set_fixed_value">
- <description summary="set the value of the option">
- If the option is of type fixed, set the value of the option.
- Otherwise, this request is a protocol error.
- </description>
- <arg name="value" type="fixed"/>
- </request>
- </interface>
-</protocol>
diff --git a/river/Option.zig b/river/Option.zig
deleted file mode 100644
index 4265afa..0000000
--- a/river/Option.zig
+++ /dev/null
@@ -1,184 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020-2021 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const Self = @This();
-
-const std = @import("std");
-const mem = std.mem;
-const meta = std.meta;
-
-const wayland = @import("wayland");
-const wl = wayland.server.wl;
-const river = wayland.server.river;
-
-const util = @import("util.zig");
-
-const Output = @import("Output.zig");
-const OptionsManager = @import("OptionsManager.zig");
-const OutputOption = @import("OutputOption.zig");
-
-const log = std.log.scoped(.river_options);
-
-pub const Value = union(enum) {
- int: i32,
- uint: u32,
- fixed: wl.Fixed,
- string: ?[*:0]const u8,
-
- pub fn dupe(value: Value) !Value {
- return switch (value) {
- .string => |v| Value{ .string = if (v) |s| try util.gpa.dupeZ(u8, mem.span(s)) else null },
- else => value,
- };
- }
-
- pub fn deinit(value: *Value) void {
- if (value.* == .string) if (value.string) |s| util.gpa.free(mem.span(s));
- }
-};
-
-options_manager: *OptionsManager,
-link: wl.list.Link = undefined,
-
-key: [:0]const u8,
-value: Value,
-
-output_options: wl.list.Head(OutputOption, "link") = undefined,
-
-event: struct {
- /// Emitted whenever the value of the option changes.
- update: wl.Signal(*Value),
-} = undefined,
-
-handles: wl.list.Head(river.OptionHandleV2, null) = undefined,
-
-/// Allocate a new option, duping the provided key and value
-pub fn create(options_manager: *OptionsManager, key: [*:0]const u8, value: Value) !void {
- const self = try util.gpa.create(Self);
- errdefer util.gpa.destroy(self);
-
- var owned_value = try value.dupe();
- errdefer owned_value.deinit();
-
- self.* = .{
- .options_manager = options_manager,
- .key = try util.gpa.dupeZ(u8, mem.span(key)),
- .value = owned_value,
- };
- errdefer util.gpa.free(self.key);
-
- self.output_options.init();
- errdefer {
- var it = self.output_options.safeIterator(.forward);
- while (it.next()) |output_option| output_option.destroy();
- }
- var it = options_manager.server.root.all_outputs.first;
- while (it) |node| : (it = node.next) try OutputOption.create(self, node.data);
-
- self.event.update.init();
- self.handles.init();
-
- options_manager.options.append(self);
-}
-
-pub fn destroy(self: *Self) void {
- {
- var it = self.handles.safeIterator(.forward);
- while (it.next()) |handle| handle.destroy();
- }
- {
- var it = self.output_options.safeIterator(.forward);
- while (it.next()) |output_option| output_option.destroy();
- }
- self.value.deinit();
- self.link.remove();
- util.gpa.destroy(self);
-}
-
-pub fn getOutputOption(self: *Self, output: *Output) ?*OutputOption {
- var it = self.output_options.iterator(.forward);
- while (it.next()) |output_option| {
- if (output_option.output == output) return output_option;
- } else return null;
-}
-
-/// If the value is a string, the string is cloned.
-/// If the value is changed, send the proper event to all clients
-pub fn set(self: *Self, value: Value) !void {
- if (meta.activeTag(value) != meta.activeTag(self.value)) return error.TypeMismatch;
-
- self.value.deinit();
- self.value = try value.dupe();
-
- {
- var it = self.handles.iterator(.forward);
- while (it.next()) |handle| self.sendValue(handle);
- }
- {
- var it = self.output_options.iterator(.forward);
- while (it.next()) |output_option| {
- if (output_option.value == null) output_option.notifyChanged();
- }
- }
-
- self.event.update.emit(&self.value);
-}
-
-pub fn sendValue(self: Self, handle: *river.OptionHandleV2) void {
- switch (self.value) {
- .int => |v| handle.sendIntValue(v),
- .uint => |v| handle.sendUintValue(v),
- .fixed => |v| handle.sendFixedValue(v),
- .string => |v| handle.sendStringValue(v),
- }
-}
-
-pub fn addHandle(self: *Self, output: ?*Output, handle: *river.OptionHandleV2) void {
- if (output) |o| {
- self.getOutputOption(o).?.addHandle(handle);
- } else {
- self.handles.append(handle);
- self.sendValue(handle);
- handle.setHandler(*Self, handleRequest, handleDestroy, self);
- }
-}
-
-fn handleRequest(handle: *river.OptionHandleV2, request: river.OptionHandleV2.Request, self: *Self) void {
- switch (request) {
- .destroy => handle.destroy(),
- .set_int_value => |req| self.set(.{ .int = req.value }) catch |err| switch (err) {
- error.TypeMismatch => handle.postError(.type_mismatch, "option is not of type int"),
- error.OutOfMemory => unreachable,
- },
- .set_uint_value => |req| self.set(.{ .uint = req.value }) catch |err| switch (err) {
- error.TypeMismatch => handle.postError(.type_mismatch, "option is not of type uint"),
- error.OutOfMemory => unreachable,
- },
- .set_fixed_value => |req| self.set(.{ .fixed = req.value }) catch |err| switch (err) {
- error.TypeMismatch => handle.postError(.type_mismatch, "option is not of type fixed"),
- error.OutOfMemory => unreachable,
- },
- .set_string_value => |req| self.set(.{ .string = req.value }) catch |err| switch (err) {
- error.TypeMismatch => handle.postError(.type_mismatch, "option is not of type string"),
- error.OutOfMemory => handle.getClient().postNoMemory(),
- },
- }
-}
-
-fn handleDestroy(handle: *river.OptionHandleV2, self: *Self) void {
- handle.getLink().remove();
-}
diff --git a/river/OptionsManager.zig b/river/OptionsManager.zig
deleted file mode 100644
index a42f449..0000000
--- a/river/OptionsManager.zig
+++ /dev/null
@@ -1,188 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2020-2021 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const Self = @This();
-
-const std = @import("std");
-const mem = std.mem;
-
-const wayland = @import("wayland");
-const wl = wayland.server.wl;
-const river = wayland.server.river;
-
-const wlr = @import("wlroots");
-
-const util = @import("util.zig");
-
-const Option = @import("Option.zig");
-const Output = @import("Output.zig");
-const OutputOption = @import("OutputOption.zig");
-const Server = @import("Server.zig");
-
-const log = std.log.scoped(.river_options);
-
-server: *Server,
-global: *wl.Global,
-server_destroy: wl.Listener(*wl.Server) = wl.Listener(*wl.Server).init(handleServerDestroy),
-
-options: wl.list.Head(Option, "link") = undefined,
-
-pub fn init(self: *Self, server: *Server) !void {
- self.* = .{
- .server = server,
- .global = try wl.Global.create(server.wl_server, river.OptionsManagerV2, 1, *Self, self, bind),
- };
- self.options.init();
- server.wl_server.addDestroyListener(&self.server_destroy);
-
- try Option.create(self, "layout", .{ .string = null });
- try Option.create(self, "output_title", .{ .string = null });
-}
-
-pub fn createOutputOptions(self: *Self, output: *Output) !void {
- var it = self.options.iterator(.forward);
- while (it.next()) |option| try OutputOption.create(option, output);
-}
-
-pub fn destroyOutputOptions(self: *Self, output: *Output) void {
- var it = self.options.iterator(.forward);
- while (it.next()) |option| {
- if (option.getOutputOption(output)) |output_option| output_option.destroy();
- }
-}
-
-fn handleServerDestroy(listener: *wl.Listener(*wl.Server), wl_server: *wl.Server) void {
- const self = @fieldParentPtr(Self, "server_destroy", listener);
- self.global.destroy();
- var it = self.options.safeIterator(.forward);
- while (it.next()) |option| option.destroy();
-}
-
-fn bind(client: *wl.Client, self: *Self, version: u32, id: u32) callconv(.C) void {
- const options_manager = river.OptionsManagerV2.create(client, version, id) catch {
- client.postNoMemory();
- return;
- };
- options_manager.setHandler(*Self, handleRequest, null, self);
-}
-
-pub fn getOption(self: *Self, key: [:0]const u8) ?*Option {
- var it = self.options.iterator(.forward);
- while (it.next()) |option| {
- if (mem.eql(u8, option.key, key)) return option;
- } else return null;
-}
-
-fn handleRequest(
- options_manager: *river.OptionsManagerV2,
- request: river.OptionsManagerV2.Request,
- self: *Self,
-) void {
- switch (request) {
- .destroy => options_manager.destroy(),
-
- .declare_int_option => |req| if (self.getOption(mem.span(req.key)) == null) {
- Option.create(self, req.key, .{ .int = req.value }) catch {
- options_manager.getClient().postNoMemory();
- return;
- };
- },
- .declare_uint_option => |req| if (self.getOption(mem.span(req.key)) == null) {
- Option.create(self, req.key, .{ .uint = req.value }) catch {
- options_manager.getClient().postNoMemory();
- return;
- };
- },
- .declare_string_option => |req| if (self.getOption(mem.span(req.key)) == null) {
- Option.create(self, req.key, .{ .string = req.value }) catch {
- options_manager.getClient().postNoMemory();
- return;
- };
- },
- .declare_fixed_option => |req| if (self.getOption(mem.span(req.key)) == null) {
- Option.create(self, req.key, .{ .fixed = req.value }) catch {
- options_manager.getClient().postNoMemory();
- return;
- };
- },
-
- .get_option_handle => |req| {
- const output = if (req.output) |wl_output| blk: {
- // Ignore if the wl_output is inert
- const wlr_output = wlr.Output.fromWlOutput(wl_output) orelse return;
- break :blk @intToPtr(*Output, wlr_output.data);
- } else null;
-
- const option = self.getOption(mem.span(req.key)) orelse {
- // There is no option with the requested key. In this case
- // all we do is send an undeclared event and wait for the
- // client to destroy the resource.
- const handle = river.OptionHandleV2.create(
- options_manager.getClient(),
- options_manager.getVersion(),
- req.handle,
- ) catch {
- options_manager.getClient().postNoMemory();
- return;
- };
- handle.sendUndeclared();
- handle.setHandler(*Self, undeclaredHandleRequest, null, self);
- return;
- };
-
- const handle = river.OptionHandleV2.create(
- options_manager.getClient(),
- options_manager.getVersion(),
- req.handle,
- ) catch {
- options_manager.getClient().postNoMemory();
- return;
- };
-
- option.addHandle(output, handle);
- },
-
- .unset_option => |req| {
- // Ignore if the wl_output is inert
- const wlr_output = wlr.Output.fromWlOutput(req.output) orelse return;
- const output = @intToPtr(*Output, wlr_output.data);
-
- const option = self.getOption(mem.span(req.key)) orelse return;
- option.getOutputOption(output).?.unset();
- },
- }
-}
-
-fn undeclaredHandleRequest(
- handle: *river.OptionHandleV2,
- request: river.OptionHandleV2.Request,
- self: *Self,
-) void {
- switch (request) {
- .destroy => handle.destroy(),
- .set_int_value,
- .set_uint_value,
- .set_fixed_value,
- .set_string_value,
- => {
- handle.postError(
- .request_while_undeclared,
- "a request other than destroy was made on a handle to an undeclared option",
- );
- },
- }
-}
diff --git a/river/Output.zig b/river/Output.zig
index 6f981f6..252a409 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -38,8 +38,6 @@ 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 OutputOption = @import("OutputOption.zig");
const State = struct {
/// A bit field of focused tags
@@ -150,10 +148,6 @@ pub fn init(self: *Self, root: *Root, wlr_output: *wlr.Output) !void {
.height = effective_resolution.height,
};
- const options_manager = &root.server.options_manager;
- try options_manager.createOutputOptions(self);
- errdefer options_manager.destroyOutputOptions(self);
-
self.setTitle();
}
}
@@ -428,8 +422,6 @@ fn handleDestroy(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) v
std.log.scoped(.server).debug("output '{}' destroyed", .{self.wlr_output.name});
- root.server.options_manager.destroyOutputOptions(self);
-
// Remove the destroyed output from root if it wasn't already removed
root.removeOutput(self);
diff --git a/river/OutputOption.zig b/river/OutputOption.zig
deleted file mode 100644
index db3a0b3..0000000
--- a/river/OutputOption.zig
+++ /dev/null
@@ -1,145 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2021 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const Self = @This();
-
-const std = @import("std");
-const mem = std.mem;
-const meta = std.meta;
-
-const wayland = @import("wayland");
-const wl = wayland.server.wl;
-const river = wayland.server.river;
-
-const util = @import("util.zig");
-
-const Output = @import("Output.zig");
-const OptionsManager = @import("OptionsManager.zig");
-const Option = @import("Option.zig");
-
-const Value = Option.Value;
-
-option: *Option,
-
-link: wl.list.Link = undefined,
-
-output: *Output,
-value: ?Value = null,
-
-event: struct {
- /// Emitted whenever the value of the option changes.
- update: wl.Signal(*Value),
-} = undefined,
-
-handles: wl.list.Head(river.OptionHandleV2, null) = undefined,
-
-pub fn create(option: *Option, output: *Output) !void {
- const self = try util.gpa.create(Self);
- errdefer util.gpa.destroy(self);
-
- self.* = .{ .option = option, .output = output };
- self.event.update.init();
- self.handles.init();
-
- option.output_options.append(self);
-}
-
-pub fn destroy(self: *Self) void {
- if (self.value) |*value| value.deinit();
- self.link.remove();
- util.gpa.destroy(self);
-}
-
-pub fn addHandle(self: *Self, handle: *river.OptionHandleV2) void {
- self.handles.append(handle);
- self.sendValue(handle);
- handle.setHandler(*Self, handleRequest, handleDestroy, self);
-}
-
-pub fn unset(self: *Self) void {
- if (self.value) |*value| value.deinit();
- self.value = null;
-
- // Unsetting the output-specific value causes us to fall back to the
- // global value. Send this new value to all clients.
- var it = self.handles.iterator(.forward);
- while (it.next()) |handle| {
- self.option.sendValue(handle);
- }
-
- self.event.update.emit(&self.option.value);
-}
-
-/// If the value is a string, the string is cloned.
-/// If the value is changed, send the proper event to all clients
-pub fn set(self: *Self, value: Value) !void {
- if (meta.activeTag(value) != meta.activeTag(self.option.value)) return error.TypeMismatch;
-
- if (self.value) |*v| v.deinit();
- self.value = try value.dupe();
-
- self.notifyChanged();
-}
-
-pub fn notifyChanged(self: *Self) void {
- var it = self.handles.iterator(.forward);
- while (it.next()) |handle| self.sendValue(handle);
- self.event.update.emit(self.get());
-}
-
-pub fn get(self: *Self) *Value {
- return if (self.value) |*value| value else &self.option.value;
-}
-
-fn sendValue(self: Self, handle: *river.OptionHandleV2) void {
- if (self.value) |value| {
- switch (value) {
- .int => |v| handle.sendIntValue(v),
- .uint => |v| handle.sendUintValue(v),
- .fixed => |v| handle.sendFixedValue(v),
- .string => |v| handle.sendStringValue(v),
- }
- } else {
- self.option.sendValue(handle);
- }
-}
-
-fn handleRequest(handle: *river.OptionHandleV2, request: river.OptionHandleV2.Request, self: *Self) void {
- switch (request) {
- .destroy => handle.destroy(),
- .set_int_value => |req| self.set(.{ .int = req.value }) catch |err| switch (err) {
- error.TypeMismatch => handle.postError(.type_mismatch, "option is not of type int"),
- error.OutOfMemory => unreachable,
- },
- .set_uint_value => |req| self.set(.{ .uint = req.value }) catch |err| switch (err) {
- error.TypeMismatch => handle.postError(.type_mismatch, "option is not of type uint"),
- error.OutOfMemory => unreachable,
- },
- .set_fixed_value => |req| self.set(.{ .fixed = req.value }) catch |err| switch (err) {
- error.TypeMismatch => handle.postError(.type_mismatch, "option is not of type fixed"),
- error.OutOfMemory => unreachable,
- },
- .set_string_value => |req| self.set(.{ .string = req.value }) catch |err| switch (err) {
- error.TypeMismatch => handle.postError(.type_mismatch, "option is not of type string"),
- error.OutOfMemory => handle.getClient().postNoMemory(),
- },
- }
-}
-
-fn handleDestroy(handle: *river.OptionHandleV2, self: *Self) void {
- handle.getLink().remove();
-}
diff --git a/river/Server.zig b/river/Server.zig
index 66553fe..87b6922 100644
--- a/river/Server.zig
+++ b/river/Server.zig
@@ -26,7 +26,6 @@ const c = @import("c.zig");
const util = @import("util.zig");
const Config = @import("Config.zig");
-const OptionsManager = @import("OptionsManager.zig");
const Control = @import("Control.zig");
const DecorationManager = @import("DecorationManager.zig");
const InputManager = @import("InputManager.zig");
@@ -66,7 +65,6 @@ root: Root,
config: Config,
control: Control,
status_manager: StatusManager,
-options_manager: OptionsManager,
layout_manager: LayoutManager,
pub fn init(self: *Self) !void {
@@ -117,7 +115,6 @@ pub fn init(self: *Self) !void {
try self.decoration_manager.init(self);
try self.root.init(self);
// Must be called after root is initialized
- try self.options_manager.init(self);
try self.input_manager.init(self);
try self.control.init(self);
try self.status_manager.init(self);
diff --git a/riverctl/args.zig b/riverctl/args.zig
deleted file mode 100644
index 29ca0f9..0000000
--- a/riverctl/args.zig
+++ /dev/null
@@ -1,119 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2021 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const mem = std.mem;
-const cstr = std.cstr;
-
-const root = @import("root");
-
-pub const FlagDef = struct {
- name: [*:0]const u8,
- kind: enum { boolean, arg },
-};
-
-pub fn Args(comptime num_positionals: comptime_int, comptime flag_defs: []const FlagDef) type {
- return struct {
- const Self = @This();
-
- positionals: [num_positionals][*:0]const u8,
- flags: [flag_defs.len]struct {
- name: [*:0]const u8,
- value: union {
- boolean: bool,
- arg: ?[*:0]const u8,
- },
- },
-
- pub fn parse(argv: [][*:0]const u8) Self {
- var ret: Self = undefined;
-
- // Init all flags in the flags array to false/null
- inline for (flag_defs) |flag_def, flag_idx| {
- switch (flag_def.kind) {
- .boolean => ret.flags[flag_idx] = .{
- .name = flag_def.name,
- .value = .{ .boolean = false },
- },
- .arg => ret.flags[flag_idx] = .{
- .name = flag_def.name,
- .value = .{ .arg = null },
- },
- }
- }
-
- // Parse the argv in to the positionals and flags arrays
- 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) {
- .boolean => ret.flags[flag_idx].value.boolean = true,
- .arg => {
- arg_idx += 1;
- ret.flags[flag_idx].value.arg = if (arg_idx < argv.len)
- argv[arg_idx]
- else
- root.printErrorExit("flag '" ++ flag_def.name ++
- "' requires an argument but none was provided!", .{});
- },
- }
- // 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(
- "{} positional arguments expected but more were provided!",
- .{num_positionals},
- );
- }
-
- ret.positionals[positional_idx] = argv[arg_idx];
- positional_idx += 1;
- }
-
- if (positional_idx < num_positionals) {
- root.printErrorExit(
- "{} positional arguments expected but only {} were provided!",
- .{ num_positionals, positional_idx },
- );
- }
-
- return ret;
- }
-
- pub fn boolFlag(self: Self, flag_name: [*:0]const u8) bool {
- for (self.flags) |flag| {
- if (cstr.cmp(flag.name, flag_name) == 0) return flag.value.boolean;
- }
- unreachable;
- }
-
- pub fn argFlag(self: Self, flag_name: [*:0]const u8) ?[*:0]const u8 {
- for (self.flags) |flag| {
- if (cstr.cmp(flag.name, flag_name) == 0) return flag.value.arg;
- }
- unreachable;
- }
- };
-}
diff --git a/riverctl/main.zig b/riverctl/main.zig
index 6fd6341..64434e2 100644
--- a/riverctl/main.zig
+++ b/riverctl/main.zig
@@ -22,26 +22,13 @@ const assert = std.debug.assert;
const wayland = @import("wayland");
const wl = wayland.client.wl;
-const river = wayland.client.river;
const zriver = wayland.client.zriver;
-const zxdg = wayland.client.zxdg;
const gpa = std.heap.c_allocator;
-const options = @import("options.zig");
-
-pub const Output = struct {
- wl_output: *wl.Output,
- name: []const u8,
-};
-
pub const Globals = struct {
control: ?*zriver.ControlV1 = null,
- options_manager: ?*river.OptionsManagerV2 = null,
- status_manager: ?*zriver.StatusManagerV1 = null,
seat: ?*wl.Seat = null,
- output_manager: ?*zxdg.OutputManagerV1 = null,
- outputs: std.ArrayList(Output) = std.ArrayList(Output).init(gpa),
};
pub fn main() !void {
@@ -54,20 +41,9 @@ pub fn main() !void {
\\The Wayland server does not support river-control-unstable-v1.
\\Do your versions of river and riverctl match?
, .{}),
- error.RiverStatusManagerNotAdvertised => printErrorExit(
- \\The Wayland server does not support river-status-unstable-v1.
- \\Do your versions of river and riverctl match?
- , .{}),
- error.RiverOptionsManagerNotAdvertised => printErrorExit(
- \\The Wayland server does not support river-options-unstable-v1.
- \\Do your versions of river and riverctl match?
- , .{}),
error.SeatNotAdverstised => printErrorExit(
\\The Wayland server did not advertise any seat.
, .{}),
- error.XdgOutputNotAdvertised => printErrorExit(
- \\The Wayland server does not support xdg-output-unstable-v1.
- , .{}),
else => return err,
}
};
@@ -82,32 +58,20 @@ fn _main() !void {
registry.setListener(*Globals, registryListener, &globals) catch unreachable;
_ = try display.roundtrip();
- if (os.argv.len > 2 and mem.eql(u8, "declare-option", mem.span(os.argv[1]))) {
- try options.declareOption(display, &globals);
- } else if (os.argv.len > 2 and mem.eql(u8, "get-option", mem.span(os.argv[1]))) {
- 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, "unset-option", mem.span(os.argv[1]))) {
- try options.unsetOption(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;
-
- // Skip our name, send all other args
- // This next line is needed cause of https://github.com/ziglang/zig/issues/2622
- const args = os.argv;
- for (args[1..]) |arg| control.addArgument(arg);
-
- const callback = try control.runCommand(seat);
-
- callback.setListener(?*c_void, callbackListener, null) catch unreachable;
-
- // Loop until our callback is called and we exit.
- while (true) _ = try display.dispatch();
- }
+ const control = globals.control orelse return error.RiverControlNotAdvertised;
+ const seat = globals.seat orelse return error.SeatNotAdverstised;
+
+ // Skip our name, send all other args
+ // This next line is needed cause of https://github.com/ziglang/zig/issues/2622
+ const args = os.argv;
+ for (args[1..]) |arg| control.addArgument(arg);
+
+ const callback = try control.runCommand(seat);
+
+ callback.setListener(?*c_void, callbackListener, null) catch unreachable;
+
+ // Loop until our callback is called and we exit.
+ while (true) _ = try display.dispatch();
}
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, globals: *Globals) void {
@@ -118,15 +82,6 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, globals: *
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, river.OptionsManagerV2.getInterface().name) == 0) {
- globals.options_manager = registry.bind(global.name, river.OptionsManagerV2, 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) {
- const output = registry.bind(global.name, wl.Output, 1) catch @panic("out of memory");
- globals.outputs.append(.{ .wl_output = output, .name = undefined }) catch @panic("out of memory");
}
},
.global_remove => {},
@@ -142,10 +97,7 @@ fn callbackListener(callback: *zriver.CommandCallbackV1, event: zriver.CommandCa
}
os.exit(0);
},
- .failure => |failure| {
- std.debug.print("Error: {}\n", .{failure.failure_message});
- os.exit(1);
- },
+ .failure => |failure| printErrorExit("Error: {}\n", .{failure.failure_message}),
}
}
diff --git a/riverctl/options.zig b/riverctl/options.zig
deleted file mode 100644
index 9501610..0000000
--- a/riverctl/options.zig
+++ /dev/null
@@ -1,320 +0,0 @@
-// This file is part of river, a dynamic tiling wayland compositor.
-//
-// Copyright 2021 The River Developers
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-const std = @import("std");
-const os = std.os;
-const math = std.math;
-const mem = std.mem;
-const fmt = std.fmt;
-
-const wayland = @import("wayland");
-const wl = wayland.client.wl;
-const river = wayland.client.river;
-const zriver = wayland.client.zriver;
-const zxdg = wayland.client.zxdg;
-
-const root = @import("root");
-
-const Args = @import("args.zig").Args;
-const FlagDef = @import("args.zig").FlagDef;
-const Globals = @import("main.zig").Globals;
-const Output = @import("main.zig").Output;
-
-const ValueType = enum {
- int,
- uint,
- fixed,
- string,
-};
-
-const Context = struct {
- display: *wl.Display,
- key: [*:0]const u8,
- raw_value: [*:0]const u8,
- output: ?*Output,
-};
-
-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{}).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 options_manager = globals.options_manager orelse return error.RiverOptionsManagerNotAdvertised;
-
- switch (value_type) {
- .int => options_manager.declareIntOption(key, parseInt(raw_value)),
- .uint => options_manager.declareUintOption(key, parseUint(raw_value)),
- .fixed => options_manager.declareFixedOption(key, parseFixed(raw_value)),
- .string => options_manager.declareStringOption(key, raw_value),
- }
-
- _ = try display.flush();
-}
-
-fn parseInt(raw_value: [*:0]const u8) i32 {
- return fmt.parseInt(i32, mem.span(raw_value), 10) catch
- root.printErrorExit("{} is not a valid int", .{raw_value});
-}
-
-fn parseUint(raw_value: [*:0]const u8) u32 {
- return fmt.parseInt(u32, mem.span(raw_value), 10) catch
- root.printErrorExit("{} is not a valid uint", .{raw_value});
-}
-
-fn parseFixed(raw_value: [*:0]const u8) wl.Fixed {
- return wl.Fixed.fromDouble(fmt.parseFloat(f64, mem.span(raw_value)) catch
- root.printErrorExit("{} is not a valid fixed", .{raw_value}));
-}
-
-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 },
- .{ .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 = 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, getOptionListener, &ctx) catch unreachable;
-
- // We always exit when our listener is called
- while (true) _ = try display.dispatch();
-}
-
-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 },
- .{ .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, setOptionListener, &ctx) catch unreachable;
-
- // We always exit when our listener is called
- while (true) _ = try display.dispatch();
-}
-
-pub fn unsetOption(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 },
- .{ .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
- root.printErrorExit("unset requires either -output or -focused-output", .{});
-
- const key = args.positionals[0];
-
- const options_manager = globals.options_manager orelse return error.RiverOptionsManagerNotAdvertised;
-
- options_manager.unsetOption(key, output.wl_output);
-
- _ = try display.flush();
-}
-
-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| {
- const xdg_output = try output_manager.getXdgOutput(output.wl_output);
- xdg_output.setListener(*Output, xdgOutputListener, output) catch unreachable;
- }
- _ = try display.roundtrip();
-
- for (globals.outputs.items) |*output| {
- if (mem.eql(u8, output.name, mem.span(output_name))) return output;
- }
- root.printErrorExit("unknown output '{}'", .{output_name});
-}
-
-fn xdgOutputListener(xdg_output: *zxdg.OutputV1, event: zxdg.OutputV1.Event, output: *Output) void {
- switch (event) {
- .name => |ev| output.name = std.heap.c_allocator.dupe(u8, mem.span(ev.name)) catch @panic("out of memory"),
- else => {},
- }
-}
-
-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: *river.OptionHandleV2,
- event: river.OptionHandleV2.Event,
- ctx: *const Context,
-) void {
- switch (event) {
- .undeclared => root.printErrorExit("option '{}' has not been declared", .{ctx.key}),
- .int_value => |ev| printOutputExit("{}", .{ev.value}),
- .uint_value => |ev| printOutputExit("{}", .{ev.value}),
- .fixed_value => |ev| printOutputExit("{d}", .{ev.value.toDouble()}),
- .string_value => |ev| if (ev.value) |s| printOutputExit("{}", .{s}) else os.exit(0),
- }
-}
-
-fn printOutputExit(comptime format: []const u8, args: anytype) noreturn {
- const stdout = std.io.getStdOut().writer();
- stdout.print(format ++ "\n", args) catch os.exit(1);
- os.exit(0);
-}
-
-fn setOptionListener(
- handle: *river.OptionHandleV2,
- event: river.OptionHandleV2.Event,
- ctx: *const Context,
-) void {
- switch (event) {
- .undeclared => root.printErrorExit("option '{}' has not been declared", .{ctx.key}),
- .int_value => |ev| handle.setIntValue(parseInt(ctx.raw_value)),
- .uint_value => |ev| handle.setUintValue(parseUint(ctx.raw_value)),
- .fixed_value => |ev| handle.setFixedValue(parseFixed(ctx.raw_value)),
- .string_value => |ev| handle.setStringValue(if (ctx.raw_value[0] == 0) null else ctx.raw_value),
- }
- _ = ctx.display.flush() catch os.exit(1);
- os.exit(0);
-}
-
-fn modOptionListener(
- handle: *river.OptionHandleV2,
- event: river.OptionHandleV2.Event,
- ctx: *const Context,
-) void {
- switch (event) {
- .undeclared => root.printErrorExit("option '{}' has not been declared", .{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);
-}
-
-fn modIntValueRaw(handle: *river.OptionHandleV2, 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 modifier", .{raw_value});
- const new_value = math.add(i32, current, mod) catch
- root.printErrorExit("provided value of {d} would overflow option if added", .{mod});
- handle.setIntValue(new_value);
-}
-
-fn modUintValueRaw(handle: *river.OptionHandleV2, 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 modifier", .{raw_value});
- const new = @intCast(i32, current) + mod;
- handle.setUintValue(if (new < 0) 0 else @intCast(u32, new));
-}
-
-fn modFixedValueRaw(handle: *river.OptionHandleV2, 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 modifier", .{raw_value});
- handle.setFixedValue(wl.Fixed.fromDouble(current.toDouble() + mod));
-}
diff --git a/rivertile/main.zig b/rivertile/main.zig
index b33d9b0..4133a75 100644
--- a/rivertile/main.zig
+++ b/rivertile/main.zig
@@ -63,7 +63,6 @@ const gpa = std.heap.c_allocator;
const Context = struct {
initialized: bool = false,
layout_manager: ?*river.LayoutManagerV1 = null,
- options_manager: ?*river.OptionsManagerV2 = null,
outputs: std.TailQueue(Output) = .{},
fn addOutput(context: *Context, registry: *wl.Registry, name: u32) !void {
@@ -76,102 +75,25 @@ const Context = struct {
}
};
-fn Option(comptime key: [:0]const u8, comptime T: type, comptime default: T) type {
- return struct {
- const Self = @This();
- output: *Output,
- handle: *river.OptionHandleV2,
- value: T = default,
-
- fn init(option: *Self, context: *Context, output: *Output) !void {
- option.* = .{
- .output = output,
- .handle = try context.options_manager.?.getOptionHandle(key, output.wl_output),
- };
- option.handle.setListener(*Self, optionListener, option) catch unreachable;
- }
-
- fn deinit(option: *Self) void {
- option.handle.destroy();
- option.* = undefined;
- }
-
- fn optionListener(handle: *river.OptionHandleV2, event: river.OptionHandleV2.Event, option: *Self) void {
- const prev_value = option.value;
- assert(event != .undeclared); // We declare all options used in main()
- switch (T) {
- u32 => switch (event) {
- .uint_value => |ev| option.value = ev.value,
- else => std.log.err("expected value of uint type for " ++ key ++
- " option, falling back to default", .{}),
- },
- f64 => switch (event) {
- .fixed_value => |ev| option.value = ev.value.toDouble(),
- else => std.log.err("expected value of fixed type for " ++ key ++
- " option, falling back to default", .{}),
- },
- Location => switch (event) {
- .string_value => |ev| if (ev.value) |value| {
- if (std.meta.stringToEnum(Location, mem.span(value))) |location| {
- option.value = location;
- } else {
- std.log.err(
- \\invalid main_location "{s}", must be "top", "bottom", "left", or "right"
- , .{value});
- }
- },
- else => std.log.err("expected value of string type for " ++ key ++
- " option, falling back to default", .{}),
- },
- else => unreachable,
- }
- if (option.value != prev_value) option.output.layout.parametersChanged();
- }
- };
-}
-
const Output = struct {
wl_output: *wl.Output,
name: u32,
- main_location: Option("main_location", Location, default_main_location) = undefined,
- main_count: Option("main_count", u32, default_main_count) = undefined,
- main_factor: Option("main_factor", f64, default_main_factor) = undefined,
- view_padding: Option("view_padding", u32, default_view_padding) = undefined,
- outer_padding: Option("outer_padding", u32, default_outer_padding) = undefined,
-
layout: *river.LayoutV1 = undefined,
fn init(output: *Output, context: *Context, wl_output: *wl.Output, name: u32) !void {
output.* = .{ .wl_output = wl_output, .name = name };
- if (context.initialized) try output.initOptionsAndLayout(context);
+ if (context.initialized) try output.getLayout(context);
}
- fn initOptionsAndLayout(output: *Output, context: *Context) !void {
+ fn getLayout(output: *Output, context: *Context) !void {
assert(context.initialized);
- try output.main_location.init(context, output);
- errdefer output.main_location.deinit();
- try output.main_count.init(context, output);
- errdefer output.main_count.deinit();
- try output.main_factor.init(context, output);
- errdefer output.main_factor.deinit();
- try output.view_padding.init(context, output);
- errdefer output.view_padding.deinit();
- try output.outer_padding.init(context, output);
- errdefer output.outer_padding.deinit();
-
output.layout = try context.layout_manager.?.getLayout(output.wl_output, "rivertile");
output.layout.setListener(*Output, layoutListener, output) catch unreachable;
}
fn deinit(output: *Output) void {
output.wl_output.release();
-
- output.main_count.deinit();
- output.main_factor.deinit();
- output.view_padding.deinit();
- output.outer_padding.deinit();
-
output.layout.destroy();
}
@@ -180,18 +102,18 @@ const Output = struct {
.namespace_in_use => fatal("namespace 'rivertile' already in use.", .{}),
.layout_demand => |ev| {
- const secondary_count = if (ev.view_count > output.main_count.value)
- ev.view_count - output.main_count.value
+ const secondary_count = if (ev.view_count > default_main_count)
+ ev.view_count - default_main_count
else
0;
- const usable_width = switch (output.main_location.value) {
- .left, .right => ev.usable_width - (2 * output.outer_padding.value),
- .top, .bottom => ev.usable_height - (2 * output.outer_padding.value),
+ const usable_width = switch (default_main_location) {
+ .left, .right => ev.usable_width - 2 * default_outer_padding,
+ .top, .bottom => ev.usable_height - 2 * default_outer_padding,
};
- const usable_height = switch (output.main_location.value) {
- .left, .right => ev.usable_height - (2 * output.outer_padding.value),
- .top, .bottom => ev.usable_width - (2 * output.outer_padding.value),
+ const usable_height = switch (default_main_location) {
+ .left, .right => ev.usable_height - 2 * default_outer_padding,
+ .top, .bottom => ev.usable_width - 2 * default_outer_padding,
};
// to make things pixel-perfect, we make the first main and first secondary
@@ -204,18 +126,18 @@ const Output = struct {
var secondary_height: u32 = undefined;
var secondary_height_rem: u32 = undefined;
- if (output.main_count.value > 0 and secondary_count > 0) {
- main_width = @floatToInt(u32, output.main_factor.value * @intToFloat(f64, usable_width));
- main_height = usable_height / output.main_count.value;
- main_height_rem = usable_height % output.main_count.value;
+ if (default_main_count > 0 and secondary_count > 0) {
+ main_width = @floatToInt(u32, default_main_factor * @intToFloat(f64, usable_width));
+ main_height = usable_height / default_main_count;
+ main_height_rem = usable_height % default_main_count;
secondary_width = usable_width - main_width;
secondary_height = usable_height / secondary_count;
secondary_height_rem = usable_height % secondary_count;
- } else if (output.main_count.value > 0) {
+ } else if (default_main_count > 0) {
main_width = usable_width;
- main_height = usable_height / output.main_count.value;
- main_height_rem = usable_height % output.main_count.value;
+ main_height = usable_height / default_main_count;
+ main_height_rem = usable_height % default_main_count;
} else if (secondary_width > 0) {
main_width = 0;
secondary_width = usable_width;
@@ -230,50 +152,50 @@ const Output = struct {
var width: u32 = undefined;
var height: u32 = undefined;
- if (i < output.main_count.value) {
+ if (i < default_main_count) {
x = 0;
y = @intCast(i32, (i * main_height) + if (i > 0) main_height_rem else 0);
width = main_width;
height = main_height + if (i == 0) main_height_rem else 0;
} else {
x = @intCast(i32, main_width);
- y = @intCast(i32, (i - output.main_count.value) * secondary_height +
- if (i > output.main_count.value) secondary_height_rem else 0);
+ y = @intCast(i32, (i - default_main_count) * secondary_height +
+ if (i > default_main_count) secondary_height_rem else 0);
width = secondary_width;
- height = secondary_height + if (i == output.main_count.value) secondary_height_rem else 0;
+ height = secondary_height + if (i == default_main_count) secondary_height_rem else 0;
}
- x += @intCast(i32, output.view_padding.value);
- y += @intCast(i32, output.view_padding.value);
- width -= 2 * output.view_padding.value;
- height -= 2 * output.view_padding.value;
+ x += @intCast(i32, default_view_padding);
+ y += @intCast(i32, default_view_padding);
+ width -= 2 * default_view_padding;
+ height -= 2 * default_view_padding;
- switch (output.main_location.value) {
+ switch (default_main_location) {
.left => layout.pushViewDimensions(
ev.serial,
- x + @intCast(i32, output.outer_padding.value),
- y + @intCast(i32, output.outer_padding.value),
+ x + @intCast(i32, default_outer_padding),
+ y + @intCast(i32, default_outer_padding),
width,
height,
),
.right => layout.pushViewDimensions(
ev.serial,
- @intCast(i32, usable_width - width) - x + @intCast(i32, output.outer_padding.value),
- y + @intCast(i32, output.outer_padding.value),
+ @intCast(i32, usable_width - width) - x + @intCast(i32, default_outer_padding),
+ y + @intCast(i32, default_outer_padding),
width,
height,
),
.top => layout.pushViewDimensions(
ev.serial,
- y + @intCast(i32, output.outer_padding.value),
- x + @intCast(i32, output.outer_padding.value),
+ y + @intCast(i32, default_outer_padding),
+ x + @intCast(i32, default_outer_padding),
height,
width,
),
.bottom => layout.pushViewDimensions(
ev.serial,
- y + @intCast(i32, output.outer_padding.value),
- @intCast(i32, usable_width - width) - x + @intCast(i32, output.outer_padding.value),
+ y + @intCast(i32, default_outer_padding),
+ @intCast(i32, usable_width - width) - x + @intCast(i32, default_outer_padding),
height,
width,
),
@@ -305,23 +227,13 @@ pub fn main() !void {
if (context.layout_manager == null) {
fatal("wayland compositor does not support river_layout_v1.\n", .{});
}
- if (context.options_manager == null) {
- fatal("wayland compositor does not support river_options_v2.\n", .{});
- }
-
- // TODO: should be @tagName(default_main_location), https://github.com/ziglang/zig/issues/3779
- context.options_manager.?.declareStringOption("main_location", "left");
- context.options_manager.?.declareUintOption("main_count", default_main_count);
- context.options_manager.?.declareFixedOption("main_factor", wl.Fixed.fromDouble(default_main_factor));
- context.options_manager.?.declareUintOption("view_padding", default_view_padding);
- context.options_manager.?.declareUintOption("outer_padding", default_outer_padding);
context.initialized = true;
var it = context.outputs.first;
while (it) |node| : (it = node.next) {
const output = &node.data;
- try output.initOptionsAndLayout(&context);
+ try output.getLayout(&context);
}
while (true) _ = try display.dispatch();
@@ -332,8 +244,6 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: *
.global => |global| {
if (std.cstr.cmp(global.interface, river.LayoutManagerV1.getInterface().name) == 0) {
context.layout_manager = registry.bind(global.name, river.LayoutManagerV1, 1) catch return;
- } else if (std.cstr.cmp(global.interface, river.OptionsManagerV2.getInterface().name) == 0) {
- context.options_manager = registry.bind(global.name, river.OptionsManagerV2, 1) catch return;
} else if (std.cstr.cmp(global.interface, wl.Output.getInterface().name) == 0) {
context.addOutput(registry, global.name) catch |err| fatal("failed to bind output: {}", .{err});
}