view: implement frame-perfect destroy
river's View objects may now outlive their wlroots counterparts so that we can continue to render a destroyed view until the transaction is completed.
This commit is contained in:
parent
59d6432332
commit
fa08d85c58
@ -288,7 +288,7 @@ fn layoutExternal(self: *Self, visible_count: u32) !void {
|
|||||||
var view_it = ViewStack(View).pendingIterator(self.views.first, self.pending.tags);
|
var view_it = ViewStack(View).pendingIterator(self.views.first, self.pending.tags);
|
||||||
while (view_it.next()) |node| {
|
while (view_it.next()) |node| {
|
||||||
const view = &node.view;
|
const view = &node.view;
|
||||||
if (!view.pending.float and !view.pending.fullscreen) {
|
if (!view.pending.float and !view.pending.fullscreen and !view.destroying) {
|
||||||
view.pending.box = view_boxen.items[i];
|
view.pending.box = view_boxen.items[i];
|
||||||
view.applyConstraints();
|
view.applyConstraints();
|
||||||
i += 1;
|
i += 1;
|
||||||
@ -309,7 +309,7 @@ pub fn arrangeViews(self: *Self) void {
|
|||||||
var it = ViewStack(View).pendingIterator(self.views.first, self.pending.tags);
|
var it = ViewStack(View).pendingIterator(self.views.first, self.pending.tags);
|
||||||
while (it.next()) |node| {
|
while (it.next()) |node| {
|
||||||
const view = &node.view;
|
const view = &node.view;
|
||||||
if (!view.pending.float and !view.pending.fullscreen) layout_count += 1;
|
if (!view.pending.float and !view.pending.fullscreen and !view.destroying) layout_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the usable area has a zero dimension, trying to arrange the layout
|
// If the usable area has a zero dimension, trying to arrange the layout
|
||||||
|
@ -133,6 +133,11 @@ pub fn startTransaction(self: *Self) void {
|
|||||||
while (view_it.next()) |view_node| {
|
while (view_it.next()) |view_node| {
|
||||||
const view = &view_node.view;
|
const view = &view_node.view;
|
||||||
|
|
||||||
|
if (view.destroying) {
|
||||||
|
if (view.saved_buffers.items.len == 0) view.saveBuffers();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (view.shouldTrackConfigure()) {
|
if (view.shouldTrackConfigure()) {
|
||||||
// Clear the serial in case this transaction is interrupting a prior one.
|
// Clear the serial in case this transaction is interrupting a prior one.
|
||||||
view.pending_serial = null;
|
view.pending_serial = null;
|
||||||
@ -226,9 +231,17 @@ fn commitTransaction(self: *Self) void {
|
|||||||
|
|
||||||
var view_tags_changed = false;
|
var view_tags_changed = false;
|
||||||
|
|
||||||
var view_it = ViewStack(View).iterator(output.views.first, std.math.maxInt(u32));
|
var view_it = output.views.first;
|
||||||
while (view_it.next()) |view_node| {
|
while (view_it) |view_node| {
|
||||||
const view = &view_node.view;
|
const view = &view_node.view;
|
||||||
|
|
||||||
|
if (view.destroying) {
|
||||||
|
view_it = view_node.next;
|
||||||
|
view.destroy();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
defer view_it = view_node.next;
|
||||||
|
|
||||||
if (!view.shouldTrackConfigure() and view.pending_serial != null) continue;
|
if (!view.shouldTrackConfigure() and view.pending_serial != null) continue;
|
||||||
|
|
||||||
// Apply pending state of the view
|
// Apply pending state of the view
|
||||||
|
@ -79,9 +79,14 @@ impl: Impl,
|
|||||||
/// The output this view is currently associated with
|
/// The output this view is currently associated with
|
||||||
output: *Output,
|
output: *Output,
|
||||||
|
|
||||||
/// This is non-null exactly when the view is mapped
|
/// This is from the point where the view is mapped until the surface
|
||||||
|
/// is destroyed by wlroots.
|
||||||
wlr_surface: ?*c.wlr_surface,
|
wlr_surface: ?*c.wlr_surface,
|
||||||
|
|
||||||
|
/// This View struct outlasts the wlroots object it wraps. This bool is set to
|
||||||
|
/// true when the backing wlr_xdg_toplevel or equivalent has been destroyed.
|
||||||
|
destroying: bool,
|
||||||
|
|
||||||
/// The double-buffered state of the view
|
/// The double-buffered state of the view
|
||||||
current: State,
|
current: State,
|
||||||
pending: State,
|
pending: State,
|
||||||
@ -110,6 +115,7 @@ pub fn init(self: *Self, output: *Output, tags: u32, surface: var) void {
|
|||||||
self.output = output;
|
self.output = output;
|
||||||
|
|
||||||
self.wlr_surface = null;
|
self.wlr_surface = null;
|
||||||
|
self.destroying = false;
|
||||||
|
|
||||||
self.current = .{
|
self.current = .{
|
||||||
.box = .{
|
.box = .{
|
||||||
@ -140,9 +146,17 @@ pub fn init(self: *Self, output: *Output, tags: u32, surface: var) void {
|
|||||||
} else unreachable;
|
} else unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Self) void {
|
/// Deinit the view, remove it from the view stack and free the memory.
|
||||||
|
pub fn destroy(self: *Self) void {
|
||||||
for (self.saved_buffers.items) |buffer| c.wlr_buffer_unlock(&buffer.wlr_client_buffer.*.base);
|
for (self.saved_buffers.items) |buffer| c.wlr_buffer_unlock(&buffer.wlr_client_buffer.*.base);
|
||||||
self.saved_buffers.deinit();
|
self.saved_buffers.deinit();
|
||||||
|
switch (self.impl) {
|
||||||
|
.xdg_toplevel => |*xdg_toplevel| xdg_toplevel.deinit(),
|
||||||
|
.xwayland_view => |*xwayland_view| xwayland_view.deinit(),
|
||||||
|
}
|
||||||
|
const node = @fieldParentPtr(ViewStack(Self).Node, "view", self);
|
||||||
|
self.output.views.remove(node);
|
||||||
|
util.gpa.destroy(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle changes to pending state and start a transaction to apply them
|
/// Handle changes to pending state and start a transaction to apply them
|
||||||
@ -362,6 +376,8 @@ pub fn unmap(self: *Self) void {
|
|||||||
|
|
||||||
log.debug(.server, "view '{}' unmapped", .{self.getTitle()});
|
log.debug(.server, "view '{}' unmapped", .{self.getTitle()});
|
||||||
|
|
||||||
|
self.destroying = true;
|
||||||
|
|
||||||
// Inform all seats that the view has been unmapped so they can handle focus
|
// Inform all seats that the view has been unmapped so they can handle focus
|
||||||
var it = root.server.input_manager.seats.first;
|
var it = root.server.input_manager.seats.first;
|
||||||
while (it) |node| : (it = node.next) {
|
while (it) |node| : (it = node.next) {
|
||||||
@ -369,12 +385,6 @@ pub fn unmap(self: *Self) void {
|
|||||||
seat.handleViewUnmap(self);
|
seat.handleViewUnmap(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.wlr_surface = null;
|
|
||||||
|
|
||||||
// Remove the view from its output's stack
|
|
||||||
const node = @fieldParentPtr(ViewStack(Self).Node, "view", self);
|
|
||||||
self.output.views.remove(node);
|
|
||||||
|
|
||||||
self.output.sendViewTags();
|
self.output.sendViewTags();
|
||||||
|
|
||||||
// Still need to arrange if fullscreened from the layout
|
// Still need to arrange if fullscreened from the layout
|
||||||
@ -382,10 +392,3 @@ pub fn unmap(self: *Self) void {
|
|||||||
|
|
||||||
root.startTransaction();
|
root.startTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destory the view and free the ViewStack node holding it.
|
|
||||||
pub fn destroy(self: *const Self) void {
|
|
||||||
self.deinit();
|
|
||||||
const node = @fieldParentPtr(ViewStack(Self).Node, "view", self);
|
|
||||||
util.gpa.destroy(node);
|
|
||||||
}
|
|
||||||
|
@ -24,6 +24,10 @@ const c = @import("c.zig");
|
|||||||
const Box = @import("Box.zig");
|
const Box = @import("Box.zig");
|
||||||
const View = @import("View.zig");
|
const View = @import("View.zig");
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn needsConfigure(self: Self) bool {
|
pub fn needsConfigure(self: Self) bool {
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,15 @@ pub fn init(self: *Self, view: *View, wlr_xdg_surface: *c.wlr_xdg_surface) void
|
|||||||
c.wl_signal_add(&self.wlr_xdg_surface.events.unmap, &self.listen_unmap);
|
c.wl_signal_add(&self.wlr_xdg_surface.events.unmap, &self.listen_unmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
if (self.view.wlr_surface != null) {
|
||||||
|
// Remove listeners that are active for the entire lifetime of the view
|
||||||
|
c.wl_list_remove(&self.listen_destroy.link);
|
||||||
|
c.wl_list_remove(&self.listen_map.link);
|
||||||
|
c.wl_list_remove(&self.listen_unmap.link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if a configure must be sent to ensure the dimensions of the
|
/// Returns true if a configure must be sent to ensure the dimensions of the
|
||||||
/// pending_box are applied.
|
/// pending_box are applied.
|
||||||
pub fn needsConfigure(self: Self) bool {
|
pub fn needsConfigure(self: Self) bool {
|
||||||
@ -139,14 +148,8 @@ pub fn getConstraints(self: Self) View.Constraints {
|
|||||||
/// Called when the xdg surface is destroyed
|
/// Called when the xdg surface is destroyed
|
||||||
fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
|
const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
|
||||||
const output = self.view.output;
|
self.deinit();
|
||||||
|
self.view.wlr_surface = null;
|
||||||
// Remove listeners that are active for the entire lifetime of the view
|
|
||||||
c.wl_list_remove(&self.listen_destroy.link);
|
|
||||||
c.wl_list_remove(&self.listen_map.link);
|
|
||||||
c.wl_list_remove(&self.listen_unmap.link);
|
|
||||||
|
|
||||||
self.view.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when the xdg surface is mapped, or ready to display on-screen.
|
/// Called when the xdg surface is mapped, or ready to display on-screen.
|
||||||
|
@ -56,6 +56,15 @@ pub fn init(self: *Self, view: *View, wlr_xwayland_surface: *c.wlr_xwayland_surf
|
|||||||
c.wl_signal_add(&self.wlr_xwayland_surface.events.unmap, &self.listen_unmap);
|
c.wl_signal_add(&self.wlr_xwayland_surface.events.unmap, &self.listen_unmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
if (self.view.wlr_surface != null) {
|
||||||
|
// Remove listeners that are active for the entire lifetime of the view
|
||||||
|
c.wl_list_remove(&self.listen_destroy.link);
|
||||||
|
c.wl_list_remove(&self.listen_map.link);
|
||||||
|
c.wl_list_remove(&self.listen_unmap.link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn needsConfigure(self: Self) bool {
|
pub fn needsConfigure(self: Self) bool {
|
||||||
return self.wlr_xwayland_surface.x != self.view.pending.box.x or
|
return self.wlr_xwayland_surface.x != self.view.pending.box.x or
|
||||||
self.wlr_xwayland_surface.y != self.view.pending.box.y or
|
self.wlr_xwayland_surface.y != self.view.pending.box.y or
|
||||||
@ -132,13 +141,8 @@ pub fn getConstraints(self: Self) View.Constraints {
|
|||||||
/// Called when the xwayland surface is destroyed
|
/// Called when the xwayland surface is destroyed
|
||||||
fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
|
const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
|
||||||
|
self.deinit();
|
||||||
// Remove listeners that are active for the entire lifetime of the view
|
self.view.wlr_surface = null;
|
||||||
c.wl_list_remove(&self.listen_destroy.link);
|
|
||||||
c.wl_list_remove(&self.listen_map.link);
|
|
||||||
c.wl_list_remove(&self.listen_unmap.link);
|
|
||||||
|
|
||||||
self.view.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when the xwayland surface is mapped, or ready to display on-screen.
|
/// Called when the xwayland surface is mapped, or ready to display on-screen.
|
||||||
|
Loading…
Reference in New Issue
Block a user