view include/ext-workspace.h @ 1:50bab568625d default tip

lol
author tsukasa <qwerasdnoob@gmail.com>
date Sat, 11 Jul 2026 17:56:41 +0800
parents
children
line wrap: on
line source

/* My shitty port of Mango's ext-workspace.h for my compositor; 
 * thanks DreamMaoMao for doing most of the heavy lifting 
 * by actually writing the thing, it didn't work the first 
 * few tries and I barely have any idea how this code works anyway. 
 * To say that it's the eighth wonder of the world 
 * would be an understatement
*/

#include "wlr_ext_workspace_v1.h"

#define EXT_WORKSPACE_CAPS \
	(EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE | \
	 EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_DEACTIVATE)

/* types */

struct ext_workspace {
	struct wl_list link;           
	uint32_t tag;                  
	Monitor *m;
	struct wlr_ext_workspace_handle_v1 *handle;
};

/* global */

static struct wlr_ext_workspace_manager_v1 *ext_workspace_manager;
static struct wl_list ext_workspaces; 
static struct wl_listener ext_commit_listener;
static struct wl_listener ext_destroy_listener;

/* helpers */


static int
get_tag_status(uint32_t tag, Monitor *m)
{
	Client *c;
	uint32_t mask = 1 << (tag - 1);

	wl_list_for_each(c, &clients, link) {
		if (c->mon != m)
			continue;
		if (!(c->tags & mask))
			continue;
		if (c->isurgent)
			return 2;
		return 1;
	}
	return 0;
}

static const char *
tag_name(uint32_t tag)
{
	static const char *names[] = {
		"1", "2", "3", "4", "5", "6", "7", "8", "9",
	};
	if (tag >= 1 && tag <= LENGTH(names))
		return names[tag - 1];
	return "?";
}

/* workspace lifecycle */

static void
ext_workspace_destroy(struct ext_workspace *ws)
{
	wlr_ext_workspace_handle_v1_destroy(ws->handle);
	wl_list_remove(&ws->link);
	free(ws);
}

static void
ext_workspace_create(uint32_t tag, Monitor *m)
{
	const char *name = tag_name(tag);

	struct ext_workspace *ws = ecalloc(1, sizeof(*ws));
	ws->tag = tag;
	ws->m = m;
	ws->handle = wlr_ext_workspace_handle_v1_create(
			ext_workspace_manager, name, EXT_WORKSPACE_CAPS);
	ws->handle->data = ws;

	wlr_ext_workspace_handle_v1_set_group(ws->handle, m->ext_group);
	wlr_ext_workspace_handle_v1_set_name(ws->handle, name);

	wl_list_insert(ext_workspaces.prev, &ws->link);
}

/* monitor integration */

static void
ext_workspace_createmon(Monitor *m)
{
	uint32_t i;

	m->ext_group = wlr_ext_workspace_group_handle_v1_create(
			ext_workspace_manager, 0);
	wlr_ext_workspace_group_handle_v1_output_enter(
			m->ext_group, m->wlr_output);

	for (i = 1; i <= TAGCOUNT; i++)
		ext_workspace_create(i, m);
}

static void
ext_workspace_cleanupmon(Monitor *m)
{
	struct ext_workspace *ws, *tmp;
	wl_list_for_each_safe(ws, tmp, &ext_workspaces, link)
		if (ws->m == m)
			ext_workspace_destroy(ws);

	wlr_ext_workspace_group_handle_v1_output_leave(
			m->ext_group, m->wlr_output);
	wlr_ext_workspace_group_handle_v1_destroy(m->ext_group);
	m->ext_group = NULL;
}

/* status */

static void
ext_workspace_printstatus(Monitor *m)
{
	struct ext_workspace *ws;

	wl_list_for_each(ws, &ext_workspaces, link) {
		if (ws->m != m)
			continue;

		int status = get_tag_status(ws->tag, m);
		int active = !!(m->tagset[m->seltags] & (1 << (ws->tag - 1)) & TAGMASK);

		wlr_ext_workspace_handle_v1_set_urgent(ws->handle, status == 2);
		wlr_ext_workspace_handle_v1_set_hidden(ws->handle, 0);
		wlr_ext_workspace_handle_v1_set_active(ws->handle, active);
	}
}

/* commit handler */

static void
handle_ext_commit(struct wl_listener *listener, void *data)
{
	struct wlr_ext_workspace_v1_commit_event *event = data;
	struct wlr_ext_workspace_v1_request *req;

	wl_list_for_each(req, event->requests, link) {
		struct ext_workspace *ws = NULL;
		struct ext_workspace *w;

		switch (req->type) {
		case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE:
			if (!req->activate.workspace)
				break;
			wl_list_for_each(w, &ext_workspaces, link) {
				if (w->handle == req->activate.workspace) {
					ws = w;
					break;
				}
			}
			if (!ws)
				break;
			view(&(Arg){.ui = 1 << (ws->tag - 1)});
			break;

		case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE:
			if (!req->deactivate.workspace)
				break;
			wl_list_for_each(w, &ext_workspaces, link) {
				if (w->handle == req->deactivate.workspace) {
					ws = w;
					break;
				}
			}
			if (!ws)
				break;
			toggleview(&(Arg){.ui = 1 << (ws->tag - 1)});
			break;

		default:
			break;
		}
	}
}

/* init */

static void
handle_ext_destroy(struct wl_listener *listener, void *data)
{
	wl_list_remove(&ext_commit_listener.link);
	wl_list_remove(&ext_destroy_listener.link);
}

static void
workspaces_init(void)
{
	ext_workspace_manager = wlr_ext_workspace_manager_v1_create(dpy, 1);
	wl_list_init(&ext_workspaces);

	ext_commit_listener.notify = handle_ext_commit;
	wl_signal_add(&ext_workspace_manager->events.commit,
			&ext_commit_listener);

	ext_destroy_listener.notify = handle_ext_destroy;
	wl_signal_add(&ext_workspace_manager->events.destroy,
			&ext_destroy_listener);
}