diff --git a/include/protocols/cosmic-workspaces-internal.h b/include/protocols/cosmic-workspaces-internal.h index 36556bc6..ce977d8b 100644 --- a/include/protocols/cosmic-workspaces-internal.h +++ b/include/protocols/cosmic-workspaces-internal.h @@ -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 index 00000000..64383ba9 --- /dev/null +++ b/include/protocols/transaction-addon.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_PROTOCOLS_TRANSACTION_ADDON_H +#define LABWC_PROTOCOLS_TRANSACTION_ADDON_H + +#include + +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 */ diff --git a/src/protocols/cosmic_workspaces/cosmic-workspaces.c b/src/protocols/cosmic_workspaces/cosmic-workspaces.c index 83d9ed2f..dc638d24 100644 --- a/src/protocols/cosmic_workspaces/cosmic-workspaces.c +++ b/src/protocols/cosmic_workspaces/cosmic-workspaces.c @@ -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); diff --git a/src/protocols/cosmic_workspaces/meson.build b/src/protocols/cosmic_workspaces/meson.build index 29bb80ca..31ce18b8 100644 --- a/src/protocols/cosmic_workspaces/meson.build +++ b/src/protocols/cosmic_workspaces/meson.build @@ -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 index 571b2c11..00000000 --- a/src/protocols/cosmic_workspaces/transactions.c +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include -#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); -} diff --git a/src/protocols/meson.build b/src/protocols/meson.build index d4412119..093b1961 100644 --- a/src/protocols/meson.build +++ b/src/protocols/meson.build @@ -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 index 00000000..c4678497 --- /dev/null +++ b/src/protocols/transaction-addon.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#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; +}