fix flicker when arranging tiled windows

Fixes https://codeberg.org/dwl/dwl/issues/552
Fixes https://codeberg.org/dwl/dwl/issues/705

Also, floating children of fullscreen windows in floating layout now go
in LyrFS instead of LyrTile.
This commit is contained in:
Guido Cella
2026-10-20 21:23:49 +02:00
committed by Guido Cella
parent f08d8b3913
commit d3fa32993c
2 changed files with 280 additions and 52 deletions
+22 -5
View File
@@ -324,19 +324,36 @@ client_set_scale(struct wlr_surface *s, float scale)
wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale)); wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale));
} }
static inline void /* Returns nonzero if the client has to commit a buffer with the new size */
static inline uint32_t
client_set_size(Client *c, uint32_t width, uint32_t height) client_set_size(Client *c, uint32_t width, uint32_t height)
{ {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) { if (client_is_x11(c)) {
wlr_xwayland_surface_configure(c->surface.xwayland, wlr_xwayland_surface_configure(c->surface.xwayland,
c->geom.x + c->bw, c->geom.y + c->bw, width, height); c->geom.x + c->bw, c->geom.y + c->bw, width, height);
return; return (int)width != client_surface(c)->current.width
|| (int)height != client_surface(c)->current.height;
} }
#endif #endif
if ((int32_t)width != c->surface.xdg->toplevel->current.width if ((int32_t)width == c->surface.xdg->toplevel->scheduled.width
|| (int32_t)height != c->surface.xdg->toplevel->current.height) && (int32_t)height == c->surface.xdg->toplevel->scheduled.height)
wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height); return 0;
return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height);
}
static inline void
client_send_frame_done_iterator(struct wlr_surface *s, int sx, int sy, void *data)
{
wlr_surface_send_frame_done(s, data);
}
static inline void
client_send_frame_done(Client *c)
{
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
wlr_surface_for_each_surface(client_surface(c), client_send_frame_done_iterator, &now);
} }
static inline void static inline void
+258 -47
View File
@@ -126,6 +126,9 @@ typedef struct {
struct wl_list flink; struct wl_list flink;
struct wlr_box geom; /* layout-relative, includes border */ struct wlr_box geom; /* layout-relative, includes border */
struct wlr_box prev; /* layout-relative, includes border */ struct wlr_box prev; /* layout-relative, includes border */
struct wlr_box current; /* geom currently shown in the scene graph */
struct wlr_scene_node *snapshot; /* content shown while resizing */
uint32_t resize_serial; /* nonzero while waiting for a resized buffer */
struct wlr_box bounds; /* only width and height are used */ struct wlr_box bounds; /* only width and height are used */
union { union {
struct wlr_xdg_surface *xdg; struct wlr_xdg_surface *xdg;
@@ -262,6 +265,7 @@ typedef struct {
/* function declarations */ /* function declarations */
static void applybounds(Client *c, struct wlr_box *bbox); static void applybounds(Client *c, struct wlr_box *bbox);
static void applygeom(Client *c);
static void applyrules(Client *c); static void applyrules(Client *c);
static void arrange(Monitor *m); static void arrange(Monitor *m);
static void arrangelayer(Monitor *m, struct wl_list *list, static void arrangelayer(Monitor *m, struct wl_list *list,
@@ -352,6 +356,8 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags);
static void setpsel(struct wl_listener *listener, void *data); static void setpsel(struct wl_listener *listener, void *data);
static void setsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data);
static void setup(void); static void setup(void);
static struct wlr_scene_node *snapshot(struct wlr_scene_node *node,
struct wlr_scene_tree *parent, void *data);
static void spawn(const Arg *arg); static void spawn(const Arg *arg);
static void startdrag(struct wl_listener *listener, void *data); static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg); static void tag(const Arg *arg);
@@ -361,6 +367,11 @@ static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg); static void togglefullscreen(const Arg *arg);
static void toggletag(const Arg *arg); static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg); static void toggleview(const Arg *arg);
static void transactionapply(void);
static void transactioncheck(void *data);
static void transactioncommit(Client *c, int ready);
static void transactionschedule(void);
static int transactiontimeout(void *data);
static void unlocksession(struct wl_listener *listener, void *data); static void unlocksession(struct wl_listener *listener, void *data);
static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
static void unmapnotify(struct wl_listener *listener, void *data); static void unmapnotify(struct wl_listener *listener, void *data);
@@ -432,6 +443,14 @@ static struct wl_list mons;
static Monitor *selmon; static Monitor *selmon;
static int consumed[KEY_MAX + 1]; static int consumed[KEY_MAX + 1];
/* Transactions: the scene graph is updated only once all visible clients have
* committed buffers with their new size, or after TRANSACTION_TIMEOUT ms */
#define TRANSACTION_TIMEOUT 100
static struct wl_event_source *transaction_idle;
static struct wl_event_source *transaction_timer;
static int transaction_waiting;
static struct wl_array ghosts; /* struct wlr_scene_node *, unmapped clients */
/* global event handlers */ /* global event handlers */
static struct wl_listener cursor_axis = {.notify = axisnotify}; static struct wl_listener cursor_axis = {.notify = axisnotify};
static struct wl_listener cursor_button = {.notify = buttonpress}; static struct wl_listener cursor_button = {.notify = buttonpress};
@@ -466,6 +485,7 @@ static struct wl_listener new_foreign_toplevel_capture_request = {.notify = capt
#ifdef XWAYLAND #ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data); static void activatex11(struct wl_listener *listener, void *data);
static void associatex11(struct wl_listener *listener, void *data); static void associatex11(struct wl_listener *listener, void *data);
static void commitx11(struct wl_listener *listener, void *data);
static void configurex11(struct wl_listener *listener, void *data); static void configurex11(struct wl_listener *listener, void *data);
static void createnotifyx11(struct wl_listener *listener, void *data); static void createnotifyx11(struct wl_listener *listener, void *data);
static void dissociatex11(struct wl_listener *listener, void *data); static void dissociatex11(struct wl_listener *listener, void *data);
@@ -516,6 +536,41 @@ applybounds(Client *c, struct wlr_box *bbox)
c->geom.y = bbox->y; c->geom.y = bbox->y;
} }
void
applygeom(Client *c)
{
struct wlr_box clip;
Client *p = client_get_parent(c);
/* We move all floating clients to LyrTile while in floating layout
* to avoid "real" floating clients be always on top */
wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
|| (p && p->isfullscreen) ? LyrFS
: c->isfloating && c->mon->lt[c->mon->sellt]->arrange ? LyrFloat
: LyrTile]);
c->current = c->geom;
if (c->snapshot) {
wlr_scene_node_destroy(c->snapshot);
c->snapshot = NULL;
wlr_scene_node_set_enabled(&c->scene_surface->node, 1);
}
/* Update scene-graph, including borders */
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw);
wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw);
wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw);
wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw);
wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw);
wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw);
client_get_clip(c, &clip);
wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
}
void void
applyrules(Client *c) applyrules(Client *c)
{ {
@@ -555,37 +610,18 @@ arrange(Monitor *m)
return; return;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (c->mon == m) { if (c->mon == m)
wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m));
client_set_suspended(c, !VISIBLEON(c, m) client_set_suspended(c, !VISIBLEON(c, m)
&& (!c->image_capture_source && (!c->image_capture_source
|| wl_list_empty(&c->image_capture_source->resources))); || wl_list_empty(&c->image_capture_source->resources)));
}
} }
wlr_scene_node_set_enabled(&m->fullscreen_bg->node,
(c = focustop(m)) && c->isfullscreen);
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
/* We move all clients (except fullscreen and unmanaged) to LyrTile while
* in floating layout to avoid "real" floating clients be always on top */
wl_list_for_each(c, &clients, link) {
if (c->mon != m || c->scene->node.parent == layers[LyrFS])
continue;
wlr_scene_node_reparent(&c->scene->node,
(!m->lt[m->sellt]->arrange && c->isfloating)
? layers[LyrTile]
: (m->lt[m->sellt]->arrange && c->isfloating)
? layers[LyrFloat]
: c->scene->node.parent);
}
if (m->lt[m->sellt]->arrange) if (m->lt[m->sellt]->arrange)
m->lt[m->sellt]->arrange(m); m->lt[m->sellt]->arrange(m);
motionnotify(0, NULL, 0, 0, 0, 0);
checkidleinhibitor(NULL); transactionschedule();
} }
void void
@@ -809,6 +845,11 @@ cleanup(void)
* Destroy it until it's fixed on the wlroots side */ * Destroy it until it's fixed on the wlroots side */
wlr_backend_destroy(backend); wlr_backend_destroy(backend);
if (transaction_idle)
wl_event_source_remove(transaction_idle);
wl_event_source_remove(transaction_timer);
wl_array_release(&ghosts);
wl_display_destroy(dpy); wl_display_destroy(dpy);
/* Destroy after the wayland display (when the monitors are already destroyed) /* Destroy after the wayland display (when the monitors are already destroyed)
to avoid destroying them with an invalid scene output. */ to avoid destroying them with an invalid scene output. */
@@ -990,6 +1031,8 @@ commitnotify(struct wl_listener *listener, void *data)
return; return;
} }
transactioncommit(c, c->resize_serial && c->surface.xdg->current.configure_serial
>= c->resize_serial);
resize(c, c->geom, (c->isfloating && !c->isfullscreen)); resize(c, c->geom, (c->isfloating && !c->isfullscreen));
} }
@@ -2385,7 +2428,7 @@ void
resize(Client *c, struct wlr_box geo, int interact) resize(Client *c, struct wlr_box geo, int interact)
{ {
struct wlr_box *bbox; struct wlr_box *bbox;
struct wlr_box clip; uint32_t serial;
if (!c->mon || !client_surface(c)->mapped) if (!c->mon || !client_surface(c)->mapped)
return; return;
@@ -2396,20 +2439,27 @@ resize(Client *c, struct wlr_box geo, int interact)
c->geom = geo; c->geom = geo;
applybounds(c, bbox); applybounds(c, bbox);
/* Update scene-graph, including borders */ if ((serial = client_set_size(c, c->geom.width - 2 * c->bw,
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); c->geom.height - 2 * c->bw)))
wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); c->resize_serial = serial;
wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw);
wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw);
wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw);
wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw);
wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw);
wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw);
client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); if (interact || (!c->resize_serial && wlr_box_equal(&c->geom, &c->current))) {
client_get_clip(c, &clip); c->resize_serial = 0;
wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); applygeom(c);
return;
}
/* Until the transaction is applied, keep showing the current content
* instead of buffers with the new size at the old position. */
if (c->resize_serial) {
if (!c->snapshot && c->scene->node.enabled
&& (c->snapshot = snapshot(&c->scene_surface->node, c->scene, c)))
wlr_scene_node_set_enabled(&c->scene_surface->node, 0);
/* The scene graph doesn't send frame done events to hidden
* surfaces, but the client may be waiting for one to redraw */
client_send_frame_done(c);
}
transactionschedule();
} }
void void
@@ -2510,14 +2560,10 @@ setcursorshape(struct wl_listener *listener, void *data)
void void
setfloating(Client *c, int floating) setfloating(Client *c, int floating)
{ {
Client *p = client_get_parent(c);
c->isfloating = floating; c->isfloating = floating;
/* If in floating layout do not change the client's layer */ /* If in floating layout do not change the client's layer */
if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange) if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange)
return; return;
wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
(p && p->isfullscreen) ? LyrFS
: c->isfloating ? LyrFloat : LyrTile]);
arrange(c->mon); arrange(c->mon);
printstatus(); printstatus();
} }
@@ -2530,8 +2576,6 @@ setfullscreen(Client *c, int fullscreen)
return; return;
c->bw = fullscreen ? 0 : borderpx; c->bw = fullscreen ? 0 : borderpx;
client_set_fullscreen(c, fullscreen); client_set_fullscreen(c, fullscreen);
wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
? LyrFS : c->isfloating ? LyrFloat : LyrTile]);
if (fullscreen) { if (fullscreen) {
c->prev = c->geom; c->prev = c->geom;
@@ -2735,6 +2779,9 @@ setup(void)
wl_list_init(&clients); wl_list_init(&clients);
wl_list_init(&fstack); wl_list_init(&fstack);
transaction_timer = wl_event_loop_add_timer(event_loop, transactiontimeout, NULL);
wl_array_init(&ghosts);
xdg_shell = wlr_xdg_shell_create(dpy, 6); xdg_shell = wlr_xdg_shell_create(dpy, 6);
wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel); wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup); wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup);
@@ -2856,6 +2903,57 @@ setup(void)
#endif #endif
} }
struct wlr_scene_node *
snapshot(struct wlr_scene_node *node, struct wlr_scene_tree *parent, void *data)
{
/* Copy the visible part of a scene graph subtree, keeping references to
* the buffers so that they can be shown after the client changes them */
struct wlr_scene_node *child, *copy;
struct wlr_scene_tree *tree;
struct wlr_scene_rect *rect;
struct wlr_scene_buffer *src, *buffer;
if (!node->enabled)
return NULL;
switch (node->type) {
case WLR_SCENE_NODE_TREE:
tree = wlr_scene_tree_create(parent);
wl_list_for_each(child, &wlr_scene_tree_from_node(node)->children, link)
snapshot(child, tree, data);
copy = &tree->node;
break;
case WLR_SCENE_NODE_RECT:
rect = wlr_scene_rect_from_node(node);
copy = &wlr_scene_rect_create(parent, rect->width, rect->height, rect->color)->node;
break;
case WLR_SCENE_NODE_BUFFER:
src = wlr_scene_buffer_from_node(node);
buffer = wlr_scene_buffer_create(parent, NULL);
wlr_scene_buffer_set_dest_size(buffer, src->dst_width, src->dst_height);
wlr_scene_buffer_set_opaque_region(buffer, &src->opaque_region);
wlr_scene_buffer_set_opacity(buffer, src->opacity);
wlr_scene_buffer_set_filter_mode(buffer, src->filter_mode);
wlr_scene_buffer_set_transfer_function(buffer, src->transfer_function);
wlr_scene_buffer_set_primaries(buffer, src->primaries);
wlr_scene_buffer_set_color_encoding(buffer, src->color_encoding);
wlr_scene_buffer_set_color_range(buffer, src->color_range);
wlr_scene_buffer_set_source_box(buffer, &src->src_box);
wlr_scene_buffer_set_transform(buffer, src->transform);
wlr_scene_buffer_set_buffer(buffer, src->buffer);
copy = &buffer->node;
break;
default:
return NULL;
}
copy->data = data;
wlr_scene_node_set_position(copy, node->x, node->y);
if (node->parent == parent)
wlr_scene_node_place_above(copy, node);
return copy;
}
void void
spawn(const Arg *arg) spawn(const Arg *arg)
{ {
@@ -2977,6 +3075,88 @@ toggleview(const Arg *arg)
printstatus(); printstatus();
} }
void
transactionapply(void)
{
struct wlr_scene_node **ghost;
Client *c;
Monitor *m;
wl_event_source_timer_update(transaction_timer, 0);
transaction_waiting = 0;
wl_array_for_each(ghost, &ghosts)
wlr_scene_node_destroy(*ghost);
ghosts.size = 0;
wl_list_for_each(c, &clients, link) {
c->resize_serial = 0;
if (!c->mon)
continue;
if (c->mon->wlr_output->enabled)
wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, c->mon));
applygeom(c);
}
wl_list_for_each(m, &mons, link) {
if (m->wlr_output->enabled)
wlr_scene_node_set_enabled(&m->fullscreen_bg->node,
(c = focustop(m)) && c->isfullscreen);
}
motionnotify(0, NULL, 0, 0, 0, 0);
checkidleinhibitor(NULL);
}
void
transactioncheck(void *data)
{
Client *c;
transaction_idle = NULL;
/* Hidden clients don't have to redraw before the layout is shown */
wl_list_for_each(c, &clients, link) {
if (c->resize_serial && VISIBLEON(c, c->mon) && c->mon->wlr_output->enabled) {
if (!transaction_waiting) {
/* Don't restart the timer on updates of the pending
* layout, so the delay is bounded */
wl_event_source_timer_update(transaction_timer, TRANSACTION_TIMEOUT);
transaction_waiting = 1;
}
return;
}
}
transactionapply();
}
void
transactioncommit(Client *c, int ready)
{
if (c->resize_serial && ready) {
c->resize_serial = 0;
transactionschedule();
} else if (c->snapshot) {
/* This buffer is not shown, let the client draw again */
client_send_frame_done(c);
}
}
void
transactionschedule(void)
{
if (!transaction_idle)
transaction_idle = wl_event_loop_add_idle(event_loop, transactioncheck, NULL);
}
int
transactiontimeout(void *data)
{
transactionapply();
return 0;
}
void void
unlocksession(struct wl_listener *listener, void *data) unlocksession(struct wl_listener *listener, void *data)
{ {
@@ -3003,6 +3183,7 @@ unmapnotify(struct wl_listener *listener, void *data)
{ {
/* Called when the surface is unmapped, and should no longer be shown. */ /* Called when the surface is unmapped, and should no longer be shown. */
Client *c = wl_container_of(listener, c, unmap); Client *c = wl_container_of(listener, c, unmap);
struct wlr_scene_node *ghost, **slot;
if (c == grabc) { if (c == grabc) {
cursor_mode = CurNormal; cursor_mode = CurNormal;
grabc = NULL; grabc = NULL;
@@ -3011,6 +3192,15 @@ unmapnotify(struct wl_listener *listener, void *data)
focused_client = NULL; focused_client = NULL;
if (!client_is_unmanaged(c)) { if (!client_is_unmanaged(c)) {
/* Keep showing the client until the transaction rearranging the
* remaining ones is applied, to not show a gap */
if ((ghost = snapshot(&c->scene->node, c->scene->node.parent, NULL))) {
if ((slot = wl_array_add(&ghosts, sizeof(ghost))))
*slot = ghost;
else
wlr_scene_node_destroy(ghost);
transactionschedule();
}
wl_list_remove(&c->link); wl_list_remove(&c->link);
setmon(c, NULL, 0); setmon(c, NULL, 0);
wl_list_remove(&c->flink); wl_list_remove(&c->flink);
@@ -3034,6 +3224,9 @@ unmapnotify(struct wl_listener *listener, void *data)
c->image_capture_source = NULL; c->image_capture_source = NULL;
wlr_scene_node_destroy(&c->scene->node); wlr_scene_node_destroy(&c->scene->node);
client_surface(c)->data = NULL; client_surface(c)->data = NULL;
c->snapshot = NULL;
c->resize_serial = 0;
c->current = (struct wlr_box){0};
printstatus(); printstatus();
motionnotify(0, NULL, 0, 0, 0, 0); motionnotify(0, NULL, 0, 0, 0, 0);
} }
@@ -3233,20 +3426,26 @@ xytonode(double x, double y, struct wlr_surface **psurface,
Client **pc, LayerSurface **pl, double *nx, double *ny) Client **pc, LayerSurface **pl, double *nx, double *ny)
{ {
struct wlr_scene_node *node; struct wlr_scene_node *node;
struct wlr_scene_surface *scene_surface;
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
Client *c = NULL;
int layer; int layer;
for (layer = NUM_LAYERS - 1; !surface && layer >= (locked ? LyrBlock : 0); layer--) { for (layer = NUM_LAYERS - 1; !surface && !c && layer >= (locked ? LyrBlock : 0); layer--) {
if (layer == LyrIMPopup || if (layer == LyrIMPopup ||
!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny))) !(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny))
|| node->type != WLR_SCENE_NODE_BUFFER)
continue; continue;
if (node->type == WLR_SCENE_NODE_BUFFER) if ((scene_surface = wlr_scene_surface_try_from_buffer(
surface = wlr_scene_surface_try_from_buffer( wlr_scene_buffer_from_node(node))))
wlr_scene_buffer_from_node(node))->surface; surface = scene_surface->surface;
else /* snapshot of a client during a transaction */
c = node->data;
} }
if (psurface) *psurface = surface; if (psurface) *psurface = surface;
if (pc) *pc = c;
toplevel_from_wlr_surface(surface, pc, pl); toplevel_from_wlr_surface(surface, pc, pl);
} }
@@ -3299,10 +3498,21 @@ associatex11(struct wl_listener *listener, void *data)
{ {
Client *c = wl_container_of(listener, c, associate); Client *c = wl_container_of(listener, c, associate);
LISTEN(&client_surface(c)->events.commit, &c->commit, commitx11);
LISTEN(&client_surface(c)->events.map, &c->map, mapnotify); LISTEN(&client_surface(c)->events.map, &c->map, mapnotify);
LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify); LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify);
} }
void
commitx11(struct wl_listener *listener, void *data)
{
Client *c = wl_container_of(listener, c, commit);
struct wlr_surface *surface = client_surface(c);
transactioncommit(c, surface->current.width == c->geom.width - 2 * (int)c->bw
&& surface->current.height == c->geom.height - 2 * (int)c->bw);
}
void void
configurex11(struct wl_listener *listener, void *data) configurex11(struct wl_listener *listener, void *data)
{ {
@@ -3351,6 +3561,7 @@ void
dissociatex11(struct wl_listener *listener, void *data) dissociatex11(struct wl_listener *listener, void *data)
{ {
Client *c = wl_container_of(listener, c, dissociate); Client *c = wl_container_of(listener, c, dissociate);
wl_list_remove(&c->commit.link);
wl_list_remove(&c->map.link); wl_list_remove(&c->map.link);
wl_list_remove(&c->unmap.link); wl_list_remove(&c->unmap.link);
} }