cosmic-workspaces: abstract transaction-addon

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.
This commit is contained in:
Consolatis 2024-11-18 18:38:42 +01:00 committed by Johan Malm
parent afe416f04e
commit 63dc609085
7 changed files with 258 additions and 176 deletions

View 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);

View file

@ -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 */

View 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);

View file

@ -1,5 +1,4 @@
labwc_sources += files(
'cosmic-workspaces.c',
'transactions.c',
'output.c',
)

View file

@ -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);
}

View file

@ -1 +1,5 @@
labwc_sources += files(
'transaction-addon.c',
)
subdir('cosmic_workspaces')

View file

@ -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;
}