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
|
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.
|
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
|
## TAG MANAGEMENT
|
||||||
|
|
||||||
Tags are similar to workspaces but more flexible. You can assign views multiple
|
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_
|
*mod-option* [*-output* _output_name_|*-focused-output*] _name_ _value_
|
||||||
Add _value_ to the value of the specified option. _value_ can be negative.
|
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
|
# EXAMPLES
|
||||||
|
|
||||||
Bind bemenu-run to Super+P in normal mode:
|
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
|
/// The selected focus_follows_cursor mode
|
||||||
focus_follows_cursor: FocusFollowsCursorMode = .disabled,
|
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 {
|
opacity: struct {
|
||||||
/// The opacity of focused views
|
/// The opacity of focused views
|
||||||
focused: f32 = 1.0,
|
focused: f32 = 1.0,
|
||||||
|
@ -119,4 +124,6 @@ pub fn deinit(self: *Self) void {
|
||||||
|
|
||||||
for (self.csd_filter.items) |s| util.gpa.free(s);
|
for (self.csd_filter.items) |s| util.gpa.free(s);
|
||||||
self.csd_filter.deinit();
|
self.csd_filter.deinit();
|
||||||
|
|
||||||
|
util.gpa.free(self.default_layout_namespace);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
// If the namespace matches that of the output, set the layout as
|
||||||
// the active one of the output and arrange it.
|
// the active one of the output and arrange it.
|
||||||
if (output.layout_option.get().string) |current_layout| {
|
if (mem.eql(u8, namespace, output.layoutNamespace())) {
|
||||||
if (mem.eql(u8, namespace, mem.span(current_layout))) {
|
output.pending.layout = &node.data;
|
||||||
output.pending.layout = &node.data;
|
output.arrangeViews();
|
||||||
output.arrangeViews();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,11 @@ layout_demand: ?LayoutDemand = null,
|
||||||
/// List of all layouts
|
/// List of all layouts
|
||||||
layouts: std.TailQueue(Layout) = .{},
|
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.
|
/// Determines where new views will be attached to the view stack.
|
||||||
attach_mode: AttachMode = .top,
|
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),
|
frame: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleFrame),
|
||||||
mode: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleMode),
|
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 {
|
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
|
// 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,
|
// 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,
|
.root = root,
|
||||||
.wlr_output = wlr_output,
|
.wlr_output = wlr_output,
|
||||||
.usable_box = undefined,
|
.usable_box = undefined,
|
||||||
.layout_option = undefined,
|
|
||||||
};
|
};
|
||||||
wlr_output.data = @ptrToInt(self);
|
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);
|
try options_manager.createOutputOptions(self);
|
||||||
errdefer options_manager.destroyOutputOptions(self);
|
errdefer options_manager.destroyOutputOptions(self);
|
||||||
|
|
||||||
// Set the default title of this output
|
self.setTitle();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,10 +447,10 @@ fn handleDestroy(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) v
|
||||||
self.frame.link.remove();
|
self.frame.link.remove();
|
||||||
self.mode.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
|
// 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;
|
self.wlr_output.data = undefined;
|
||||||
|
|
||||||
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
|
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()) {
|
if (self.wlr_output.isWl()) {
|
||||||
self.wlr_output.wlSetTitle(title);
|
self.wlr_output.wlSetTitle(title);
|
||||||
} else if (wlr.config.has_x11_backend and self.wlr_output.isX11()) {
|
} 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 {
|
pub fn handleLayoutNamespaceChange(self: *Self) 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);
|
|
||||||
// The user changed the layout namespace of this output. Try to find a
|
// The user changed the layout namespace of this output. Try to find a
|
||||||
// matching layout.
|
// matching layout.
|
||||||
self.pending.layout = if (value.string) |namespace| blk: {
|
var it = self.layouts.first;
|
||||||
var layout_it = self.layouts.first;
|
self.pending.layout = while (it) |node| : (it = node.next) {
|
||||||
break :blk while (layout_it) |node| : (layout_it = node.next) {
|
if (mem.eql(u8, self.layoutNamespace(), node.data.namespace)) break &node.data;
|
||||||
if (mem.eql(u8, mem.span(namespace), node.data.namespace)) break &node.data;
|
|
||||||
} else null;
|
|
||||||
} else null;
|
} else null;
|
||||||
self.arrangeViews();
|
self.arrangeViews();
|
||||||
self.root.startTransaction();
|
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 = "close", .impl = @import("command/close.zig").close },
|
||||||
.{ .name = "csd-filter-add", .impl = @import("command/filter.zig").csdFilterAdd },
|
.{ .name = "csd-filter-add", .impl = @import("command/filter.zig").csdFilterAdd },
|
||||||
.{ .name = "declare-mode", .impl = @import("command/declare_mode.zig").declareMode },
|
.{ .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 = "enter-mode", .impl = @import("command/enter_mode.zig").enterMode },
|
||||||
.{ .name = "exit", .impl = @import("command/exit.zig").exit },
|
.{ .name = "exit", .impl = @import("command/exit.zig").exit },
|
||||||
.{ .name = "float-filter-add", .impl = @import("command/filter.zig").floatFilterAdd },
|
.{ .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-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 = "focus-view", .impl = @import("command/focus_view.zig").focusView },
|
||||||
.{ .name = "map", .impl = @import("command/map.zig").map },
|
.{ .name = "map", .impl = @import("command/map.zig").map },
|
||||||
.{ .name = "map-pointer", .impl = @import("command/map.zig").mapPointer },
|
.{ .name = "map-pointer", .impl = @import("command/map.zig").mapPointer },
|
||||||
.{ .name = "move", .impl = @import("command/move.zig").move },
|
.{ .name = "move", .impl = @import("command/move.zig").move },
|
||||||
.{ .name = "opacity", .impl = @import("command/opacity.zig").opacity },
|
.{ .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 = "resize", .impl = @import("command/move.zig").resize },
|
||||||
.{ .name = "send-to-output", .impl = @import("command/send_to_output.zig").sendToOutput },
|
.{ .name = "send-to-output", .impl = @import("command/send_to_output.zig").sendToOutput },
|
||||||
.{ .name = "set-focused-tags", .impl = @import("command/tags.zig").setFocusedTags },
|
.{ .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