aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsaac Freund <mail@isaacfreund.com>2023-02-22 23:21:28 +0100
committerIsaac Freund <mail@isaacfreund.com>2023-02-28 14:55:59 +0100
commitf5dc67cfc1fad5b24b9e2d611a99eb2f3bba776c (patch)
tree1048b2b9e607855481bcd7ee89658af3e22ff078
parentce7fda4ed943be2dcdfe87e4a0456348f2fe7433 (diff)
downloadriver-f5dc67cfc1fad5b24b9e2d611a99eb2f3bba776c.tar.gz
river-f5dc67cfc1fad5b24b9e2d611a99eb2f3bba776c.tar.xz
View: use saved surface trees for transactions
-rw-r--r--river/Root.zig8
-rw-r--r--river/View.zig107
-rw-r--r--river/XdgToplevel.zig46
-rw-r--r--river/XwaylandView.zig33
4 files changed, 86 insertions, 108 deletions
diff --git a/river/Root.zig b/river/Root.zig
index 1a57426..2e7d705 100644
--- a/river/Root.zig
+++ b/river/Root.zig
@@ -393,9 +393,9 @@ pub fn startTransaction(self: *Self) void {
view.sendFrameDone();
}
- // If there are saved buffers present, then this transaction is interrupting
- // a previous transaction and we should keep the old buffers.
- if (view.saved_buffers.items.len == 0) view.saveBuffers();
+ // If the saved surface tree is enabled, then this transaction is interrupting
+ // a previous transaction and we should keep the old surface tree.
+ if (!view.saved_surface_tree.node.enabled) view.saveSurfaceTree();
} else {
if (view.needsConfigure()) view.configure();
}
@@ -473,7 +473,7 @@ fn commitTransaction(self: *Self) void {
view_it = view_node.next;
if (!view.tree.node.enabled) {
- view.dropSavedBuffers();
+ view.dropSavedSurfaceTree();
view.output.views.remove(view_node);
if (view.destroying) view.destroy();
continue;
diff --git a/river/View.zig b/river/View.zig
index 8cd96fe..8fb07cd 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -63,14 +63,6 @@ const State = struct {
urgent: bool = false,
};
-const SavedBuffer = struct {
- client_buffer: *wlr.ClientBuffer,
- /// x/y relative to the root surface in the surface tree.
- surface_box: wlr.Box,
- source_box: wlr.FBox,
- transform: wl.Output.Transform,
-};
-
/// The implementation of this view
impl: Impl,
@@ -78,6 +70,8 @@ impl: Impl,
output: *Output,
tree: *wlr.SceneTree,
+surface_tree: *wlr.SceneTree,
+saved_surface_tree: *wlr.SceneTree,
borders: struct {
left: *wlr.SceneRect,
right: *wlr.SceneRect,
@@ -97,17 +91,6 @@ pending: State,
/// The serial sent with the currently pending configure event
pending_serial: ?u32 = null,
-/// The currently commited geometry of the surface. The x/y may be negative if
-/// for example the client has decided to draw CSD shadows a la GTK.
-surface_box: wlr.Box = undefined,
-
-/// The geometry the view's surface had when the transaction started and
-/// buffers were saved.
-saved_surface_box: wlr.Box = undefined,
-
-/// These are what we render while a transaction is in progress
-saved_buffers: std.ArrayListUnmanaged(SavedBuffer) = .{},
-
/// The floating dimensions the view, saved so that they can be restored if the
/// view returns to floating mode.
float_box: wlr.Box = undefined,
@@ -121,29 +104,45 @@ draw_borders: bool = true,
request_activate: wl.Listener(*wlr.XdgActivationV1.event.RequestActivate) =
wl.Listener(*wlr.XdgActivationV1.event.RequestActivate).init(handleRequestActivate),
-pub fn init(self: *Self, output: *Output, tree: *wlr.SceneTree, impl: Impl) error{OutOfMemory}!void {
+pub fn create(output: *Output, impl: Impl) error{OutOfMemory}!*Self {
+ const node = try util.gpa.create(ViewStack(Self).Node);
+ errdefer util.gpa.destroy(node);
+ const self = &node.view;
+
const initial_tags = blk: {
const tags = output.current.tags & server.config.spawn_tagmask;
break :blk if (tags != 0) tags else output.current.tags;
};
+ const tree = try output.layers.views.createSceneTree();
+ errdefer tree.node.destroy();
+
+ const popup_tree = try output.layers.popups.createSceneTree();
+ errdefer popup_tree.node.destroy();
+
self.* = .{
.impl = impl,
.output = output,
.tree = tree,
+ .surface_tree = try tree.createSceneTree(),
+ .saved_surface_tree = try tree.createSceneTree(),
.borders = .{
.left = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
.right = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
.top = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
.bottom = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
},
- .popup_tree = try output.layers.popups.createSceneTree(),
+ .popup_tree = popup_tree,
.current = .{ .tags = initial_tags },
.pending = .{ .tags = initial_tags },
};
+ self.saved_surface_tree.node.setEnabled(false);
+
try SceneNodeData.attach(&self.tree.node, .{ .view = self });
try SceneNodeData.attach(&self.popup_tree.node, .{ .view = self });
+
+ return self;
}
/// If saved buffers of the view are currently in use by a transaction,
@@ -155,8 +154,9 @@ pub fn destroy(self: *Self) void {
// If there are still saved buffers, then this view needs to be kept
// around until the current transaction completes. This function will be
// called again in Root.commitTransaction()
- if (self.saved_buffers.items.len == 0) {
- self.saved_buffers.deinit(util.gpa);
+ if (!self.saved_surface_tree.node.enabled) {
+ self.tree.node.destroy();
+ self.popup_tree.node.destroy();
const node = @fieldParentPtr(ViewStack(Self).Node, "view", self);
util.gpa.destroy(node);
@@ -204,7 +204,7 @@ pub fn updateCurrent(self: *Self) void {
const config = &server.config;
self.current = self.pending;
- self.dropSavedBuffers();
+ if (self.saved_surface_tree.node.enabled) self.dropSavedSurfaceTree();
const color = blk: {
if (self.current.urgent) break :blk &config.border_color_urgent;
@@ -274,39 +274,40 @@ pub fn sendFrameDone(self: Self) void {
self.rootSurface().sendFrameDone(&now);
}
-pub fn dropSavedBuffers(self: *Self) void {
- for (self.saved_buffers.items) |buffer| buffer.client_buffer.base.unlock();
- self.saved_buffers.items.len = 0;
+pub fn dropSavedSurfaceTree(self: *Self) void {
+ assert(self.saved_surface_tree.node.enabled);
+
+ var it = self.saved_surface_tree.children.safeIterator(.forward);
+ while (it.next()) |node| node.destroy();
+
+ self.saved_surface_tree.node.setEnabled(false);
+ self.surface_tree.node.setEnabled(true);
}
-pub fn saveBuffers(self: *Self) void {
- assert(self.saved_buffers.items.len == 0);
- self.saved_surface_box = self.surface_box;
- self.forEachSurface(*std.ArrayListUnmanaged(SavedBuffer), saveBuffersIterator, &self.saved_buffers);
+pub fn saveSurfaceTree(self: *Self) void {
+ assert(!self.saved_surface_tree.node.enabled);
+ assert(self.saved_surface_tree.children.empty());
+
+ self.surface_tree.node.forEachBuffer(*wlr.SceneTree, saveSurfaceTreeIter, self.saved_surface_tree);
+
+ self.surface_tree.node.setEnabled(false);
+ self.saved_surface_tree.node.setEnabled(true);
}
-fn saveBuffersIterator(
- surface: *wlr.Surface,
- surface_x: c_int,
- surface_y: c_int,
- saved_buffers: *std.ArrayListUnmanaged(SavedBuffer),
+fn saveSurfaceTreeIter(
+ buffer: *wlr.SceneBuffer,
+ sx: c_int,
+ sy: c_int,
+ saved_surface_tree: *wlr.SceneTree,
) void {
- if (surface.buffer) |buffer| {
- var source_box: wlr.FBox = undefined;
- surface.getBufferSourceBox(&source_box);
- saved_buffers.append(util.gpa, .{
- .client_buffer = buffer,
- .surface_box = .{
- .x = surface_x,
- .y = surface_y,
- .width = surface.current.width,
- .height = surface.current.height,
- },
- .source_box = source_box,
- .transform = surface.current.transform,
- }) catch return;
- _ = buffer.base.lock();
- }
+ const saved = saved_surface_tree.createSceneBuffer(buffer.buffer) catch {
+ log.err("out of memory", .{});
+ return;
+ };
+ saved.node.setPosition(sx, sy);
+ saved.setDestSize(buffer.dst_width, buffer.dst_height);
+ saved.setSourceBox(&buffer.src_box);
+ saved.setTransform(buffer.transform);
}
/// Move a view from one output to another, sending the required enter/leave
@@ -524,7 +525,7 @@ pub fn unmap(self: *Self) void {
self.tree.node.setEnabled(false);
self.popup_tree.node.setEnabled(false);
- if (self.saved_buffers.items.len == 0) self.saveBuffers();
+ if (!self.saved_surface_tree.node.enabled) self.saveSurfaceTree();
// Inform all seats that the view has been unmapped so they can handle focus
var it = server.input_manager.seats.first;
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index 73c1efa..a15a3d1 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -32,12 +32,13 @@ const ViewStack = @import("view_stack.zig").ViewStack;
const log = std.log.scoped(.xdg_shell);
-/// The view this xdg toplevel implements
+/// TODO(zig): get rid of this and use @fieldParentPtr(), https://github.com/ziglang/zig/issues/6611
view: *View,
-/// The corresponding wlroots object
xdg_toplevel: *wlr.XdgToplevel,
+geometry: wlr.Box,
+
/// Set to true when the client acks the configure with serial View.pending_serial.
acked_pending_serial: bool = false,
@@ -59,19 +60,18 @@ request_resize: wl.Listener(*wlr.XdgToplevel.event.Resize) =
set_title: wl.Listener(void) = wl.Listener(void).init(handleSetTitle),
set_app_id: wl.Listener(void) = wl.Listener(void).init(handleSetAppId),
-/// The View will add itself to the output's view stack on map
pub fn create(output: *Output, xdg_toplevel: *wlr.XdgToplevel) error{OutOfMemory}!void {
- const node = try util.gpa.create(ViewStack(View).Node);
- errdefer util.gpa.destroy(node);
- const view = &node.view;
-
- const tree = try output.layers.views.createSceneTree();
- _ = try tree.createSceneXdgSurface(xdg_toplevel.base);
-
- try view.init(output, tree, .{ .xdg_toplevel = .{
- .view = view,
+ const view = try View.create(output, .{ .xdg_toplevel = .{
+ .view = undefined,
.xdg_toplevel = xdg_toplevel,
+ .geometry = undefined,
} });
+ errdefer view.destroy();
+
+ view.impl.xdg_toplevel.view = view;
+ xdg_toplevel.base.getGeometry(&view.impl.xdg_toplevel.geometry);
+
+ _ = try view.surface_tree.createSceneXdgSurface(xdg_toplevel.base);
xdg_toplevel.base.data = @ptrToInt(view);
@@ -273,14 +273,14 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
const self = @fieldParentPtr(Self, "commit", listener);
const view = self.view;
- var new_box: wlr.Box = undefined;
- self.xdg_toplevel.base.getGeometry(&new_box);
+ var new_geometry: wlr.Box = undefined;
+ self.xdg_toplevel.base.getGeometry(&new_geometry);
+
+ const size_changed = !std.meta.eql(self.geometry, new_geometry);
+ self.geometry = new_geometry;
// If we have sent a configure changing the size
if (view.pending_serial != null) {
- // Update the stored dimensions of the surface
- view.surface_box = new_box;
-
if (self.acked_pending_serial) {
// If this commit is in response to our configure and the
// transaction code is tracking this configure, notify it.
@@ -307,16 +307,12 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
// stashed buffer from when the transaction started.
view.sendFrameDone();
}
- } else {
- const size_changed = !std.meta.eql(view.surface_box, new_box);
- view.surface_box = new_box;
+ } else if ((self.view.pending.float or self.view.output.pending.layout == null) and size_changed) {
// If the client has decided to resize itself and the view is floating,
// then respect that resize.
- if ((self.view.pending.float or self.view.output.pending.layout == null) and size_changed) {
- view.pending.box.width = new_box.width;
- view.pending.box.height = new_box.height;
- view.applyPending();
- }
+ view.pending.box.width = new_geometry.width;
+ view.pending.box.height = new_geometry.height;
+ view.applyPending();
}
}
diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig
index f5de473..e17ac0a 100644
--- a/river/XwaylandView.zig
+++ b/river/XwaylandView.zig
@@ -33,7 +33,7 @@ const XwaylandOverrideRedirect = @import("XwaylandOverrideRedirect.zig");
const log = std.log.scoped(.xwayland);
-/// The view this xwayland view implements
+/// TODO(zig): get rid of this and use @fieldParentPtr(), https://github.com/ziglang/zig/issues/6611
view: *View,
xwayland_surface: *wlr.XwaylandSurface,
@@ -55,7 +55,6 @@ set_override_redirect: wl.Listener(*wlr.XwaylandSurface) =
wl.Listener(*wlr.XwaylandSurface).init(handleSetOverrideRedirect),
// Listeners that are only active while the view is mapped
-commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
set_title: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleSetTitle),
set_class: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleSetClass),
request_fullscreen: wl.Listener(*wlr.XwaylandSurface) =
@@ -64,17 +63,14 @@ request_minimize: wl.Listener(*wlr.XwaylandSurface.event.Minimize) =
wl.Listener(*wlr.XwaylandSurface.event.Minimize).init(handleRequestMinimize),
pub fn create(output: *Output, xwayland_surface: *wlr.XwaylandSurface) error{OutOfMemory}!void {
- const node = try util.gpa.create(ViewStack(View).Node);
- const view = &node.view;
-
- const tree = try output.layers.views.createSceneTree();
-
- try view.init(output, tree, .{ .xwayland_view = .{
- .view = view,
+ const view = try View.create(output, .{ .xwayland_view = .{
+ .view = undefined,
.xwayland_surface = xwayland_surface,
} });
+ errdefer view.destroy();
- const self = &node.view.impl.xwayland_view;
+ const self = &view.impl.xwayland_view;
+ self.view = view;
xwayland_surface.data = @ptrToInt(self);
// Add listeners that are active over the view's entire lifetime
@@ -189,19 +185,16 @@ pub fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface:
// Add listeners that are only active while mapped
const surface = xwayland_surface.surface.?;
- surface.events.commit.add(&self.commit);
xwayland_surface.events.set_title.add(&self.set_title);
xwayland_surface.events.set_class.add(&self.set_class);
xwayland_surface.events.request_fullscreen.add(&self.request_fullscreen);
xwayland_surface.events.request_minimize.add(&self.request_minimize);
- self.surface_tree = view.tree.createSceneSubsurfaceTree(surface) catch {
+ self.surface_tree = view.surface_tree.createSceneSubsurfaceTree(surface) catch {
log.err("out of memory", .{});
surface.resource.getClient().postNoMemory();
return;
};
- // Place the node below the view's border nodes
- self.surface_tree.?.node.lowerToBottom();
// Use the view's "natural" size centered on the output as the default
// floating dimensions
@@ -240,7 +233,6 @@ fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSur
self.surface_tree = null;
// Remove listeners that are only active while mapped
- self.commit.link.remove();
self.set_title.link.remove();
self.set_class.link.remove();
self.request_fullscreen.link.remove();
@@ -288,17 +280,6 @@ fn handleSetOverrideRedirect(
};
}
-fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
- const self = @fieldParentPtr(Self, "commit", listener);
-
- self.view.surface_box = .{
- .x = 0,
- .y = 0,
- .width = surface.current.width,
- .height = surface.current.height,
- };
-}
-
fn handleSetTitle(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void {
const self = @fieldParentPtr(Self, "set_title", listener);
self.view.notifyTitle();