From a6f908d7ebfd94c20b27b54cf3275d7a078e3cae Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 26 Apr 2021 16:35:26 +0200 Subject: [PATCH] river: get rid of all server-created options - Replace the layout option with new default-layout and output-layout commands. - Remove the ability to get/set the output title entirely. --- doc/riverctl.1.scd | 18 +++++------- river/Config.zig | 7 +++++ river/Layout.zig | 8 ++---- river/Output.zig | 59 ++++++++++++++-------------------------- river/command.zig | 4 ++- river/command/layout.zig | 54 ++++++++++++++++++++++++++++++++++++ 6 files changed, 94 insertions(+), 56 deletions(-) create mode 100644 river/command/layout.zig 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 - if (self.layout_demand) |demand| demand.deinit(); - // 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); + 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 . + +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(); + } +}