feat:add ext-workspace protocol file for temp

This commit is contained in:
DreamMaoMao 2025-08-26 15:59:35 +08:00
parent aaae66113d
commit d7fdaa42a3
5 changed files with 1042 additions and 1 deletions

View file

@ -86,6 +86,7 @@ endif
executable('mango',
'src/mango.c',
'src/common/util.c',
'src/ext-protocol/wlr_ext_workspace_v1.c',
wayland_sources,
dependencies : [
libm,

View file

@ -18,6 +18,7 @@ wayland_xmls = [
wl_protocol_dir + '/staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml',
wl_protocol_dir + '/staging/ext-image-capture-source/ext-image-capture-source-v1.xml',
wl_protocol_dir + '/staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml',
wl_protocol_dir + '/staging/ext-workspace/ext-workspace-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml',
'dwl-ipc-unstable-v2.xml',
'wlr-layer-shell-unstable-v1.xml',

View file

@ -1,4 +1,4 @@
#include <wlr/types/wlr_ext_workspace_v1.h>
#include "wlr_ext_workspace_v1.h"
typedef struct Monitor Monitor;

View file

@ -0,0 +1,930 @@
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "wlr_ext_workspace_v1.h"
#include <wlr/types/wlr_output.h>
#include "ext-workspace-v1-protocol.h"
#define EXT_WORKSPACE_V1_VERSION 1
enum wlr_ext_workspace_v1_request_type {
WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE,
WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE,
WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE,
WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN,
WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE,
};
struct wlr_ext_workspace_v1_request {
enum wlr_ext_workspace_v1_request_type type;
// CREATE_WORKSPACE
char *name;
// CREATE_WORKSPACE / ASSIGN
struct wlr_ext_workspace_group_handle_v1 *group;
// ACTIVATE / DEACTIVATE / ASSIGN / REMOVE
struct wlr_ext_workspace_handle_v1 *workspace;
struct wl_list link; // wlr_ext_workspace_manager_client_v1.requests
};
struct wlr_ext_workspace_v1_group_output {
struct wlr_output *output;
struct wlr_ext_workspace_group_handle_v1 *group;
struct wl_listener output_bind;
struct wl_listener output_destroy;
struct wl_list link;
};
// These structs wrap wl_resource of each interface to access the request queue
// (wlr_ext_workspace_manager_client_v1.requests) assigned per manager resource
struct wlr_ext_workspace_manager_client_v1 {
struct wl_resource *resource;
struct wlr_ext_workspace_manager_v1 *manager;
struct wl_list requests; // wlr_ext_workspace_v1_request.link
struct wl_list link; // wlr_ext_workspace_manager_v1.clients
};
struct wlr_ext_workspace_group_client_v1 {
struct wl_resource *resource;
struct wlr_ext_workspace_group_handle_v1 *group;
struct wlr_ext_workspace_manager_client_v1 *manager;
struct wl_list link; // wlr_ext_workspace_group_v1.clients
};
struct wlr_ext_workspace_client_v1 {
struct wl_resource *resource;
struct wlr_ext_workspace_handle_v1 *workspace;
struct wlr_ext_workspace_manager_client_v1 *manager;
struct wl_list link; // wlr_ext_workspace_v1.clients
};
static const struct ext_workspace_group_handle_v1_interface group_impl;
static struct wlr_ext_workspace_group_client_v1 *group_client_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&ext_workspace_group_handle_v1_interface, &group_impl));
return wl_resource_get_user_data(resource);
}
static const struct ext_workspace_handle_v1_interface workspace_impl;
static struct wlr_ext_workspace_client_v1 *workspace_client_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&ext_workspace_handle_v1_interface, &workspace_impl));
return wl_resource_get_user_data(resource);
}
static const struct ext_workspace_manager_v1_interface manager_impl;
static struct wlr_ext_workspace_manager_client_v1 *manager_client_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&ext_workspace_manager_v1_interface, &manager_impl));
return wl_resource_get_user_data(resource);
}
static void workspace_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void workspace_handle_activate(struct wl_client *client,
struct wl_resource *workspace_resource) {
struct wlr_ext_workspace_client_v1 *workspace =
workspace_client_from_resource(workspace_resource);
if (!workspace) {
return;
}
struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
if (!req) {
wl_resource_post_no_memory(workspace_resource);
return;
}
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE;
req->workspace = workspace->workspace;
wl_list_insert(workspace->manager->requests.prev, &req->link);
}
static void workspace_handle_deactivate(struct wl_client *client,
struct wl_resource *workspace_resource) {
struct wlr_ext_workspace_client_v1 *workspace =
workspace_client_from_resource(workspace_resource);
if (!workspace) {
return;
}
struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
if (!req) {
wl_resource_post_no_memory(workspace_resource);
return;
}
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE;
req->workspace = workspace->workspace;
wl_list_insert(workspace->manager->requests.prev, &req->link);
}
static void workspace_handle_assign(struct wl_client *client,
struct wl_resource *workspace_resource,
struct wl_resource *group_resource) {
struct wlr_ext_workspace_client_v1 *workspace =
workspace_client_from_resource(workspace_resource);
struct wlr_ext_workspace_group_client_v1 *group =
group_client_from_resource(group_resource);
if (!workspace || !group) {
return;
}
struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
if (!req) {
wl_resource_post_no_memory(workspace_resource);
return;
}
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN;
req->group = group->group;
req->workspace = workspace->workspace;
wl_list_insert(workspace->manager->requests.prev, &req->link);
}
static void workspace_handle_remove(struct wl_client *client,
struct wl_resource *workspace_resource) {
struct wlr_ext_workspace_client_v1 *workspace =
workspace_client_from_resource(workspace_resource);
if (!workspace) {
return;
}
struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
if (!req) {
wl_resource_post_no_memory(workspace_resource);
return;
}
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE;
req->workspace = workspace->workspace;
wl_list_insert(workspace->manager->requests.prev, &req->link);
}
static const struct ext_workspace_handle_v1_interface workspace_impl = {
.destroy = workspace_handle_destroy,
.activate = workspace_handle_activate,
.deactivate = workspace_handle_deactivate,
.assign = workspace_handle_assign,
.remove = workspace_handle_remove,
};
static void group_handle_create_workspace(struct wl_client *client,
struct wl_resource *group_resource, const char *name) {
struct wlr_ext_workspace_group_client_v1 *group =
group_client_from_resource(group_resource);
if (!group) {
return;
}
struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
if (!req) {
wl_resource_post_no_memory(group_resource);
return;
}
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE;
req->group = group->group;
wl_list_insert(group->manager->requests.prev, &req->link);
}
static void group_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static const struct ext_workspace_group_handle_v1_interface group_impl = {
.create_workspace = group_handle_create_workspace,
.destroy = group_handle_destroy,
};
static void destroy_workspace_client(
struct wlr_ext_workspace_client_v1 *workspace_client) {
wl_list_remove(&workspace_client->link);
wl_resource_set_user_data(workspace_client->resource, NULL);
free(workspace_client);
}
static void workspace_resource_destroy(struct wl_resource *resource) {
struct wlr_ext_workspace_client_v1 *workspace_client =
workspace_client_from_resource(resource);
if (workspace_client) {
destroy_workspace_client(workspace_client);
}
}
static struct wlr_ext_workspace_client_v1 *create_workspace_client(
struct wlr_ext_workspace_handle_v1 *workspace,
struct wlr_ext_workspace_manager_client_v1 *manager_client) {
struct wlr_ext_workspace_client_v1 *workspace_client =
calloc(1, sizeof(*workspace_client));
if (!workspace_client) {
return NULL;
}
struct wl_client *client = wl_resource_get_client(manager_client->resource);
workspace_client->resource = wl_resource_create(client,
&ext_workspace_handle_v1_interface,
wl_resource_get_version(manager_client->resource), 0);
if (!workspace_client->resource) {
free(workspace_client);
return NULL;
}
wl_resource_set_implementation(workspace_client->resource,
&workspace_impl, workspace_client, workspace_resource_destroy);
workspace_client->workspace = workspace;
workspace_client->manager = manager_client;
wl_list_insert(&workspace->clients, &workspace_client->link);
return workspace_client;
}
static void destroy_group_client(struct wlr_ext_workspace_group_client_v1 *group_client) {
wl_list_remove(&group_client->link);
wl_resource_set_user_data(group_client->resource, NULL);
free(group_client);
}
static void group_handle_resource_destroy(struct wl_resource *resource) {
struct wlr_ext_workspace_group_client_v1 *group_client =
group_client_from_resource(resource);
if (group_client) {
destroy_group_client(group_client);
}
}
static struct wlr_ext_workspace_group_client_v1 *create_group_client(
struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_ext_workspace_manager_client_v1 *manager_client) {
struct wlr_ext_workspace_group_client_v1 *group_client =
calloc(1, sizeof(*group_client));
if (!group_client) {
return NULL;
}
struct wl_client *client = wl_resource_get_client(manager_client->resource);
uint32_t version = wl_resource_get_version(manager_client->resource);
group_client->resource = wl_resource_create(client,
&ext_workspace_group_handle_v1_interface, version, 0);
if (group_client->resource == NULL) {
free(group_client);
return NULL;
}
wl_resource_set_implementation(group_client->resource, &group_impl,
group_client, group_handle_resource_destroy);
group_client->group = group;
group_client->manager = manager_client;
wl_list_insert(&group->clients, &group_client->link);
return group_client;
}
static void destroy_request(struct wlr_ext_workspace_v1_request *req) {
wl_list_remove(&req->link);
free(req->name);
free(req);
}
static void manager_handle_commit(struct wl_client *client, struct wl_resource *resource) {
struct wlr_ext_workspace_manager_client_v1 *manager =
manager_client_from_resource(resource);
if (!manager) {
return;
}
struct wlr_ext_workspace_v1_request *req, *tmp;
wl_list_for_each_safe(req, tmp, &manager->requests, link) {
switch (req->type) {
case WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE:
wl_signal_emit_mutable(
&req->group->events.create_workspace, req->name);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE:
wl_signal_emit_mutable(
&req->workspace->events.activate, NULL);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE:
wl_signal_emit_mutable(
&req->workspace->events.deactivate, NULL);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN:
wl_signal_emit_mutable(
&req->workspace->events.assign, &req->group);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE:
wl_signal_emit_mutable(
&req->workspace->events.remove, NULL);
break;
default:
abort();
}
destroy_request(req);
}
}
static void handle_idle(void *data) {
struct wlr_ext_workspace_manager_v1 *manager = data;
struct wlr_ext_workspace_manager_client_v1 *manager_client;
wl_list_for_each(manager_client, &manager->clients, link) {
ext_workspace_manager_v1_send_done(manager_client->resource);
}
manager->idle_source = NULL;
}
static void manager_schedule_done(struct wlr_ext_workspace_manager_v1 *manager) {
if (!manager->idle_source) {
manager->idle_source = wl_event_loop_add_idle(manager->event_loop,
handle_idle, manager);
}
}
static void workspace_send_details(struct wlr_ext_workspace_client_v1 *workspace_client) {
struct wlr_ext_workspace_handle_v1 *workspace = workspace_client->workspace;
struct wl_resource *resource = workspace_client->resource;
ext_workspace_handle_v1_send_capabilities(resource, workspace->caps);
if (workspace->coordinates.size > 0) {
ext_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates);
}
if (workspace->name) {
ext_workspace_handle_v1_send_name(resource, workspace->name);
}
if (workspace->id) {
ext_workspace_handle_v1_send_id(resource, workspace->id);
}
ext_workspace_handle_v1_send_state(resource, workspace->state);
manager_schedule_done(workspace->manager);
}
static void manager_handle_stop(struct wl_client *client, struct wl_resource *resource) {
ext_workspace_manager_v1_send_finished(resource);
wl_resource_destroy(resource);
}
static const struct ext_workspace_manager_v1_interface manager_impl = {
.commit = manager_handle_commit,
.stop = manager_handle_stop,
};
static void destroy_manager_client(struct wlr_ext_workspace_manager_client_v1 *manager_client) {
struct wlr_ext_workspace_v1_request *req, *tmp;
wl_list_for_each_safe(req, tmp, &manager_client->requests, link) {
destroy_request(req);
}
wl_list_remove(&manager_client->link);
wl_resource_set_user_data(manager_client->resource, NULL);
free(manager_client);
}
static void manager_resource_destroy(struct wl_resource *resource) {
struct wlr_ext_workspace_manager_client_v1 *manager_client =
manager_client_from_resource(resource);
if (manager_client) {
destroy_manager_client(manager_client);
}
}
static void group_send_details(struct wlr_ext_workspace_group_client_v1 *group_client) {
struct wlr_ext_workspace_group_handle_v1 *group = group_client->group;
struct wl_resource *resource = group_client->resource;
struct wl_client *client = wl_resource_get_client(resource);
ext_workspace_group_handle_v1_send_capabilities(resource, group->caps);
struct wlr_ext_workspace_v1_group_output *group_output;
wl_list_for_each(group_output, &group->outputs, link) {
struct wl_resource *output_resource;
wl_resource_for_each(output_resource, &group_output->output->resources) {
if (wl_resource_get_client(output_resource) == client) {
ext_workspace_group_handle_v1_send_output_enter(
resource, output_resource);
}
}
}
manager_schedule_done(group->manager);
}
static void manager_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct wlr_ext_workspace_manager_v1 *manager = data;
struct wlr_ext_workspace_manager_client_v1 *manager_client =
calloc(1, sizeof(*manager_client));
if (!manager_client) {
wl_client_post_no_memory(client);
return;
}
manager_client->manager = manager;
wl_list_init(&manager_client->requests);
wl_list_insert(&manager->clients, &manager_client->link);
manager_client->resource = wl_resource_create(client,
&ext_workspace_manager_v1_interface, version, id);
if (!manager_client->resource) {
free(manager_client);
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(manager_client->resource, &manager_impl,
manager_client, manager_resource_destroy);
struct wlr_ext_workspace_group_handle_v1 *group;
wl_list_for_each(group, &manager->groups, link) {
struct wlr_ext_workspace_group_client_v1 *group_client =
create_group_client(group, manager_client);
if (!group_client) {
wl_resource_post_no_memory(manager_client->resource);
continue;
}
ext_workspace_manager_v1_send_workspace_group(
manager_client->resource, group_client->resource);
group_send_details(group_client);
}
struct wlr_ext_workspace_handle_v1 *workspace;
wl_list_for_each(workspace, &manager->workspaces, link) {
struct wlr_ext_workspace_client_v1 *workspace_client =
create_workspace_client(workspace, manager_client);
if (!workspace) {
wl_client_post_no_memory(client);
continue;
}
ext_workspace_manager_v1_send_workspace(
manager_client->resource, workspace_client->resource);
workspace_send_details(workspace_client);
if (!workspace->group) {
continue;
}
struct wlr_ext_workspace_group_client_v1 *group_client;
wl_list_for_each(group_client, &workspace->group->clients, link) {
if (group_client->manager == manager_client) {
ext_workspace_group_handle_v1_send_workspace_enter(
group_client->resource, workspace_client->resource);
}
}
}
manager_schedule_done(manager);
}
static void manager_handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_ext_workspace_manager_v1 *manager =
wl_container_of(listener, manager, display_destroy);
wl_signal_emit_mutable(&manager->events.destroy, NULL);
assert(wl_list_empty(&manager->events.destroy.listener_list));
struct wlr_ext_workspace_group_handle_v1 *group, *tmp;
wl_list_for_each_safe(group, tmp, &manager->groups, link) {
wlr_ext_workspace_group_handle_v1_destroy(group);
}
struct wlr_ext_workspace_handle_v1 *workspace, *tmp2;
wl_list_for_each_safe(workspace, tmp2, &manager->workspaces, link) {
wlr_ext_workspace_handle_v1_destroy(workspace);
}
struct wlr_ext_workspace_manager_client_v1 *manager_client, *tmp3;
wl_list_for_each_safe(manager_client, tmp3, &manager->clients, link) {
destroy_manager_client(manager_client);
}
if (manager->idle_source) {
wl_event_source_remove(manager->idle_source);
}
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
free(manager);
}
struct wlr_ext_workspace_manager_v1 *wlr_ext_workspace_manager_v1_create(
struct wl_display *display, uint32_t version) {
assert(version <= EXT_WORKSPACE_V1_VERSION);
struct wlr_ext_workspace_manager_v1 *manager = calloc(1, sizeof(*manager));
if (!manager) {
return NULL;
}
manager->global = wl_global_create(display,
&ext_workspace_manager_v1_interface,
version, manager, manager_bind);
if (!manager->global) {
free(manager);
return NULL;
}
manager->event_loop = wl_display_get_event_loop(display);
manager->display_destroy.notify = manager_handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->display_destroy);
wl_list_init(&manager->groups);
wl_list_init(&manager->workspaces);
wl_list_init(&manager->clients);
wl_signal_init(&manager->events.destroy);
return manager;
}
struct wlr_ext_workspace_group_handle_v1 *wlr_ext_workspace_group_handle_v1_create(
struct wlr_ext_workspace_manager_v1 *manager, uint32_t caps) {
struct wlr_ext_workspace_group_handle_v1 *group = calloc(1, sizeof(*group));
if (!group) {
return NULL;
}
group->manager = manager;
group->caps = caps;
wl_list_init(&group->outputs);
wl_list_init(&group->clients);
wl_signal_init(&group->events.create_workspace);
wl_signal_init(&group->events.destroy);
wl_list_insert(manager->groups.prev, &group->link);
struct wlr_ext_workspace_manager_client_v1 *manager_client;
wl_list_for_each(manager_client, &manager->clients, link) {
struct wlr_ext_workspace_group_client_v1 *group_client =
create_group_client(group, manager_client);
if (!group_client) {
continue;
}
ext_workspace_manager_v1_send_workspace_group(
manager_client->resource, group_client->resource);
group_send_details(group_client);
}
manager_schedule_done(manager);
return group;
}
static void workspace_send_group(struct wlr_ext_workspace_handle_v1 *workspace,
struct wlr_ext_workspace_group_handle_v1 *group, bool enter) {
struct wlr_ext_workspace_client_v1 *workspace_client;
wl_list_for_each(workspace_client, &workspace->clients, link) {
struct wlr_ext_workspace_group_client_v1 *group_client;
wl_list_for_each(group_client, &group->clients, link) {
if (group_client->manager != workspace_client->manager) {
continue;
}
if (enter) {
ext_workspace_group_handle_v1_send_workspace_enter(
group_client->resource, workspace_client->resource);
} else {
ext_workspace_group_handle_v1_send_workspace_leave(
group_client->resource, workspace_client->resource);
}
}
}
manager_schedule_done(workspace->manager);
}
static void destroy_group_output(struct wlr_ext_workspace_v1_group_output *group_output) {
wl_list_remove(&group_output->output_bind.link);
wl_list_remove(&group_output->output_destroy.link);
wl_list_remove(&group_output->link);
free(group_output);
}
static void group_send_output(struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_output *output, bool enter) {
struct wlr_ext_workspace_group_client_v1 *group_client;
wl_list_for_each(group_client, &group->clients, link) {
struct wl_client *client =
wl_resource_get_client(group_client->resource);
struct wl_resource *output_resource;
wl_resource_for_each(output_resource, &output->resources) {
if (wl_resource_get_client(output_resource) != client) {
continue;
}
if (enter) {
ext_workspace_group_handle_v1_send_output_enter(
group_client->resource, output_resource);
} else {
ext_workspace_group_handle_v1_send_output_leave(
group_client->resource, output_resource);
}
}
}
manager_schedule_done(group->manager);
}
void wlr_ext_workspace_group_handle_v1_destroy(
struct wlr_ext_workspace_group_handle_v1 *group) {
wl_signal_emit_mutable(&group->events.destroy, NULL);
assert(wl_list_empty(&group->events.create_workspace.listener_list));
assert(wl_list_empty(&group->events.destroy.listener_list));
struct wlr_ext_workspace_handle_v1 *workspace;
wl_list_for_each(workspace, &group->manager->workspaces, link) {
if (workspace->group == group) {
workspace_send_group(workspace, group, false);
workspace->group = NULL;
}
}
struct wlr_ext_workspace_group_client_v1 *group_client, *tmp;
wl_list_for_each_safe(group_client, tmp, &group->clients, link) {
ext_workspace_group_handle_v1_send_removed(group_client->resource);
destroy_group_client(group_client);
}
struct wlr_ext_workspace_manager_client_v1 *manager_client;
wl_list_for_each(manager_client, &group->manager->clients, link) {
struct wlr_ext_workspace_v1_request *req, *tmp2;
wl_list_for_each_safe(req, tmp2, &manager_client->requests, link) {
if (req->group == group) {
destroy_request(req);
}
}
}
struct wlr_ext_workspace_v1_group_output *group_output, *tmp3;
wl_list_for_each_safe(group_output, tmp3, &group->outputs, link) {
group_send_output(group, group_output->output, false);
destroy_group_output(group_output);
}
manager_schedule_done(group->manager);
wl_list_remove(&group->link);
free(group);
}
static void handle_output_bind(struct wl_listener *listener, void *data) {
struct wlr_ext_workspace_v1_group_output *group_output =
wl_container_of(listener, group_output, output_bind);
struct wlr_output_event_bind *event = data;
struct wl_client *client = wl_resource_get_client(event->resource);
struct wlr_ext_workspace_group_client_v1 *group_client;
wl_list_for_each(group_client, &group_output->group->clients, link) {
if (wl_resource_get_client(group_client->resource) == client) {
ext_workspace_group_handle_v1_send_output_enter(
group_client->resource, event->resource);
}
}
manager_schedule_done(group_output->group->manager);
}
static void handle_output_destroy(struct wl_listener *listener, void *data) {
struct wlr_ext_workspace_v1_group_output *group_output =
wl_container_of(listener, group_output, output_destroy);
group_send_output(group_output->group, group_output->output, false);
destroy_group_output(group_output);
}
static struct wlr_ext_workspace_v1_group_output *get_group_output(
struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_output *output) {
struct wlr_ext_workspace_v1_group_output *group_output;
wl_list_for_each(group_output, &group->outputs, link) {
if (group_output->output == output) {
return group_output;
}
}
return NULL;
}
void wlr_ext_workspace_group_handle_v1_output_enter(
struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_output *output) {
if (get_group_output(group, output)) {
return;
}
struct wlr_ext_workspace_v1_group_output *group_output =
calloc(1, sizeof(*group_output));
if (!group_output) {
return;
}
group_output->output = output;
group_output->group = group;
wl_list_insert(&group->outputs, &group_output->link);
group_output->output_bind.notify = handle_output_bind;
wl_signal_add(&output->events.bind, &group_output->output_bind);
group_output->output_destroy.notify = handle_output_destroy;
wl_signal_add(&output->events.destroy, &group_output->output_destroy);
group_send_output(group, output, true);
}
void wlr_ext_workspace_group_handle_v1_output_leave(
struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_output *output) {
struct wlr_ext_workspace_v1_group_output *group_output =
get_group_output(group, output);
if (!group_output) {
return;
}
group_send_output(group, output, false);
destroy_group_output(group_output);
}
struct wlr_ext_workspace_handle_v1 *wlr_ext_workspace_handle_v1_create(
struct wlr_ext_workspace_manager_v1 *manager, const char *id, uint32_t caps) {
struct wlr_ext_workspace_handle_v1 *workspace = calloc(1, sizeof(*workspace));
if (!workspace) {
return NULL;
}
workspace->manager = manager;
workspace->caps = caps;
if (id) {
workspace->id = strdup(id);
if (!workspace->id) {
return NULL;
}
}
wl_list_init(&workspace->clients);
wl_array_init(&workspace->coordinates);
wl_signal_init(&workspace->events.activate);
wl_signal_init(&workspace->events.deactivate);
wl_signal_init(&workspace->events.remove);
wl_signal_init(&workspace->events.assign);
wl_signal_init(&workspace->events.destroy);
wl_list_insert(&manager->workspaces, &workspace->link);
struct wlr_ext_workspace_manager_client_v1 *manager_client;
wl_list_for_each(manager_client, &manager->clients, link) {
struct wlr_ext_workspace_client_v1 *workspace_client =
create_workspace_client(workspace, manager_client);
if (!workspace_client) {
continue;
}
ext_workspace_manager_v1_send_workspace(
manager_client->resource, workspace_client->resource);
workspace_send_details(workspace_client);
}
manager_schedule_done(manager);
return workspace;
}
void wlr_ext_workspace_handle_v1_destroy(struct wlr_ext_workspace_handle_v1 *workspace) {
wl_signal_emit_mutable(&workspace->events.destroy, NULL);
assert(wl_list_empty(&workspace->events.activate.listener_list));
assert(wl_list_empty(&workspace->events.deactivate.listener_list));
assert(wl_list_empty(&workspace->events.remove.listener_list));
assert(wl_list_empty(&workspace->events.assign.listener_list));
assert(wl_list_empty(&workspace->events.destroy.listener_list));
if (workspace->group) {
workspace_send_group(workspace, workspace->group, false);
}
struct wlr_ext_workspace_client_v1 *workspace_client, *tmp;
wl_list_for_each_safe(workspace_client, tmp, &workspace->clients, link) {
ext_workspace_handle_v1_send_removed(workspace_client->resource);
destroy_workspace_client(workspace_client);
}
struct wlr_ext_workspace_manager_client_v1 *manager_client;
wl_list_for_each(manager_client, &workspace->manager->clients, link) {
struct wlr_ext_workspace_v1_request *req, *tmp2;
wl_list_for_each_safe(req, tmp2, &manager_client->requests, link) {
if (req->workspace == workspace) {
destroy_request(req);
}
}
}
manager_schedule_done(workspace->manager);
wl_list_remove(&workspace->link);
wl_array_release(&workspace->coordinates);
free(workspace->id);
free(workspace->name);
free(workspace);
}
void wlr_ext_workspace_handle_v1_set_group(
struct wlr_ext_workspace_handle_v1 *workspace,
struct wlr_ext_workspace_group_handle_v1 *group) {
if (workspace->group == group) {
return;
}
if (workspace->group) {
workspace_send_group(workspace, group, false);
}
workspace->group = group;
if (group) {
workspace_send_group(workspace, group, true);
}
}
void wlr_ext_workspace_handle_v1_set_name(
struct wlr_ext_workspace_handle_v1 *workspace, const char *name) {
assert(name);
if (workspace->name && strcmp(workspace->name, name) == 0) {
return;
}
free(workspace->name);
workspace->name = strdup(name);
if (workspace->name == NULL) {
return;
}
struct wlr_ext_workspace_client_v1 *workspace_client;
wl_list_for_each(workspace_client, &workspace->clients, link) {
ext_workspace_handle_v1_send_name(
workspace_client->resource, workspace->name);
}
manager_schedule_done(workspace->manager);
}
static bool array_equal(struct wl_array *a, struct wl_array *b) {
return (a->size == b->size) &&
(a->size == 0 || memcmp(a->data, b->data, a->size) == 0);
}
void wlr_ext_workspace_handle_v1_set_coordinates(
struct wlr_ext_workspace_handle_v1 *workspace,
struct wl_array *coordinates) {
assert(coordinates);
if (array_equal(&workspace->coordinates, coordinates)) {
return;
}
wl_array_release(&workspace->coordinates);
wl_array_init(&workspace->coordinates);
wl_array_copy(&workspace->coordinates, coordinates);
struct wlr_ext_workspace_client_v1 *workspace_client;
wl_list_for_each(workspace_client, &workspace->clients, link) {
ext_workspace_handle_v1_send_coordinates(
workspace_client->resource, &workspace->coordinates);
}
manager_schedule_done(workspace->manager);
}
static void workspace_set_state(struct wlr_ext_workspace_handle_v1 *workspace,
enum ext_workspace_handle_v1_state state, bool enabled) {
uint32_t old_state = workspace->state;
if (enabled) {
workspace->state |= state;
} else {
workspace->state &= ~state;
}
if (old_state == workspace->state) {
return;
}
struct wlr_ext_workspace_client_v1 *workspace_client;
wl_list_for_each(workspace_client, &workspace->clients, link) {
ext_workspace_handle_v1_send_state(
workspace_client->resource, workspace->state);
}
manager_schedule_done(workspace->manager);
}
void wlr_ext_workspace_handle_v1_set_active(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) {
workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE, enabled);
}
void wlr_ext_workspace_handle_v1_set_urgent(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) {
workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_URGENT, enabled);
}
void wlr_ext_workspace_handle_v1_set_hidden(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) {
workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN, enabled);
}

View file

@ -0,0 +1,109 @@
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_TYPES_WLR_EXT_WORKSPACE_V1_H
#define WLR_TYPES_WLR_EXT_WORKSPACE_V1_H
#include <wayland-server-core.h>
struct wlr_output;
enum wlr_ext_workspace_group_handle_v1_cap {
WLR_EXT_WORKSPACE_GROUP_HANDLE_V1_CAP_CREATE_WORKSPACE = 1 << 0,
};
enum wlr_ext_workspace_handle_v1_cap {
WLR_EXT_WORKSPACE_HANDLE_V1_CAP_ACTIVATE = 1 << 0,
WLR_EXT_WORKSPACE_HANDLE_V1_CAP_DEACTIVATE = 1 << 1,
WLR_EXT_WORKSPACE_HANDLE_V1_CAP_REMOVE = 1 << 2,
WLR_EXT_WORKSPACE_HANDLE_V1_CAP_ASSIGN = 1 << 3,
};
struct wlr_ext_workspace_manager_v1 {
struct wl_global *global;
struct wl_list groups; // wlr_ext_workspace_group_handle_v1.link
struct wl_list workspaces; // wlr_ext_workspace_handle_v1.link
struct {
struct wl_signal destroy;
} events;
struct wl_list clients; // wlr_ext_workspace_manager_client_v1.link
struct wl_event_source *idle_source;
struct wl_event_loop *event_loop;
struct wl_listener display_destroy;
};
struct wlr_ext_workspace_group_handle_v1 {
struct wlr_ext_workspace_manager_v1 *manager;
uint32_t caps; // wlr_ext_workspace_group_handle_v1_cap
struct {
struct wl_signal create_workspace; // const char *
struct wl_signal destroy;
} events;
struct wl_list link; // wlr_ext_workspace_manager_v1.groups
struct wl_list outputs; // wlr_ext_workspace_v1_group_output.link
struct wl_list clients; // wlr_ext_workspace_manager_client_v1.link
};
struct wlr_ext_workspace_handle_v1 {
struct wlr_ext_workspace_manager_v1 *manager;
struct wlr_ext_workspace_group_handle_v1 *group; // May be NULL
char *id;
char *name;
struct wl_array coordinates;
uint32_t caps; // wlr_ext_workspace_handle_v1_cap
uint32_t state; // ext_workspace_handle_v1_state
struct {
struct wl_signal activate;
struct wl_signal deactivate;
struct wl_signal remove;
struct wl_signal assign; // wlr_ext_workspace_group_handle_v1
struct wl_signal destroy;
} events;
struct wl_list link; // wlr_ext_workspace_manager_v1.workspaces;
struct wl_list clients;
};
struct wlr_ext_workspace_manager_v1 *wlr_ext_workspace_manager_v1_create(
struct wl_display *display, uint32_t version);
struct wlr_ext_workspace_group_handle_v1 *wlr_ext_workspace_group_handle_v1_create(
struct wlr_ext_workspace_manager_v1 *manager, uint32_t caps);
void wlr_ext_workspace_group_handle_v1_destroy(
struct wlr_ext_workspace_group_handle_v1 *group);
void wlr_ext_workspace_group_handle_v1_output_enter(
struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output);
void wlr_ext_workspace_group_handle_v1_output_leave(
struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output);
struct wlr_ext_workspace_handle_v1 *wlr_ext_workspace_handle_v1_create(
struct wlr_ext_workspace_manager_v1 *manager, const char *id, uint32_t caps);
void wlr_ext_workspace_handle_v1_destroy(struct wlr_ext_workspace_handle_v1 *workspace);
void wlr_ext_workspace_handle_v1_set_group(
struct wlr_ext_workspace_handle_v1 *workspace,
struct wlr_ext_workspace_group_handle_v1 *group);
void wlr_ext_workspace_handle_v1_set_name(
struct wlr_ext_workspace_handle_v1 *workspace, const char *name);
void wlr_ext_workspace_handle_v1_set_coordinates(
struct wlr_ext_workspace_handle_v1 *workspace, struct wl_array *coordinates);
void wlr_ext_workspace_handle_v1_set_active(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);
void wlr_ext_workspace_handle_v1_set_urgent(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);
void wlr_ext_workspace_handle_v1_set_hidden(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);
#endif