--- /dev/null
+/* 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 */
#include "cosmic-workspace-unstable-v1-protocol.h"
#include "protocols/cosmic-workspaces.h"
#include "protocols/cosmic-workspaces-internal.h"
+#include "protocols/transaction-addon.h"
/*
* .--------------------.
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)
{
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 = {
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);
}
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,
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,
}
/* 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
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));
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,
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,
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);
}
}
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);
}
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,
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);
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);
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);
/* 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);
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);
}
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);
+++ /dev/null
-// 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);
-}