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

commitad24f059fec6b77a829565b829a813bcc65a6c8f
parenta419c01df0
authorIsaac Freund <[email protected]>
date2020-06-28 21:42
river: implement toggle-fullscreen

The command works, but behaves a little strangely. Also, fullscreen
views are not yet rendered on an opqaue backdrop.

 river/Output.zig                    | 36 ++++++++++++++++++++----------------
 river/View.zig                      | 10 ++++++++++
 river/VoidView.zig                  |  4 ++++
 river/XdgToplevel.zig               |  4 ++++
 river/XwaylandView.zig              |  4 ++++
 river/command.zig                   |  1 +
 river/command/toggle_fullscreen.zig | 36 ++++++++++++++++++++++++++++++++++++
 7 files changed, 79 insertions(+), 16 deletions(-)

diff --git a/river/Output.zig b/river/Output.zig
index 5b6855e..e4e688a 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -297,29 +297,33 @@ fn layoutExternal(self: *Self, visible_count: u32, output_tags: u32) !void {
 /// pending state, the changes are not appplied until a transaction is started
 /// and completed.
 pub fn arrangeViews(self: *Self) void {
-    // If the output has a zero dimension, trying to arrange would cause
-    // an underflow and is pointless anyway.
-    if (self.usable_box.width == 0 or self.usable_box.height == 0) return;
-
     const output_tags = if (self.pending_focused_tags) |tags|
         tags
     else
         self.current_focused_tags;
 
-    const visible_count = blk: {
-        var count: u32 = 0;
-        var it = ViewStack(View).pendingIterator(self.views.first, output_tags);
-        while (it.next()) |node| {
-            if (!node.view.pending.float and !node.view.pending.fullscreen) count += 1;
+    const full_area = Box.fromWlrBox(c.wlr_output_layout_get_box(self.root.wlr_output_layout, self.wlr_output).*);
+
+    // Make fullscreen views take the full area, count up views that will be
+    // arranged by the layout.
+    var layout_count: u32 = 0;
+    var it = ViewStack(View).pendingIterator(self.views.first, output_tags);
+    while (it.next()) |node| {
+        const view = &node.view;
+        if (view.pending.fullscreen) {
+            view.pending.box = full_area;
+        } else if (!view.pending.float) {
+            layout_count += 1;
         }
-        break :blk count;
-    };
+    }
 
-    if (visible_count == 0) return;
+    // If the usable area has a zero dimension, trying to arrange the layout
+    // would cause an underflow and is pointless anyway.
+    if (layout_count == 0 or self.usable_box.width == 0 or self.usable_box.height == 0) return;
 
-    if (std.mem.eql(u8, self.layout, "full")) return layoutFull(self, visible_count, output_tags);
+    if (std.mem.eql(u8, self.layout, "full")) return layoutFull(self, layout_count, output_tags);
 
-    layoutExternal(self, visible_count, output_tags) catch |err| {
+    layoutExternal(self, layout_count, output_tags) catch |err| {
         switch (err) {
             LayoutError.BadExitCode => log.err(.layout, "layout command exited with non-zero return code", .{}),
             LayoutError.BadWindowConfiguration => log.err(.layout, "invalid window configuration", .{}),
@@ -327,11 +331,11 @@ pub fn arrangeViews(self: *Self) void {
             else => log.err(.layout, "'{}' error while trying to use external layout", .{err}),
         }
         log.err(.layout, "falling back to internal layout", .{});
-        layoutFull(self, visible_count, output_tags);
+        layoutFull(self, layout_count, output_tags);
     };
 }
 
-/// Arrange all layer surfaces of this output and addjust the usable aread
+/// Arrange all layer surfaces of this output and adjust the usable area
 pub fn arrangeLayers(self: *Self) void {
     const full_box = blk: {
         var width: c_int = undefined;
diff --git a/river/View.zig b/river/View.zig
index 5d9cd52..e3995a8 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -197,6 +197,16 @@ pub fn setFocused(self: *Self, focused: bool) void {
     }
 }
 
+/// Set the pending state to fullscren and inform the client. Should be
+/// followed by starting a transaction to apply the pending state.
+pub fn setFullscreen(self: *Self, fullscreen: bool) void {
+    self.pending.fullscreen = fullscreen;
+    switch (self.impl) {
+        .xdg_toplevel => |xdg_toplevel| xdg_toplevel.setFullscreen(fullscreen),
+        .xwayland_view => |xwayland_view| xwayland_view.setFullscreen(fullscreen),
+    }
+}
+
 /// Move a view from one output to another, sending the required enter/leave
 /// events.
 pub fn sendToOutput(self: *Self, destination_output: *Output) void {
diff --git a/river/VoidView.zig b/river/VoidView.zig
index 81392ff..d9bd3d4 100644
--- a/river/VoidView.zig
+++ b/river/VoidView.zig
@@ -35,6 +35,10 @@ pub fn setActivated(self: Self, activated: bool) void {
     unreachable;
 }
 
+pub fn setFullscreen(self: Self, fullscreen: bool) void {
+    unreachable;
+}
+
 pub fn close(self: Self) void {
     unreachable;
 }
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index 7243454..647c85e 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -88,6 +88,10 @@ pub fn setActivated(self: Self, activated: bool) void {
     _ = c.wlr_xdg_toplevel_set_activated(self.wlr_xdg_surface, activated);
 }
 
+pub fn setFullscreen(self: Self, fullscreen: bool) void {
+    _ = c.wlr_xdg_toplevel_set_fullscreen(self.wlr_xdg_surface, fullscreen);
+}
+
 /// Close the view. This will lead to the unmap and destroy events being sent
 pub fn close(self: Self) void {
     c.wlr_xdg_toplevel_send_close(self.wlr_xdg_surface);
diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig
index 1cd0143..0f6cc82 100644
--- a/river/XwaylandView.zig
+++ b/river/XwaylandView.zig
@@ -83,6 +83,10 @@ pub fn setActivated(self: Self, activated: bool) void {
     c.wlr_xwayland_surface_activate(self.wlr_xwayland_surface, activated);
 }
 
+pub fn setFullscreen(self: Self, fullscreen: bool) void {
+    c.wlr_xwayland_surface_set_fullscreen(self.wlr_xwayland_surface, fullscreen);
+}
+
 /// Close the view. This will lead to the unmap and destroy events being sent
 pub fn close(self: Self) void {
     c.wlr_xwayland_surface_close(self.wlr_xwayland_surface);
diff --git a/river/command.zig b/river/command.zig
index d5e0b6e..f71f18f 100644
--- a/river/command.zig
+++ b/river/command.zig
@@ -81,6 +81,7 @@ const str_to_impl_fn = [_]struct {
     .{ .name = "toggle-focused-tags", .impl = impl.toggleFocusedTags },
     .{ .name = "toggle-view-tags",    .impl = impl.toggleViewTags },
     .{ .name = "zoom",                .impl = impl.zoom },
+    .{ .name = "toggle-fullscreen",   .impl = impl.toggleFullscreen },
 };
 // zig fmt: on
 
diff --git a/river/command/toggle_fullscreen.zig b/river/command/toggle_fullscreen.zig
new file mode 100644
index 0000000..0b3acc7
--- /dev/null
+++ b/river/command/toggle_fullscreen.zig
@@ -0,0 +1,36 @@
+// This file is part of river, a dynamic tiling wayland compositor.
+//
+// Copyright 2020 Isaac Freund
+//
+// 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 Error = @import("../command.zig").Error;
+const Seat = @import("../Seat.zig");
+
+/// Toggle fullscreen state of the currently focused view
+pub fn toggleFullscreen(
+    allocator: *std.mem.Allocator,
+    seat: *Seat,
+    args: []const []const u8,
+    out: *?[]const u8,
+) Error!void {
+    if (args.len > 1) return Error.TooManyArguments;
+
+    if (seat.focused_view) |view| {
+        view.setFullscreen(!view.pending.fullscreen);
+        view.output.root.arrange();
+    }
+}