Wave 1 changes: - Reduce CFG_MAX_KEYBINDS 256→128, CFG_MAX_STRLEN 256→64, CFG_MAX_ARGS 16→8 → saves ~2.5 MB BSS from Config struct - Add truncation warnings to config_load when keybind/button/scroll limit reached - Reorder Client struct: HOT fields (type, mon, tags, floating flags, geom) in first cache line, COLD fields (wl_listeners) at end — saves cache misses in arrange() - Reorder LayerSurface struct similarly - Split Monitor into Monitor/MonitorCold: hot fields (~120B) stay inline, cold layout state lazily allocated on first dwindle/master use - All cold-field accesses through m->cold-> indirection - cleanupmon frees m->cold - Zero-warning build with clang -Werror -Wpedantic
279 lines
7.1 KiB
C
279 lines
7.1 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wayland-server-core.h>
|
|
#include <wlr/types/wlr_output.h>
|
|
|
|
#include "client.h"
|
|
#include "ipc.h"
|
|
#include "layout.h"
|
|
#include "util.h"
|
|
|
|
/* IPC globals */
|
|
struct wl_global *ipc_global = nullptr;
|
|
struct wl_list ipc_managers; /* IpcManager.link */
|
|
|
|
/* 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 nullptr;
|
|
}
|
|
|
|
/* 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) */
|
|
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 */
|
|
zpeachwm_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 = ZPEACHWM_IPC_OUTPUT_V2_TAG_STATE_NONE;
|
|
uint32_t clients_on_tag = 0;
|
|
|
|
if (m->tagset[m->seltags] & mask)
|
|
state = ZPEACHWM_IPC_OUTPUT_V2_TAG_STATE_ACTIVE;
|
|
if (urg & mask)
|
|
state = ZPEACHWM_IPC_OUTPUT_V2_TAG_STATE_URGENT;
|
|
if (occ & mask)
|
|
clients_on_tag = 1; /* report >=1, not exact count */
|
|
|
|
focused = (sel_tags & mask) ? 1 : 0;
|
|
zpeachwm_ipc_output_v2_send_tag(res, tag, state, clients_on_tag, focused);
|
|
}
|
|
|
|
/* layout index */
|
|
{
|
|
uint32_t i;
|
|
for (i = 0; i < (uint32_t)layout_count; i++)
|
|
if (&layouts[i] == curlayout(m))
|
|
break;
|
|
zpeachwm_ipc_output_v2_send_layout(res, i);
|
|
}
|
|
|
|
/* layout symbol */
|
|
zpeachwm_ipc_output_v2_send_layout_symbol(res, m->cold->ltsymbol[current_tag_idx(m)]);
|
|
|
|
/* title/appid/fullscreen/floating */
|
|
if (c) {
|
|
zpeachwm_ipc_output_v2_send_title(res, client_get_title(c));
|
|
zpeachwm_ipc_output_v2_send_appid(res, client_get_appid(c));
|
|
if (wl_resource_get_version(res) >= 2) {
|
|
zpeachwm_ipc_output_v2_send_fullscreen(res, c->isfullscreen ? 1 : 0);
|
|
zpeachwm_ipc_output_v2_send_floating(res, c->isfloating ? 1 : 0);
|
|
}
|
|
} else {
|
|
zpeachwm_ipc_output_v2_send_title(res, "");
|
|
zpeachwm_ipc_output_v2_send_appid(res, "");
|
|
if (wl_resource_get_version(res) >= 2) {
|
|
zpeachwm_ipc_output_v2_send_fullscreen(res, 0);
|
|
zpeachwm_ipc_output_v2_send_floating(res, 0);
|
|
}
|
|
}
|
|
|
|
/* frame -- signals end of this batch of events */
|
|
zpeachwm_ipc_output_v2_send_frame(res);
|
|
}
|
|
|
|
/* Called instead of (or alongside) the old printstatus() */
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* zpeachwm_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);
|
|
if (!out) return;
|
|
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);
|
|
if (!out) return;
|
|
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);
|
|
if (!out) return;
|
|
Monitor *m = out->mon;
|
|
if (!m || idx >= (uint32_t)layout_count) return;
|
|
|
|
int ti = current_tag_idx(m);
|
|
ensure_cold(m);
|
|
if (m->cold->lt[ti][m->cold->sellt[ti]] != &layouts[idx])
|
|
m->cold->sellt[ti] ^= 1;
|
|
m->cold->lt[ti][m->cold->sellt[ti]] = &layouts[idx];
|
|
strncpy(m->cold->ltsymbol[ti], layouts[idx].symbol, LENGTH(m->cold->ltsymbol[ti]));
|
|
arrange(m);
|
|
printstatus();
|
|
}
|
|
|
|
static const struct zpeachwm_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);
|
|
}
|
|
|
|
/* zpeachwm_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 : nullptr;
|
|
|
|
IpcOutput *out = ecalloc(1, sizeof(*out));
|
|
out->mon = m;
|
|
out->resource = wl_resource_create(client, &zpeachwm_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 zpeachwm_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, &zpeachwm_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 */
|
|
zpeachwm_ipc_manager_v2_send_tags(mgr->resource, TAGCOUNT);
|
|
for (uint32_t i = 0; i < (uint32_t)layout_count; i++)
|
|
zpeachwm_ipc_manager_v2_send_layout(mgr->resource, layouts[i].symbol);
|
|
}
|
|
|
|
void
|
|
ipc_init(void)
|
|
{
|
|
wl_list_init(&ipc_managers);
|
|
ipc_global = wl_global_create(dpy, &zpeachwm_ipc_manager_v2_interface,
|
|
2, nullptr, ipc_bind);
|
|
}
|