]> git.mdlowis.com Git - proto/labwc.git/commitdiff
cosmic-workspaces: abstract transaction-addon
authorConsolatis <35009135+Consolatis@users.noreply.github.com>
Mon, 18 Nov 2024 17:38:42 +0000 (18:38 +0100)
committerJohan Malm <johanmalm@users.noreply.github.com>
Sun, 15 Dec 2024 21:06:39 +0000 (21:06 +0000)
This allows to use it for a future ext-workspace implementation.
It is also more generalized so can be used for other protocol
implementation in the future in case the protocols require some
kind of transaction management.

include/protocols/cosmic-workspaces-internal.h
include/protocols/transaction-addon.h [new file with mode: 0644]
src/protocols/cosmic_workspaces/cosmic-workspaces.c
src/protocols/cosmic_workspaces/meson.build
src/protocols/cosmic_workspaces/transactions.c [deleted file]
src/protocols/meson.build
src/protocols/transaction-addon.c [new file with mode: 0644]

index 36556bc681083530517fa0271fe46215dabcd9ba..ce977d8b1803be83d7c5738603244707315ff6fc 100644 (file)
@@ -16,43 +16,6 @@ enum pending_change {
        CW_PENDING_WS_REMOVE     = 1 << 3,
 };
 
-struct transaction {
-       uint32_t change;
-       struct wl_list link;
-};
-
-struct transaction_workspace {
-       struct transaction base;
-       struct lab_cosmic_workspace *workspace;
-};
-
-struct transaction_group {
-       struct transaction base;
-       struct lab_cosmic_workspace_group *group;
-       char *new_workspace_name;
-};
-
-struct session_context {
-       int ref_count;
-       struct wl_list transactions;
-};
-
-struct wl_resource_addon {
-       struct session_context *ctx;
-       void *data;
-};
-
-struct wl_resource_addon *resource_addon_create(struct session_context *ctx);
-
-void transaction_add_workspace_ev(struct lab_cosmic_workspace *ws,
-       struct wl_resource *resource, enum pending_change change);
-
-void transaction_add_workspace_group_ev(struct lab_cosmic_workspace_group *group,
-       struct wl_resource *resource, enum pending_change change,
-       const char *new_workspace_name);
-
-void resource_addon_destroy(struct wl_resource_addon *addon);
-
 void group_output_send_initial_state(struct lab_cosmic_workspace_group *group,
        struct wl_resource *group_resource);
 
diff --git a/include/protocols/transaction-addon.h b/include/protocols/transaction-addon.h
new file mode 100644 (file)
index 0000000..64383ba
--- /dev/null
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef LABWC_PROTOCOLS_TRANSACTION_ADDON_H
+#define LABWC_PROTOCOLS_TRANSACTION_ADDON_H
+
+#include <wayland-server-core.h>
+
+struct lab_transaction_op {
+       uint32_t change;
+       void *src;
+       void *data;
+
+       struct {
+               struct wl_signal destroy;
+       } events;
+
+       // Private
+       struct wl_list link;
+};
+
+struct lab_transaction_session_context {
+       int ref_count;
+       struct wl_list transaction_ops;
+};
+
+struct lab_wl_resource_addon {
+       struct lab_transaction_session_context *ctx;
+       void *data;
+};
+
+/*
+ * Creates a new addon which can be attached to a wl_resource via
+ * wl_resource_set_user_data() and retrieved via wl_resource_get_user_data().
+ *
+ * Usually the ctx argument should be addon->ctx of the parent wl_resource.
+ * If it is NULL it will be created automatically which can be used for top
+ * level wl_resources (when a client binds a wl_global from the registry).
+ *
+ * The context refcount is increased by one after this call.
+ */
+struct lab_wl_resource_addon *lab_resource_addon_create(
+       struct lab_transaction_session_context *ctx);
+
+/*
+ * A generic transaction operation attached to
+ * a session context transaction operation list.
+ *
+ * All arguments other than the context are user defined.
+ * Use of an enum for pending_change is suggested.
+ *
+ * The client is responsible for eventually freeing the data
+ * passed in the void *src and *data arguments by listening
+ * to the events.destroy signal. The transaction operations can be
+ * looped through by using lab_transaction_for_each(trans_op, ctx).
+ */
+struct lab_transaction_op *lab_transaction_op_add(
+       struct lab_transaction_session_context *ctx,
+       uint32_t pending_change, void *src, void *data);
+
+/*
+ * Removes the transaction operation from the ctx list and frees it.
+ *
+ * Does *not* free any passed in src or data arguments.
+ * Use the events.destroy signal for that if necessary.
+ */
+void lab_transaction_op_destroy(struct lab_transaction_op *transaction_op);
+
+/*
+ * Destroys the addon.
+ *
+ * The context refcount is decreased by one. If it reaches
+ * zero the context will be free'd alongside the addon itself.
+ * If the context is destroyed all pending transaction operations
+ * are destroyed as well.
+ */
+void lab_resource_addon_destroy(struct lab_wl_resource_addon *addon);
+
+/* Convinience wrappers for looping through the pending transaction ops of a ctx */
+#define lab_transaction_for_each(transaction_op, ctx) \
+       wl_list_for_each(transaction_op, &(ctx)->transaction_ops, link)
+
+#define lab_transaction_for_each_safe(trans_op, trans_op_tmp, ctx) \
+       wl_list_for_each_safe(trans_op, trans_op_tmp, &(ctx)->transaction_ops, link)
+
+#endif /* LABWC_PROTOCOLS_TRANSACTIONS_ADDON_H */
index 83d9ed2fccd5b92495c3a2d9b119d80ab289a864..dc638d249977221f997facbdb9adc8471bff07be 100644 (file)
@@ -8,6 +8,7 @@
 #include "cosmic-workspace-unstable-v1-protocol.h"
 #include "protocols/cosmic-workspaces.h"
 #include "protocols/cosmic-workspaces-internal.h"
+#include "protocols/transaction-addon.h"
 
 /*
  *     .--------------------.
@@ -49,6 +50,13 @@ enum workspace_state {
        CW_WS_STATE_INVALID = 1 << 31,
 };
 
+struct ws_create_workspace_event {
+       char *name;
+       struct {
+               struct wl_listener transaction_op_destroy;
+       } on;
+};
+
 static void
 add_caps(struct wl_array *caps_arr, uint32_t caps)
 {
@@ -79,37 +87,40 @@ workspace_handle_destroy(struct wl_client *client, struct wl_resource *resource)
 static void
 workspace_handle_activate(struct wl_client *client, struct wl_resource *resource)
 {
-       struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
+       struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
        if (!addon) {
                /* workspace was destroyed from the compositor side */
                return;
        }
        struct lab_cosmic_workspace *workspace = addon->data;
-       transaction_add_workspace_ev(workspace, resource, CW_PENDING_WS_ACTIVATE);
+       lab_transaction_op_add(addon->ctx, CW_PENDING_WS_ACTIVATE,
+               workspace, /*data*/ NULL);
 }
 
 static void
 workspace_handle_deactivate(struct wl_client *client, struct wl_resource *resource)
 {
-       struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
+       struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
        if (!addon) {
                /* Workspace was destroyed from the compositor side */
                return;
        }
        struct lab_cosmic_workspace *workspace = addon->data;
-       transaction_add_workspace_ev(workspace, resource, CW_PENDING_WS_DEACTIVATE);
+       lab_transaction_op_add(addon->ctx, CW_PENDING_WS_DEACTIVATE,
+               workspace, /*data*/ NULL);
 }
 
 static void
 workspace_handle_remove(struct wl_client *client, struct wl_resource *resource)
 {
-       struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
+       struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
        if (!addon) {
                /* workspace was destroyed from the compositor side */
                return;
        }
        struct lab_cosmic_workspace *workspace = addon->data;
-       transaction_add_workspace_ev(workspace, resource, CW_PENDING_WS_REMOVE);
+       lab_transaction_op_add(addon->ctx, CW_PENDING_WS_REMOVE,
+               workspace, /*data*/ NULL);
 }
 
 static const struct zcosmic_workspace_handle_v1_interface workspace_impl = {
@@ -122,9 +133,9 @@ static const struct zcosmic_workspace_handle_v1_interface workspace_impl = {
 static void
 workspace_instance_resource_destroy(struct wl_resource *resource)
 {
-       struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
+       struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
        if (addon) {
-               resource_addon_destroy(addon);
+               lab_resource_addon_destroy(addon);
                wl_resource_set_user_data(resource, NULL);
        }
 
@@ -133,7 +144,7 @@ workspace_instance_resource_destroy(struct wl_resource *resource)
 
 static struct wl_resource *
 workspace_resource_create(struct lab_cosmic_workspace *workspace,
-               struct wl_resource *group_resource, struct session_context *ctx)
+               struct wl_resource *group_resource, struct lab_transaction_session_context *ctx)
 {
        struct wl_client *client = wl_resource_get_client(group_resource);
        struct wl_resource *resource = wl_resource_create(client,
@@ -144,7 +155,7 @@ workspace_resource_create(struct lab_cosmic_workspace *workspace,
                return NULL;
        }
 
-       struct wl_resource_addon *addon = resource_addon_create(ctx);
+       struct lab_wl_resource_addon *addon = lab_resource_addon_create(ctx);
        addon->data = workspace;
 
        wl_resource_set_implementation(resource, &workspace_impl, addon,
@@ -212,17 +223,35 @@ workspace_set_state(struct lab_cosmic_workspace *workspace,
 }
 
 /* Group */
+static void
+ws_create_workspace_handle_transaction_op_destroy(struct wl_listener *listener, void *data)
+{
+       struct ws_create_workspace_event *ev =
+               wl_container_of(listener, ev, on.transaction_op_destroy);
+       wl_list_remove(&ev->on.transaction_op_destroy.link);
+       free(ev->name);
+       free(ev);
+}
+
 static void
 group_handle_create_workspace(struct wl_client *client,
                struct wl_resource *resource, const char *name)
 {
-       struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
+       struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
        if (!addon) {
                return;
        }
 
        struct lab_cosmic_workspace_group *group = addon->data;
-       transaction_add_workspace_group_ev(group, resource, CW_PENDING_WS_CREATE, name);
+       struct ws_create_workspace_event *ev = znew(*ev);
+       ev->name = xstrdup(name);
+
+       struct lab_transaction_op *transaction_op = lab_transaction_op_add(
+               addon->ctx, CW_PENDING_WS_CREATE, group, ev);
+
+       ev->on.transaction_op_destroy.notify =
+               ws_create_workspace_handle_transaction_op_destroy;
+       wl_signal_add(&transaction_op->events.destroy, &ev->on.transaction_op_destroy);
 }
 
 static void
@@ -239,9 +268,9 @@ static const struct zcosmic_workspace_group_handle_v1_interface group_impl = {
 static void
 group_instance_resource_destroy(struct wl_resource *resource)
 {
-       struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
+       struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
        if (addon) {
-               resource_addon_destroy(addon);
+               lab_resource_addon_destroy(addon);
                wl_resource_set_user_data(resource, NULL);
        }
        wl_list_remove(wl_resource_get_link(resource));
@@ -249,7 +278,7 @@ group_instance_resource_destroy(struct wl_resource *resource)
 
 static struct wl_resource *
 group_resource_create(struct lab_cosmic_workspace_group *group,
-               struct wl_resource *manager_resource, struct session_context *ctx)
+               struct wl_resource *manager_resource, struct lab_transaction_session_context *ctx)
 {
        struct wl_client *client = wl_resource_get_client(manager_resource);
        struct wl_resource *resource = wl_resource_create(client,
@@ -260,7 +289,7 @@ group_resource_create(struct lab_cosmic_workspace_group *group,
                return NULL;
        }
 
-       struct wl_resource_addon *addon = resource_addon_create(ctx);
+       struct lab_wl_resource_addon *addon = lab_resource_addon_create(ctx);
        addon->data = group;
 
        wl_resource_set_implementation(resource, &group_impl, addon,
@@ -284,40 +313,38 @@ group_send_state(struct lab_cosmic_workspace_group *group, struct wl_resource *r
 static void
 manager_handle_commit(struct wl_client *client, struct wl_resource *resource)
 {
-       struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
+       struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
        if (!addon) {
                return;
        }
 
-       struct transaction_group *trans_grp;
-       struct transaction_workspace *trans_ws;
-       struct transaction *trans, *trans_tmp;
-       wl_list_for_each_safe(trans, trans_tmp, &addon->ctx->transactions, link) {
-               switch (trans->change) {
+       struct lab_cosmic_workspace *workspace;
+       struct lab_cosmic_workspace_group *group;
+       struct lab_transaction_op *trans_op, *trans_op_tmp;
+       lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) {
+               switch (trans_op->change) {
                case CW_PENDING_WS_CREATE:
-                       trans_grp = wl_container_of(trans, trans_grp, base);
-                       wl_signal_emit_mutable(
-                               &trans_grp->group->events.create_workspace,
-                               trans_grp->new_workspace_name);
-                       free(trans_grp->new_workspace_name);
+                       group = trans_op->src;
+                       struct ws_create_workspace_event *ev = trans_op->data;
+                       wl_signal_emit_mutable(&group->events.create_workspace, ev->name);
                        break;
                case CW_PENDING_WS_ACTIVATE:
-                       trans_ws = wl_container_of(trans, trans_ws, base);
-                       wl_signal_emit_mutable(&trans_ws->workspace->events.activate, NULL);
+                       workspace = trans_op->src;
+                       wl_signal_emit_mutable(&workspace->events.activate, NULL);
                        break;
                case CW_PENDING_WS_DEACTIVATE:
-                       trans_ws = wl_container_of(trans, trans_ws, base);
-                       wl_signal_emit_mutable(&trans_ws->workspace->events.deactivate, NULL);
+                       workspace = trans_op->src;
+                       wl_signal_emit_mutable(&workspace->events.deactivate, NULL);
                        break;
                case CW_PENDING_WS_REMOVE:
-                       trans_ws = wl_container_of(trans, trans_ws, base);
-                       wl_signal_emit_mutable(&trans_ws->workspace->events.remove, NULL);
+                       workspace = trans_op->src;
+                       wl_signal_emit_mutable(&workspace->events.remove, NULL);
                        break;
                default:
-                       wlr_log(WLR_ERROR, "Invalid transaction state: %u", trans->change);
+                       wlr_log(WLR_ERROR, "Invalid transaction state: %u", trans_op->change);
                }
-               wl_list_remove(&trans->link);
-               free(trans);
+
+               lab_transaction_op_destroy(trans_op);
        }
 }
 
@@ -336,9 +363,9 @@ static const struct zcosmic_workspace_manager_v1_interface manager_impl = {
 static void
 manager_instance_resource_destroy(struct wl_resource *resource)
 {
-       struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
+       struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
        if (addon) {
-               resource_addon_destroy(addon);
+               lab_resource_addon_destroy(addon);
                wl_resource_set_user_data(resource, NULL);
        }
 
@@ -358,7 +385,7 @@ manager_handle_bind(struct wl_client *client, void *data,
                return;
        }
 
-       struct wl_resource_addon *addon = resource_addon_create(/* session context*/ NULL);
+       struct lab_wl_resource_addon *addon = lab_resource_addon_create(/* session context*/ NULL);
        addon->data = manager;
 
        wl_resource_set_implementation(resource, &manager_impl,
@@ -495,7 +522,7 @@ lab_cosmic_workspace_group_create(struct lab_cosmic_workspace_manager *manager)
 
        struct wl_resource *resource, *tmp;
        wl_resource_for_each_safe(resource, tmp, &manager->resources) {
-               struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
+               struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
                assert(addon && addon->ctx);
                struct wl_resource *group_resource =
                        group_resource_create(group, resource, addon->ctx);
@@ -522,9 +549,9 @@ lab_cosmic_workspace_group_destroy(struct lab_cosmic_workspace_group *group)
 
        struct wl_resource *resource, *res_tmp;
        wl_resource_for_each_safe(resource, res_tmp, &group->resources) {
-               struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
+               struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
                if (addon) {
-                       resource_addon_destroy(addon);
+                       lab_resource_addon_destroy(addon);
                        wl_resource_set_user_data(resource, NULL);
                }
                zcosmic_workspace_group_handle_v1_send_remove(resource);
@@ -532,6 +559,20 @@ lab_cosmic_workspace_group_destroy(struct lab_cosmic_workspace_group *group)
                wl_list_init(wl_resource_get_link(resource));
        }
 
+       /* Cancel pending transaction operations involving this group */
+       struct lab_transaction_op *trans_op, *trans_op_tmp;
+       wl_resource_for_each(resource, &group->manager->resources) {
+               struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
+               if (!addon) {
+                       continue;
+               }
+               lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) {
+                       if (trans_op->src == group) {
+                               lab_transaction_op_destroy(trans_op);
+                       }
+               }
+       }
+
        wl_list_remove(&group->link);
        wl_array_release(&group->capabilities);
        free(group);
@@ -569,7 +610,7 @@ lab_cosmic_workspace_create(struct lab_cosmic_workspace_group *group)
        /* Notify clients */
        struct wl_resource *group_resource;
        wl_resource_for_each(group_resource, &group->resources) {
-               struct wl_resource_addon *addon = wl_resource_get_user_data(group_resource);
+               struct lab_wl_resource_addon *addon = wl_resource_get_user_data(group_resource);
                assert(addon && addon->ctx);
                struct wl_resource *workspace_resource =
                        workspace_resource_create(workspace, group_resource, addon->ctx);
@@ -642,9 +683,9 @@ lab_cosmic_workspace_destroy(struct lab_cosmic_workspace *workspace)
 
        struct wl_resource *resource, *tmp;
        wl_resource_for_each_safe(resource, tmp, &workspace->resources) {
-               struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
+               struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
                if (addon) {
-                       resource_addon_destroy(addon);
+                       lab_resource_addon_destroy(addon);
                        wl_resource_set_user_data(resource, NULL);
                }
                zcosmic_workspace_handle_v1_send_remove(resource);
@@ -653,6 +694,20 @@ lab_cosmic_workspace_destroy(struct lab_cosmic_workspace *workspace)
        }
        manager_schedule_done_event(workspace->group->manager);
 
+       /* Cancel pending transaction operations involving this workspace */
+       struct lab_transaction_op *trans_op, *trans_op_tmp;
+       wl_resource_for_each(resource, &workspace->group->manager->resources) {
+               struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
+               if (!addon) {
+                       continue;
+               }
+               lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) {
+                       if (trans_op->src == workspace) {
+                               lab_transaction_op_destroy(trans_op);
+                       }
+               }
+       }
+
        wl_list_remove(&workspace->link);
        wl_array_release(&workspace->coordinates);
        wl_array_release(&workspace->capabilities);
index 29bb80cab9a4ff6623d5537456bab3945723a3bc..31ce18b8a525f5deeb8d54a5df0dd6e9dd533389 100644 (file)
@@ -1,5 +1,4 @@
 labwc_sources += files(
   'cosmic-workspaces.c',
-  'transactions.c',
   'output.c',
 )
diff --git a/src/protocols/cosmic_workspaces/transactions.c b/src/protocols/cosmic_workspaces/transactions.c
deleted file mode 100644 (file)
index 571b2c1..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#include <assert.h>
-#include <wayland-server-core.h>
-#include <wlr/util/log.h>
-#include "common/list.h"
-#include "common/mem.h"
-#include "protocols/cosmic-workspaces-internal.h"
-
-static void
-transactions_destroy(struct wl_list *list)
-{
-       struct transaction_group *group;
-       struct transaction *trans, *trans_tmp;
-       wl_list_for_each_safe(trans, trans_tmp, list, link) {
-               if (trans->change == CW_PENDING_WS_CREATE) {
-                       group = wl_container_of(trans, group, base);
-                       free(group->new_workspace_name);
-               }
-               wl_list_remove(&trans->link);
-               free(trans);
-       }
-}
-
-void
-resource_addon_destroy(struct wl_resource_addon *addon)
-{
-       assert(addon);
-       assert(addon->ctx);
-
-       addon->ctx->ref_count--;
-       assert(addon->ctx->ref_count >= 0);
-
-       wlr_log(WLR_DEBUG, "New refcount for session %p: %d",
-               addon->ctx, addon->ctx->ref_count);
-       if (!addon->ctx->ref_count) {
-               wlr_log(WLR_DEBUG, "Destroying session context");
-               transactions_destroy(&addon->ctx->transactions);
-               free(addon->ctx);
-       }
-
-       free(addon);
-}
-
-struct wl_resource_addon *
-resource_addon_create(struct session_context *ctx)
-{
-       struct wl_resource_addon *addon = znew(*addon);
-       if (!ctx) {
-               ctx = znew(*ctx);
-               wl_list_init(&ctx->transactions);
-       }
-       addon->ctx = ctx;
-       addon->ctx->ref_count++;
-       return addon;
-}
-
-void
-transaction_add_workspace_ev(struct lab_cosmic_workspace *ws,
-               struct wl_resource *resource, enum pending_change change)
-{
-       struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
-       if (!addon) {
-               wlr_log(WLR_ERROR, "Failed to find manager addon for workspace transaction");
-               return;
-       }
-
-       assert(change != CW_PENDING_WS_CREATE);
-
-       struct transaction_workspace *trans_ws = znew(*trans_ws);
-       trans_ws->workspace = ws;
-       trans_ws->base.change = change;
-       wl_list_append(&addon->ctx->transactions, &trans_ws->base.link);
-}
-
-void
-transaction_add_workspace_group_ev(struct lab_cosmic_workspace_group *group,
-               struct wl_resource *resource, enum pending_change change,
-               const char *new_workspace_name)
-{
-       struct wl_resource_addon *addon = wl_resource_get_user_data(resource);
-       if (!addon) {
-               wlr_log(WLR_ERROR, "Failed to find manager addon for group transaction");
-               return;
-       }
-
-       assert(change == CW_PENDING_WS_CREATE);
-
-       struct transaction_group *trans_grp = znew(*trans_grp);
-       trans_grp->group = group;
-       trans_grp->base.change = change;
-       trans_grp->new_workspace_name = xstrdup(new_workspace_name);
-       wl_list_append(&addon->ctx->transactions, &trans_grp->base.link);
-}
index d4412119b0082d3a1249fa44c7e3597e818c0782..093b1961632216dd51c5a0b85f8b835b1a19ab4a 100644 (file)
@@ -1 +1,5 @@
+labwc_sources += files(
+  'transaction-addon.c',
+)
+
 subdir('cosmic_workspaces')
diff --git a/src/protocols/transaction-addon.c b/src/protocols/transaction-addon.c
new file mode 100644 (file)
index 0000000..c467849
--- /dev/null
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <assert.h>
+#include <wayland-server-core.h>
+#include "common/list.h"
+#include "common/mem.h"
+#include "protocols/transaction-addon.h"
+
+void
+lab_transaction_op_destroy(struct lab_transaction_op *trans_op)
+{
+       wl_signal_emit_mutable(&trans_op->events.destroy, trans_op);
+       wl_list_remove(&trans_op->link);
+       free(trans_op);
+}
+
+static void
+transaction_destroy(struct wl_list *list)
+{
+       struct lab_transaction_op *trans_op, *trans_op_tmp;
+       wl_list_for_each_safe(trans_op, trans_op_tmp, list, link) {
+               lab_transaction_op_destroy(trans_op);
+       }
+}
+
+void
+lab_resource_addon_destroy(struct lab_wl_resource_addon *addon)
+{
+       assert(addon);
+       assert(addon->ctx);
+
+       addon->ctx->ref_count--;
+       assert(addon->ctx->ref_count >= 0);
+
+       if (!addon->ctx->ref_count) {
+               transaction_destroy(&addon->ctx->transaction_ops);
+               free(addon->ctx);
+       }
+
+       free(addon);
+}
+
+struct lab_wl_resource_addon *
+lab_resource_addon_create(struct lab_transaction_session_context *ctx)
+{
+       struct lab_wl_resource_addon *addon = znew(*addon);
+       if (!ctx) {
+               ctx = znew(*ctx);
+               wl_list_init(&ctx->transaction_ops);
+       }
+       addon->ctx = ctx;
+       addon->ctx->ref_count++;
+       return addon;
+}
+
+struct lab_transaction_op *
+lab_transaction_op_add(struct lab_transaction_session_context *ctx,
+               uint32_t pending_change, void *src, void *data)
+{
+       assert(ctx);
+
+       struct lab_transaction_op *trans_op = znew(*trans_op);
+       trans_op->change = pending_change;
+       trans_op->src = src;
+       trans_op->data = data;
+
+       wl_signal_init(&trans_op->events.destroy);
+       wl_list_append(&ctx->transaction_ops, &trans_op->link);
+
+       return trans_op;
+}