git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commita4aca7c291cfed9a80829c4e50fbc1120b3e782b
parentfa1ddef8b0
authorLeon Henrik Plickat <[email protected]>
date2020-08-17 23:13
Implement "attach-mode"

 doc/riverctl.1.scd            |  3 +++
 river/Output.zig              |  5 +++++
 river/View.zig                |  4 ++--
 river/command.zig             |  1 +
 river/command/attach_mode.zig | 41 +++++++++++++++++++++++++++++++++++++++++
 river/view_stack.zig          | 32 ++++++++++++++++++++++++++++++++
 6 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd
index 3f32965..3b7da3c 100644
--- a/doc/riverctl.1.scd
+++ b/doc/riverctl.1.scd
@@ -95,6 +95,9 @@ that tag 1 through 9 are visible.
 
 ## CONFIGURATION COMMANDS
 
+*attach-mode* *top*|*bottom*
+	Configure where new views should attach in the view stack for the currently focused output.
+
 *background-color* _#RRGGBB_|_#RRGGBBAA_
 	Set the background color.
 
diff --git a/river/Output.zig b/river/Output.zig
index a62c724..777d831 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -30,6 +30,7 @@ const LayerSurface = @import("LayerSurface.zig");
 const Root = @import("Root.zig");
 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 State = struct {
@@ -37,6 +38,8 @@ const State = struct {
     tags: u32,
 };
 
+attach_mode: AttachMode,
+
 root: *Root,
 wlr_output: *c.wlr_output,
 
@@ -107,6 +110,8 @@ pub fn init(self: *Self, root: *Root, wlr_output: *c.wlr_output) !void {
 
     self.layout = try std.mem.dupe(util.gpa, u8, "full");
 
+    self.attach_mode = .top;
+
     self.status_trackers = std.SinglyLinkedList(OutputStatus).init();
 
     // Set up listeners
diff --git a/river/View.zig b/river/View.zig
index d745f6d..2686b2e 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -243,7 +243,7 @@ pub fn sendToOutput(self: *Self, destination_output: *Output) void {
     const node = @fieldParentPtr(ViewStack(Self).Node, "view", self);
 
     self.output.views.remove(node);
-    destination_output.views.push(node);
+    destination_output.views.attach(node, destination_output.attach_mode);
 
     self.output.sendViewTags();
     destination_output.sendViewTags();
@@ -340,7 +340,7 @@ pub fn map(self: *Self) void {
 
     // Add the view to the stack of its output
     const node = @fieldParentPtr(ViewStack(Self).Node, "view", self);
-    self.output.views.push(node);
+    self.output.views.attach(node, self.output.attach_mode);
 
     // Focus the new view, assuming the seat is focusing the proper output
     // and there isn't something else like a fullscreen view grabbing focus.
diff --git a/river/command.zig b/river/command.zig
index dc6619d..7cb685e 100644
--- a/river/command.zig
+++ b/river/command.zig
@@ -30,6 +30,7 @@ const str_to_impl_fn = [_]struct {
     name: []const u8,
     impl: fn (*std.mem.Allocator, *Seat, []const []const u8, *?[]const u8) Error!void,
 }{
+    .{ .name = "attach-mode",            .impl = @import("command/attach_mode.zig").attach_mode },
     .{ .name = "background-color",       .impl = @import("command/config.zig").backgroundColor },
     .{ .name = "border-color-focused",   .impl = @import("command/config.zig").borderColorFocused },
     .{ .name = "border-color-unfocused", .impl = @import("command/config.zig").borderColorUnfocused },
diff --git a/river/command/attach_mode.zig b/river/command/attach_mode.zig
new file mode 100644
index 0000000..a689893
--- /dev/null
+++ b/river/command/attach_mode.zig
@@ -0,0 +1,41 @@
+// This file is part of river, a dynamic tiling wayland compositor.
+//
+// Copyright 2020 Leon Henrik Plickat
+//
+// 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 attach_mode(
+    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;
+
+    if (std.mem.eql(u8, "top", args[1])) {
+        seat.focused_output.attach_mode = .top;
+    } else if (std.mem.eql(u8, "bottom", args[1])) {
+        seat.focused_output.attach_mode = .bottom;
+    } else {
+        return Error.UnknownOption;
+    }
+}
diff --git a/river/view_stack.zig b/river/view_stack.zig
index ea344d1..eb69ebc 100644
--- a/river/view_stack.zig
+++ b/river/view_stack.zig
@@ -17,6 +17,11 @@
 
 const View = @import("View.zig");
 
+pub const AttachMode = enum {
+    top,
+    bottom,
+};
+
 /// A specialized doubly-linked stack that allows for filtered iteration
 /// over the nodes. T must be View or *View.
 pub fn ViewStack(comptime T: type) type {
@@ -64,6 +69,33 @@ pub fn ViewStack(comptime T: type) type {
             self.first = new_node;
         }
 
+        /// Add a node to the bottom of the stack.
+        pub fn append(self: *Self, new_node: *Node) void {
+            // Set the prev/next pointers of the new node
+            new_node.prev = self.last;
+            new_node.next = null;
+
+            if (self.last) |last| {
+                // If the list is not empty, set the next pointer of the current
+                // first node to the new node.
+                last.next = new_node;
+            } else {
+                // If the list is empty set the first pointer to the new node.
+                self.first = new_node;
+            }
+
+            // Set the last pointer to the new node
+            self.last = new_node;
+        }
+
+        /// Attach a node into the viewstack based on the attach mode
+        pub fn attach(self: *Self, new_node: *Node, mode: AttachMode) void {
+            switch (mode) {
+                .top => self.push(new_node),
+                .bottom => self.append(new_node),
+            }
+        }
+
         /// Remove a node from the view stack. This removes it from the stack of
         /// all views as well as the stack of visible ones.
         pub fn remove(self: *Self, target_node: *Node) void {