xdg-toplevel: fix configure serial checking

Currently if another configure is in flight after the one we are
tracking the serial of and the client acks the second configure as well
(or only the second configure) before committing, we will never realize
the configure we are tracking has been acked.

Instead, listen for the ack_configure signal and set a bool that we can
check on surface commit.

This probably isn't an issue that would actually be hit by well behaved
clients as river doesn't send redundant configure events. However,
having correct code is always better even if it's slightly more complex.
This commit is contained in:
Isaac Freund 2021-09-18 18:25:21 +02:00
parent ab55ab8fc2
commit b8ebbc29cf
No known key found for this signature in database
GPG key ID: 86DED400DDFD7A11
2 changed files with 26 additions and 7 deletions

View file

@ -229,10 +229,10 @@ pub fn needsConfigure(self: Self) bool {
}; };
} }
pub fn configure(self: Self) void { pub fn configure(self: *Self) void {
switch (self.impl) { switch (self.impl) {
.xdg_toplevel => |xdg_toplevel| xdg_toplevel.configure(), .xdg_toplevel => |*xdg_toplevel| xdg_toplevel.configure(),
.xwayland_view => |xwayland_view| xwayland_view.configure(), .xwayland_view => |*xwayland_view| xwayland_view.configure(),
} }
} }

View file

@ -40,6 +40,9 @@ view: *View,
/// The corresponding wlroots object /// The corresponding wlroots object
xdg_surface: *wlr.XdgSurface, xdg_surface: *wlr.XdgSurface,
/// Set to true when the client acks the configure with serial View.pending_serial.
acked_pending_serial: bool = false,
// Listeners that are always active over the view's lifetime // Listeners that are always active over the view's lifetime
destroy: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleDestroy), destroy: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleDestroy),
map: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleMap), map: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleMap),
@ -48,6 +51,8 @@ new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNe
new_subsurface: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleNewSubsurface), new_subsurface: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleNewSubsurface),
// Listeners that are only active while the view is mapped // Listeners that are only active while the view is mapped
ack_configure: wl.Listener(*wlr.XdgSurface.Configure) =
wl.Listener(*wlr.XdgSurface.Configure).init(handleAckConfigure),
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit), commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
request_fullscreen: wl.Listener(*wlr.XdgToplevel.event.SetFullscreen) = request_fullscreen: wl.Listener(*wlr.XdgToplevel.event.SetFullscreen) =
wl.Listener(*wlr.XdgToplevel.event.SetFullscreen).init(handleRequestFullscreen), wl.Listener(*wlr.XdgToplevel.event.SetFullscreen).init(handleRequestFullscreen),
@ -104,10 +109,11 @@ pub fn needsConfigure(self: Self) bool {
} }
/// Send a configure event, applying the pending state of the view. /// Send a configure event, applying the pending state of the view.
pub fn configure(self: Self) void { pub fn configure(self: *Self) void {
const toplevel = self.xdg_surface.role_data.toplevel; const toplevel = self.xdg_surface.role_data.toplevel;
const state = &self.view.pending; const state = &self.view.pending;
self.view.pending_serial = toplevel.setSize(state.box.width, state.box.height); self.view.pending_serial = toplevel.setSize(state.box.width, state.box.height);
self.acked_pending_serial = false;
} }
/// Close the view. This will lead to the unmap and destroy events being sent /// Close the view. This will lead to the unmap and destroy events being sent
@ -173,6 +179,7 @@ fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurfa
const toplevel = self.xdg_surface.role_data.toplevel; const toplevel = self.xdg_surface.role_data.toplevel;
// Add listeners that are only active while mapped // Add listeners that are only active while mapped
self.xdg_surface.events.ack_configure.add(&self.ack_configure);
self.xdg_surface.surface.events.commit.add(&self.commit); self.xdg_surface.surface.events.commit.add(&self.commit);
toplevel.events.request_fullscreen.add(&self.request_fullscreen); toplevel.events.request_fullscreen.add(&self.request_fullscreen);
toplevel.events.request_move.add(&self.request_move); toplevel.events.request_move.add(&self.request_move);
@ -237,6 +244,7 @@ fn handleUnmap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSur
const self = @fieldParentPtr(Self, "unmap", listener); const self = @fieldParentPtr(Self, "unmap", listener);
// Remove listeners that are only active while mapped // Remove listeners that are only active while mapped
self.ack_configure.link.remove();
self.commit.link.remove(); self.commit.link.remove();
self.request_fullscreen.link.remove(); self.request_fullscreen.link.remove();
self.request_move.link.remove(); self.request_move.link.remove();
@ -247,7 +255,18 @@ fn handleUnmap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSur
self.view.unmap(); self.view.unmap();
} }
/// Called when the surface is comitted fn handleAckConfigure(
listener: *wl.Listener(*wlr.XdgSurface.Configure),
acked_configure: *wlr.XdgSurface.Configure,
) void {
const self = @fieldParentPtr(Self, "ack_configure", listener);
if (self.view.pending_serial) |serial| {
if (serial == acked_configure.serial) {
self.acked_pending_serial = true;
}
}
}
fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void { fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
const self = @fieldParentPtr(Self, "commit", listener); const self = @fieldParentPtr(Self, "commit", listener);
const view = self.view; const view = self.view;
@ -257,11 +276,11 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) voi
const new_box = Box.fromWlrBox(wlr_box); const new_box = Box.fromWlrBox(wlr_box);
// If we have sent a configure changing the size // If we have sent a configure changing the size
if (view.pending_serial) |s| { if (view.pending_serial != null) {
// Update the stored dimensions of the surface // Update the stored dimensions of the surface
view.surface_box = new_box; view.surface_box = new_box;
if (s == self.xdg_surface.configure_serial) { if (self.acked_pending_serial) {
// If this commit is in response to our configure and the // If this commit is in response to our configure and the
// transaction code is tracking this configure, notify it. // transaction code is tracking this configure, notify it.
// Otherwise, apply the pending state immediately. // Otherwise, apply the pending state immediately.