view include/dwl-ipc.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

#include "dwl-ipc-unstable-v2-protocol.h"

/* Forward declarations for dwl.c static functions */

static void arrange(Monitor *m);
static void focusclient(Client *c, int lift);
static Client *focustop(Monitor *m);
static void printstatus(void);

/* globals  */

static struct wl_global *ipc_global = NULL;
/* All bound manager objects */
static struct wl_list ipc_managers; /* IpcManager.link */

typedef struct {
	struct wl_resource     *resource;
	struct wl_list          link;     /* ipc_managers */
	struct wl_list          outputs;  /* IpcOutput.link */
} IpcManager;

typedef struct {
	struct wl_resource     *resource;
	Monitor                *mon;
	struct wl_list          link;     /* IpcManager.outputs */
} IpcOutput;

/* helpers  */

static IpcOutput *
ipc_output_for_mon(IpcManager *mgr, Monitor *m)
{
	IpcOutput *o;
	wl_list_for_each(o, &mgr->outputs, link)
		if (o->mon == m)
			return o;
	return NULL;
}

/* Send the full state of monitor m to one IpcOutput resource */
static void
ipc_send_output_state(IpcOutput *ipc_out)
{
	Monitor *m = ipc_out->mon;
	struct wl_resource *res = ipc_out->resource;
	Client *c;
	uint32_t occ = 0, urg = 0, sel_tags = 0;
	int tag, focused;

	/* Compute occupancy/urgency (same logic as printstatus, I think) */
	wl_list_for_each(c, &clients, link) {
		if (c->mon != m)
			continue;
		occ |= c->tags;
		if (c->isurgent)
			urg |= c->tags;
	}

	c = focustop(m);
	sel_tags = c ? c->tags : 0;

	/* active */
	zdwl_ipc_output_v2_send_active(res, m == selmon ? 1 : 0);

	/* per-tag state */
	for (tag = 0; tag < TAGCOUNT; tag++) {
		uint32_t mask   = 1u << tag;
		uint32_t state  = ZDWL_IPC_OUTPUT_V2_TAG_STATE_NONE;
		uint32_t clients_on_tag = 0;

		if (m->tagset[m->seltags] & mask)
			state = ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE;
		if (urg & mask)
			state = ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT;
		if (occ & mask)
			clients_on_tag = 1; /* report ≥1, not exact count */

		focused = (sel_tags & mask) ? 1 : 0;
		zdwl_ipc_output_v2_send_tag(res, tag, state, clients_on_tag, focused);
	}

	/* layout index */
	{
		uint32_t i;
		for (i = 0; i < LENGTH(layouts); i++)
			if (&layouts[i] == m->lt[m->sellt])
				break;
		zdwl_ipc_output_v2_send_layout(res, i);
	}

	/* layout symbol */
	zdwl_ipc_output_v2_send_layout_symbol(res, m->ltsymbol);

	/* title/appid/fullscreen/floating */
	if (c) {
		zdwl_ipc_output_v2_send_title(res, client_get_title(c));
		zdwl_ipc_output_v2_send_appid(res, client_get_appid(c));
		if (wl_resource_get_version(res) >= 2) {
			zdwl_ipc_output_v2_send_fullscreen(res, c->isfullscreen ? 1 : 0);
			zdwl_ipc_output_v2_send_floating(res, c->isfloating ? 1 : 0);
		}
	} else {
		zdwl_ipc_output_v2_send_title(res, "");
		zdwl_ipc_output_v2_send_appid(res, "");
		if (wl_resource_get_version(res) >= 2) {
			zdwl_ipc_output_v2_send_fullscreen(res, 0);
			zdwl_ipc_output_v2_send_floating(res, 0);
		}
	}

	/* frame — signals end of this batch of events */
	zdwl_ipc_output_v2_send_frame(res);
}

/* Called instead of (or alongside) the old printstatus() */
static void
ipc_printstatus(void)
{
	IpcManager *mgr;
	Monitor *m;

	wl_list_for_each(mgr, &ipc_managers, link) {
		wl_list_for_each(m, &mons, link) {
			IpcOutput *out = ipc_output_for_mon(mgr, m);
			if (out)
				ipc_send_output_state(out);
		}
	}
}

/* zdwl_ipc_output_v2 requests */

static void
ipc_output_handle_release(struct wl_client *client, struct wl_resource *resource)
{
	wl_resource_destroy(resource);
}

static void
ipc_output_handle_set_tags(struct wl_client *client, struct wl_resource *resource,
		uint32_t tagmask, uint32_t toggle_tagset)
{
	IpcOutput *out = wl_resource_get_user_data(resource);
	Monitor *m = out->mon;
	if (!m) return;

	if (toggle_tagset)
		m->seltags ^= 1;
	m->tagset[m->seltags] = tagmask & TAGMASK;
	focusclient(focustop(m), 1);
	arrange(m);
	printstatus();
}

static void
ipc_output_handle_set_client_tags(struct wl_client *client,
		struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags)
{
	IpcOutput *out = wl_resource_get_user_data(resource);
	Monitor *m = out->mon;
	Client *sel;
	uint32_t newtags;
	if (!m) return;

	sel = focustop(m);
	if (!sel) return;

	newtags = (sel->tags & and_tags) ^ xor_tags;
	if (!newtags) return;
	sel->tags = newtags;
	focusclient(focustop(m), 1);
	arrange(m);
	printstatus();
}

static void
ipc_output_handle_set_layout(struct wl_client *client,
		struct wl_resource *resource, uint32_t idx)
{
	IpcOutput *out = wl_resource_get_user_data(resource);
	Monitor *m = out->mon;
	if (!m || idx >= LENGTH(layouts)) return;

	m->lt[m->sellt] = &layouts[idx];
	strncpy(m->ltsymbol, layouts[idx].symbol, LENGTH(m->ltsymbol));
	arrange(m);
	printstatus();
}

static const struct zdwl_ipc_output_v2_interface ipc_output_impl = {
	.release          = ipc_output_handle_release,
	.set_tags         = ipc_output_handle_set_tags,
	.set_client_tags  = ipc_output_handle_set_client_tags,
	.set_layout       = ipc_output_handle_set_layout,
};

static void
ipc_output_destroy(struct wl_resource *resource)
{
	IpcOutput *out = wl_resource_get_user_data(resource);
	wl_list_remove(&out->link);
	free(out);
}

/* zdwl_ipc_manager_v2 requests */

static void
ipc_manager_handle_release(struct wl_client *client, struct wl_resource *resource)
{
	wl_resource_destroy(resource);
}

static void
ipc_manager_handle_get_output(struct wl_client *client, struct wl_resource *resource,
		uint32_t id, struct wl_resource *output_resource)
{
	IpcManager *mgr = wl_resource_get_user_data(resource);
	struct wlr_output *wlr_out = wlr_output_from_resource(output_resource);
	Monitor *m = (wlr_out && wlr_out->data) ? wlr_out->data : NULL;

	IpcOutput *out = ecalloc(1, sizeof(*out));
	out->mon = m;
	out->resource = wl_resource_create(client, &zdwl_ipc_output_v2_interface,
			wl_resource_get_version(resource), id);
	if (!out->resource) {
		free(out);
		wl_client_post_no_memory(client);
		return;
	}
	wl_resource_set_implementation(out->resource, &ipc_output_impl,
			out, ipc_output_destroy);
	wl_list_insert(&mgr->outputs, &out->link);

	if (m)
		ipc_send_output_state(out);
}

static const struct zdwl_ipc_manager_v2_interface ipc_manager_impl = {
	.release    = ipc_manager_handle_release,
	.get_output = ipc_manager_handle_get_output,
};

static void
ipc_manager_destroy(struct wl_resource *resource)
{
	IpcManager *mgr = wl_resource_get_user_data(resource);
	IpcOutput *out, *tmp;
	wl_list_for_each_safe(out, tmp, &mgr->outputs, link)
		wl_resource_destroy(out->resource);
	wl_list_remove(&mgr->link);
	free(mgr);
}

/* global bind */

static void
ipc_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
	IpcManager *mgr = ecalloc(1, sizeof(*mgr));
	wl_list_init(&mgr->outputs);
	mgr->resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface,
			version, id);
	if (!mgr->resource) {
		free(mgr);
		wl_client_post_no_memory(client);
		return;
	}
	wl_resource_set_implementation(mgr->resource, &ipc_manager_impl,
			mgr, ipc_manager_destroy);
	wl_list_insert(&ipc_managers, &mgr->link);

	/* Announce tags and layouts */
	zdwl_ipc_manager_v2_send_tags(mgr->resource, TAGCOUNT);
	for (uint32_t i = 0; i < LENGTH(layouts); i++)
		zdwl_ipc_manager_v2_send_layout(mgr->resource, layouts[i].symbol);
}

static void
ipc_init(void)
{
	wl_list_init(&ipc_managers);
	ipc_global = wl_global_create(dpy, &zdwl_ipc_manager_v2_interface,
			2, NULL, ipc_bind);
}