aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/riverctl.1.scd18
-rw-r--r--river/Config.zig7
-rw-r--r--river/Layout.zig8
-rw-r--r--river/Output.zig57
-rw-r--r--river/command.zig4
-rw-r--r--river/command/layout.zig54
6 files changed, 93 insertions, 55 deletions
diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd
index a5c444d..8219af3 100644
--- a/doc/riverctl.1.scd
+++ b/doc/riverctl.1.scd
@@ -72,6 +72,13 @@ over the Wayland protocol.
Bump the focused view to the top of the layout stack. If the top
view in the stack is already focused, bump the second view.
+*default-layout* _namespace_
+ Set the layout namespace to be used by all outputs by default.
+
+*output-layout* _namespace_
+ Set the layout namespace of currently focused output, overriding
+ the value set with *default-layout* if any.
+
## TAG MANAGEMENT
Tags are similar to workspaces but more flexible. You can assign views multiple
@@ -283,17 +290,6 @@ the currently focused output may be targeted with the *-focused-output* flag.
*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 which will always be available:
-
-*layout* (string)
- The layout namespace used to determine which layout should arrange this
- output. If set to null or no layout with this namespace exists for this
- output, the output will enter floating mode. Defaults to null.
-
-*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/Config.zig b/river/Config.zig
index 8e8abba..fe95d6b 100644
--- a/river/Config.zig
+++ b/river/Config.zig
@@ -59,6 +59,11 @@ csd_filter: std.ArrayList([]const u8),
/// The selected focus_follows_cursor mode
focus_follows_cursor: FocusFollowsCursorMode = .disabled,
+/// The default layout namespace for outputs which have never had a per-output
+/// value set. Call Output.handleLayoutNamespaceChange() on setting this if
+/// Output.layout_namespace is null.
+default_layout_namespace: []const u8 = &[0]u8{},
+
opacity: struct {
/// The opacity of focused views
focused: f32 = 1.0,
@@ -119,4 +124,6 @@ pub fn deinit(self: *Self) void {
for (self.csd_filter.items) |s| util.gpa.free(s);
self.csd_filter.deinit();
+
+ util.gpa.free(self.default_layout_namespace);
}
diff --git a/river/Layout.zig b/river/Layout.zig
index 407667f..9456b4b 100644
--- a/river/Layout.zig
+++ b/river/Layout.zig
@@ -61,11 +61,9 @@ pub fn create(client: *wl.Client, version: u32, id: u32, output: *Output, namesp
// If the namespace matches that of the output, set the layout as
// the active one of the output and arrange it.
- if (output.layout_option.get().string) |current_layout| {
- if (mem.eql(u8, namespace, mem.span(current_layout))) {
- output.pending.layout = &node.data;
- output.arrangeViews();
- }
+ if (mem.eql(u8, namespace, output.layoutNamespace())) {
+ output.pending.layout = &node.data;
+ output.arrangeViews();
}
}
diff --git a/river/Output.zig b/river/Output.zig
index d11879d..6f981f6 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -81,6 +81,11 @@ layout_demand: ?LayoutDemand = null,
/// List of all layouts
layouts: std.TailQueue(Layout) = .{},
+/// The current layout namespace of the output. If null,
+/// config.default_layout_namespace should be used instead.
+/// Call handleLayoutNamespaceChange() after setting this.
+layout_namespace: ?[]const u8 = null,
+
/// Determines where new views will be attached to the view stack.
attach_mode: AttachMode = .top,
@@ -95,11 +100,6 @@ 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),
-layout_option: *OutputOption,
-
-output_title: wl.Listener(*Option.Value) = wl.Listener(*Option.Value).init(handleTitleChange),
-layout_change: wl.Listener(*Option.Value) = wl.Listener(*Option.Value).init(handleLayoutChange),
-
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,
@@ -116,7 +116,6 @@ pub fn init(self: *Self, root: *Root, wlr_output: *wlr.Output) !void {
.root = root,
.wlr_output = wlr_output,
.usable_box = undefined,
- .layout_option = undefined,
};
wlr_output.data = @ptrToInt(self);
@@ -155,23 +154,7 @@ pub fn init(self: *Self, root: *Root, wlr_output: *wlr.Output) !void {
try options_manager.createOutputOptions(self);
errdefer options_manager.destroyOutputOptions(self);
- // Set the default title of this output
- 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;
- self.setTitle(default_title);
-
- const global_title_option = options_manager.getOption("output_title") orelse unreachable;
- const title_option = global_title_option.getOutputOption(self).?;
- title_option.set(.{ .string = default_title }) catch |err| switch (err) {
- error.TypeMismatch => unreachable,
- error.OutOfMemory => return err,
- };
-
- const global_layout_option = options_manager.getOption("layout") orelse unreachable;
- self.layout_option = global_layout_option.getOutputOption(self).?;
-
- self.layout_option.event.update.add(&self.layout_change);
- title_option.event.update.add(&self.output_title);
+ self.setTitle();
}
}
@@ -464,10 +447,10 @@ fn handleDestroy(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) v
self.frame.link.remove();
self.mode.link.remove();
- // Cleanup the layout demand, if any
+ // Free all memory and clean up the wlr.Output
if (self.layout_demand) |demand| demand.deinit();
+ if (self.layout_namespace) |namespace| util.gpa.free(namespace);
- // Free all memory and clean up the wlr.Output
self.wlr_output.data = undefined;
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
@@ -506,7 +489,9 @@ pub fn getEffectiveResolution(self: *Self) struct { width: u32, height: u32 } {
};
}
-pub fn setTitle(self: *Self, title: [*:0]const u8) void {
+fn setTitle(self: Self) void {
+ var buf: ["river - ".len + self.wlr_output.name.len + 1]u8 = undefined;
+ const title = fmt.bufPrintZ(&buf, "river - {}", .{mem.spanZ(&self.wlr_output.name)}) catch unreachable;
if (self.wlr_output.isWl()) {
self.wlr_output.wlSetTitle(title);
} else if (wlr.config.has_x11_backend and self.wlr_output.isX11()) {
@@ -514,21 +499,17 @@ pub fn setTitle(self: *Self, title: [*:0]const u8) void {
}
}
-fn handleTitleChange(listener: *wl.Listener(*Option.Value), value: *Option.Value) void {
- const self = @fieldParentPtr(Self, "output_title", listener);
- if (value.string) |title| self.setTitle(title);
-}
-
-fn handleLayoutChange(listener: *wl.Listener(*Option.Value), value: *Option.Value) void {
- const self = @fieldParentPtr(Self, "layout_change", listener);
+pub fn handleLayoutNamespaceChange(self: *Self) void {
// The user changed the layout namespace of this output. Try to find a
// matching layout.
- self.pending.layout = if (value.string) |namespace| blk: {
- var layout_it = self.layouts.first;
- break :blk while (layout_it) |node| : (layout_it = node.next) {
- if (mem.eql(u8, mem.span(namespace), node.data.namespace)) break &node.data;
- } else null;
+ var it = self.layouts.first;
+ self.pending.layout = while (it) |node| : (it = node.next) {
+ if (mem.eql(u8, self.layoutNamespace(), node.data.namespace)) break &node.data;
} else null;
self.arrangeViews();
self.root.startTransaction();
}
+
+pub fn layoutNamespace(self: Self) []const u8 {
+ return self.layout_namespace orelse self.root.server.config.default_layout_namespace;
+}
diff --git a/river/command.zig b/river/command.zig
index c0cf51b..03668df 100644
--- a/river/command.zig
+++ b/river/command.zig
@@ -50,16 +50,18 @@ const str_to_impl_fn = [_]struct {
.{ .name = "close", .impl = @import("command/close.zig").close },
.{ .name = "csd-filter-add", .impl = @import("command/filter.zig").csdFilterAdd },
.{ .name = "declare-mode", .impl = @import("command/declare_mode.zig").declareMode },
+ .{ .name = "default-layout", .impl = @import("command/layout.zig").defaultLayout },
.{ .name = "enter-mode", .impl = @import("command/enter_mode.zig").enterMode },
.{ .name = "exit", .impl = @import("command/exit.zig").exit },
.{ .name = "float-filter-add", .impl = @import("command/filter.zig").floatFilterAdd },
- .{ .name = "focus-output", .impl = @import("command/focus_output.zig").focusOutput },
.{ .name = "focus-follows-cursor", .impl = @import("command/focus_follows_cursor.zig").focusFollowsCursor },
+ .{ .name = "focus-output", .impl = @import("command/focus_output.zig").focusOutput },
.{ .name = "focus-view", .impl = @import("command/focus_view.zig").focusView },
.{ .name = "map", .impl = @import("command/map.zig").map },
.{ .name = "map-pointer", .impl = @import("command/map.zig").mapPointer },
.{ .name = "move", .impl = @import("command/move.zig").move },
.{ .name = "opacity", .impl = @import("command/opacity.zig").opacity },
+ .{ .name = "output-layout", .impl = @import("command/layout.zig").outputLayout },
.{ .name = "resize", .impl = @import("command/move.zig").resize },
.{ .name = "send-to-output", .impl = @import("command/send_to_output.zig").sendToOutput },
.{ .name = "set-focused-tags", .impl = @import("command/tags.zig").setFocusedTags },
diff --git a/river/command/layout.zig b/river/command/layout.zig
new file mode 100644
index 0000000..12df4d2
--- /dev/null
+++ b/river/command/layout.zig
@@ -0,0 +1,54 @@
+// 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 util = @import("../util.zig");
+
+const Error = @import("../command.zig").Error;
+const Seat = @import("../Seat.zig");
+
+pub fn outputLayout(
+ allocator: *std.mem.Allocator,
+ seat: *Seat,
+ args: []const []const u8,
+ out: *?[]const u8,
+) Error!void {
+ if (args.len < 2) return Error.NotEnoughArguments;
+ if (args.len > 2) return Error.TooManyArguments;
+
+ const output = seat.focused_output;
+ output.layout_namespace = try util.gpa.dupe(u8, args[1]);
+ output.handleLayoutNamespaceChange();
+}
+
+pub fn defaultLayout(
+ allocator: *std.mem.Allocator,
+ seat: *Seat,
+ args: []const []const u8,
+ out: *?[]const u8,
+) Error!void {
+ if (args.len < 2) return Error.NotEnoughArguments;
+ if (args.len > 2) return Error.TooManyArguments;
+
+ const server = seat.input_manager.server;
+ server.config.default_layout_namespace = try util.gpa.dupe(u8, args[1]);
+ var it = server.root.all_outputs.first;
+ while (it) |node| : (it = node.next) {
+ const output = node.data;
+ if (output.layout_namespace == null) output.handleLayoutNamespaceChange();
+ }
+}