diff --git a/river/Root.zig b/river/Root.zig index f032665..601b415 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -132,6 +132,7 @@ fn startTransaction(self: *Self) void { var view_it = ViewStack(View).iterator(output.views.first, std.math.maxInt(u32)); while (view_it.next()) |view_node| { const view = &view_node.view; + // Clear the serial in case this transaction is interrupting a prior one. view.pending_serial = null; @@ -145,10 +146,10 @@ fn startTransaction(self: *Self) void { view.sendFrameDone(); } - // If there is a saved buffer present, then this transaction is interrupting - // a previous transaction and we should keep the old buffer. - if (view.stashed_buffer == null) { - view.stashBuffer(); + // 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(); } } } @@ -239,7 +240,7 @@ fn commitTransaction(self: *Self) void { view_tags_changed = true; } - view.dropStashedBuffer(); + view.dropSavedBuffers(); } if (view_tags_changed) output.sendViewTags(); diff --git a/river/View.zig b/river/View.zig index cc07252..608a7fb 100644 --- a/river/View.zig +++ b/river/View.zig @@ -38,6 +38,11 @@ const Impl = union(enum) { xwayland_view: XwaylandView, }; +const SavedBuffer = struct { + wlr_buffer: *c.wlr_buffer, + box: Box, +}; + /// The implementation of this view impl: Impl, @@ -66,8 +71,8 @@ pending_tags: ?u32, pending_serial: ?u32, -// This is what we render while a transaction is in progress -stashed_buffer: ?*c.wlr_buffer, +/// These are what we render while a transaction is in progress +saved_buffers: std.ArrayList(SavedBuffer), pub fn init( self: *Self, @@ -94,7 +99,7 @@ pub fn init( self.pending_serial = null; - self.stashed_buffer = null; + self.saved_buffers = std.ArrayList(SavedBuffer).init(output.root.server.allocator); if (@TypeOf(surface) == *c.wlr_xdg_surface) { self.impl = .{ .xdg_toplevel = undefined }; @@ -106,9 +111,8 @@ pub fn init( } pub fn deinit(self: Self) void { - if (self.stashed_buffer) |buffer| { - c.wlr_buffer_unref(buffer); - } + for (self.saved_buffers.items) |buffer| c.wlr_buffer_unref(buffer.wlr_buffer); + self.saved_buffers.deinit(); } pub fn needsConfigure(self: Self) bool { @@ -135,20 +139,42 @@ pub fn sendFrameDone(self: Self) void { c.wlr_surface_send_frame_done(self.wlr_surface.?, &now); } -pub fn dropStashedBuffer(self: *Self) void { - // TODO: log debug error - if (self.stashed_buffer) |buffer| { - c.wlr_buffer_unref(buffer); - self.stashed_buffer = null; - } +pub fn dropSavedBuffers(self: *Self) void { + for (self.saved_buffers.items) |buffer| c.wlr_buffer_unref(buffer.wlr_buffer); + self.saved_buffers.items.len = 0; } -pub fn stashBuffer(self: *Self) void { - // TODO: log debug error if there is already a saved buffer - if (self.wlr_surface) |wlr_surface| { - if (c.wlr_surface_has_buffer(wlr_surface)) { - _ = c.wlr_buffer_ref(wlr_surface.buffer); - self.stashed_buffer = wlr_surface.buffer; +pub fn saveBuffers(self: *Self) void { + if (self.saved_buffers.items.len > 0) { + Log.Error.log("view already has buffers saved, overwriting", .{}); + self.saved_buffers.items.len = 0; + } + + self.forEachSurface(saveBuffersIterator, &self.saved_buffers); +} + +fn saveBuffersIterator( + wlr_surface: ?*c.wlr_surface, + surface_x: c_int, + surface_y: c_int, + data: ?*c_void, +) callconv(.C) void { + const saved_buffers = @ptrCast( + *std.ArrayList(SavedBuffer), + @alignCast(@alignOf(*std.ArrayList(SavedBuffer)), data), + ); + if (wlr_surface) |surface| { + if (c.wlr_surface_has_buffer(surface)) { + saved_buffers.append(.{ + .wlr_buffer = surface.buffer, + .box = Box{ + .x = surface_x, + .y = surface_y, + .width = @intCast(u32, surface.current.width), + .height = @intCast(u32, surface.current.height), + }, + }) catch return; + _ = c.wlr_buffer_ref(surface.buffer); } } } diff --git a/river/render.zig b/river/render.zig index a7055ef..9ba3631 100644 --- a/river/render.zig +++ b/river/render.zig @@ -136,36 +136,35 @@ fn renderLayer(output: Output, layer: std.TailQueue(LayerSurface), now: *c.times } fn renderView(output: Output, view: *View, now: *c.timespec) void { - // If we have a stashed buffer, we are in the middle of a transaction - // and need to render that buffer until the transaction is complete. - if (view.stashed_buffer) |buffer| { - var box = c.wlr_box{ - .x = view.current_box.x, - .y = view.current_box.y, - .width = @intCast(c_int, view.current_box.width), - .height = @intCast(c_int, view.current_box.height), - }; + // If we have saved buffers, we are in the middle of a transaction + // and need to render those buffers until the transaction is complete. + if (view.saved_buffers.items.len != 0) { + for (view.saved_buffers.items) |saved_buffer| { + var box = saved_buffer.box.toWlrBox(); + box.x += view.current_box.x; + box.y += view.current_box.y; - // Scale the box to the output's current scaling factor - scaleBox(&box, output.wlr_output.scale); + // Scale the box to the output's current scaling factor + scaleBox(&box, output.wlr_output.scale); - var matrix: [9]f32 = undefined; - c.wlr_matrix_project_box( - &matrix, - &box, - .WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, - &output.wlr_output.transform_matrix, - ); + var matrix: [9]f32 = undefined; + c.wlr_matrix_project_box( + &matrix, + &box, + .WL_OUTPUT_TRANSFORM_NORMAL, + 0.0, + &output.wlr_output.transform_matrix, + ); - // This takes our matrix, the texture, and an alpha, and performs the actual - // rendering on the GPU. - _ = c.wlr_render_texture_with_matrix( - output.getRenderer(), - buffer.texture, - &matrix, - 1.0, - ); + // This takes our matrix, the texture, and an alpha, and performs the actual + // rendering on the GPU. + _ = c.wlr_render_texture_with_matrix( + output.getRenderer(), + saved_buffer.wlr_buffer.texture, + &matrix, + 1.0, + ); + } } else { // Since there is no stashed buffer, we are not in the middle of // a transaction and may simply render each toplevel surface.