wayland/surface: Keep transaction entries for all referenced surfaces

This keeps all surfaces referenced by a transaction alive until the
transaction is destroyed, and makes sure transactions are applied in
the same order as they were committed with respect to all surfaces
they reference.

v2:
* Guard against NULL entry in meta_wayland_transaction_apply.
v3:
* Keep single entries hash table.
v4:
* Unref the surface in the meta_wayland_transaction_merge_into while
  loop only if the "to" transaction didn't already have an entry for it,
  to prevent premature finalization of the surface (likely followed by a
  crash).
v5:
* Unref the surface (implicitly via g_hash_table_iter_remove) in the
  meta_wayland_transaction_merge_into while loop even if the "to"
  transaction already had an entry for it, or we leak a reference.
* Use g_clear_object & g_steal_pointer to not leave behind a dangling
  from->state pointer in meta_wayland_transaction_entry_merge_into.
v6:
* Add curly braces around
  meta_wayland_transaction_add_placement_surfaces calls. (Jonas Ådahl)

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1880>
This commit is contained in:
Michel Dänzer 2022-06-13 18:58:56 +02:00 committed by Michel Dänzer
parent 5fa4ce6fa8
commit e26df6ac14

View File

@ -35,6 +35,10 @@ struct _MetaWaylandTransaction
MetaWaylandTransaction *next_candidate;
uint64_t committed_sequence;
/*
* Keys: All surfaces referenced in the transaction
* Values: Pointer to MetaWaylandTransactionEntry for the surface
*/
GHashTable *entries;
};
@ -303,7 +307,7 @@ meta_wayland_transaction_ensure_entry (MetaWaylandTransaction *transaction,
return entry;
entry = g_new0 (MetaWaylandTransactionEntry, 1);
g_hash_table_insert (transaction->entries, surface, entry);
g_hash_table_insert (transaction->entries, g_object_ref (surface), entry);
return entry;
}
@ -315,6 +319,39 @@ meta_wayland_transaction_entry_free (MetaWaylandTransactionEntry *entry)
g_free (entry);
}
static void
meta_wayland_transaction_add_placement_surfaces (MetaWaylandTransaction *transaction,
MetaWaylandSurface *surface,
MetaWaylandSurfaceState *state)
{
GSList *l;
for (l = state->subsurface_placement_ops; l; l = l->next)
{
MetaWaylandSubsurfacePlacementOp *op = l->data;
if (op->surface)
meta_wayland_transaction_ensure_entry (transaction, op->surface);
if (op->sibling)
meta_wayland_transaction_ensure_entry (transaction, op->sibling);
}
}
static void
meta_wayland_transaction_add_entry (MetaWaylandTransaction *transaction,
MetaWaylandSurface *surface,
MetaWaylandTransactionEntry *entry)
{
g_hash_table_insert (transaction->entries, g_object_ref (surface), entry);
if (entry->state)
{
meta_wayland_transaction_add_placement_surfaces (transaction, surface,
entry->state);
}
}
void
meta_wayland_transaction_add_subsurface_position (MetaWaylandTransaction *transaction,
MetaWaylandSurface *surface,
@ -347,7 +384,7 @@ meta_wayland_transaction_entry_merge_into (MetaWaylandTransactionEntry *from,
return;
}
to->state = from->state;
to->state = g_steal_pointer (&from->state);
}
void
@ -362,16 +399,23 @@ meta_wayland_transaction_merge_into (MetaWaylandTransaction *from,
while (g_hash_table_iter_next (&iter, (gpointer *) &surface,
(gpointer *) &from_entry))
{
g_hash_table_iter_steal (&iter);
to_entry = meta_wayland_transaction_get_entry (to, surface);
if (!to_entry)
{
g_hash_table_insert (to->entries, surface, from_entry);
g_hash_table_iter_steal (&iter);
meta_wayland_transaction_add_entry (to, surface, from_entry);
g_object_unref (surface);
continue;
}
if (from_entry->state)
{
meta_wayland_transaction_add_placement_surfaces (to, surface,
from_entry->state);
}
meta_wayland_transaction_entry_merge_into (from_entry, to_entry);
meta_wayland_transaction_entry_free (from_entry);
g_hash_table_iter_remove (&iter);
}
meta_wayland_transaction_free (from);
@ -405,7 +449,7 @@ meta_wayland_transaction_new (MetaWaylandCompositor *compositor)
transaction = g_new0 (MetaWaylandTransaction, 1);
transaction->compositor = compositor;
transaction->entries = g_hash_table_new_full (NULL, NULL, NULL,
transaction->entries = g_hash_table_new_full (NULL, NULL, g_object_unref,
(GDestroyNotify) meta_wayland_transaction_entry_free);
return transaction;