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.
This commit is contained in:
parent
84f5283889
commit
a6f908d7eb
6 changed files with 94 additions and 56 deletions
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -61,12 +61,10 @@ 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))) {
|
||||
if (mem.eql(u8, namespace, output.layoutNamespace())) {
|
||||
output.pending.layout = &node.data;
|
||||
output.arrangeViews();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given namespace is already in use on the given output
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 },
|
||||
|
|
54
river/command/layout.zig
Normal file
54
river/command/layout.zig
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue