This is an automated email from the git hooks/post-receive script.
git pushed a commit to reference refs/pull/135/head
in repository enlightenment.
View the commit online.
commit 9b002e209588787bb7f4643ec8e8fdc93167f8ba
Author: [email protected] <[email protected]>
AuthorDate: Sat Jun 13 11:40:50 2026 -0600
feat(e_wl_proxy): control protocol and WM-state blob store
A side channel between the proxy and an Enlightenment companion module. The
module connects to a control socket and mirrors each window's WM-state
(geometry, desktop, stacking, focus) as an opaque blob keyed by session uuid;
the proxy keeps the latest blob per uuid in a store that outlives each
Enlightenment restart, and replays them to the module on reconnect so state
can be restored after a crash.
Includes the framed control wire format (shared header in e_wl_proxy_proto.h),
the blob store, and the per-connection control endpoint with a non-blocking
socket and EMFILE-throttled accept so a wedged peer can never stall the proxy.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
src/bin/e_wl_proxy/e_wl_proxy_ctl.c | 188 +++++++++++++++++++++++++++++++
src/bin/e_wl_proxy/e_wl_proxy_ctl_conn.c | 156 +++++++++++++++++++++++++
src/bin/e_wl_proxy_proto.h | 27 +++++
3 files changed, 371 insertions(+)
diff --git a/src/bin/e_wl_proxy/e_wl_proxy_ctl.c b/src/bin/e_wl_proxy/e_wl_proxy_ctl.c
new file mode 100644
index 000000000..234bd2418
--- /dev/null
+++ b/src/bin/e_wl_proxy/e_wl_proxy_ctl.c
@@ -0,0 +1,188 @@
+#include "config.h"
+#include "e_wl_proxy.h"
+#include <Eina.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int
+_type_valid(uint32_t t)
+{
+ return t >= E_WL_PROXY_CTL_HELLO && t <= E_WL_PROXY_CTL_BYE;
+}
+
+int
+e_wl_proxy_ctl_parse(const void *buf, size_t len, E_Wl_Proxy_Ctl_Msg *msg, size_t *consumed)
+{
+ E_Wl_Proxy_Ctl_Hdr hdr;
+ size_t total;
+ const char *p = buf;
+
+ if (len < sizeof(hdr)) return 0;
+ memcpy(&hdr, buf, sizeof(hdr));
+
+ if (!_type_valid(hdr.type)) return -1;
+ if (hdr.uuid_len > E_WL_PROXY_CTL_MAX_UUID) return -1;
+ if (hdr.blob_len > E_WL_PROXY_CTL_MAX_BLOB) return -1;
+
+ total = sizeof(hdr) + hdr.uuid_len + hdr.blob_len;
+ if (len < total) return 0;
+
+ msg->type = (E_Wl_Proxy_Ctl_Type)hdr.type;
+ msg->uuid = hdr.uuid_len ? (p + sizeof(hdr)) : NULL;
+ msg->uuid_len = hdr.uuid_len;
+ msg->blob = hdr.blob_len ? (p + sizeof(hdr) + hdr.uuid_len) : NULL;
+ msg->blob_len = hdr.blob_len;
+ *consumed = total;
+ return 1;
+}
+
+ssize_t
+e_wl_proxy_ctl_serialize(E_Wl_Proxy_Ctl_Type type, const char *uuid, uint32_t uuid_len,
+ const void *blob, uint32_t blob_len, void *out, size_t outcap)
+{
+ E_Wl_Proxy_Ctl_Hdr hdr;
+ size_t total = sizeof(hdr) + uuid_len + blob_len;
+ char *p = out;
+
+ if (uuid_len > E_WL_PROXY_CTL_MAX_UUID) return -1;
+ if (blob_len > E_WL_PROXY_CTL_MAX_BLOB) return -1;
+ if (total > outcap) return -1;
+
+ hdr.type = (uint32_t)type;
+ hdr.uuid_len = uuid_len;
+ hdr.blob_len = blob_len;
+ memcpy(p, &hdr, sizeof(hdr));
+ if (uuid_len) memcpy(p + sizeof(hdr), uuid, uuid_len);
+ if (blob_len) memcpy(p + sizeof(hdr) + uuid_len, blob, blob_len);
+ return (ssize_t)total;
+}
+
+struct _E_Wl_Proxy_Store
+{
+ Eina_Hash *hash; /* uuid(string) -> _Blob* */
+};
+
+typedef struct _Blob
+{
+ void *data;
+ uint32_t len;
+} _Blob;
+
+static void
+_blob_free(void *d)
+{
+ _Blob *b = d;
+ if (!b) return;
+ free(b->data);
+ free(b);
+}
+
+E_Wl_Proxy_Store *
+e_wl_proxy_store_new(void)
+{
+ E_Wl_Proxy_Store *st = calloc(1, sizeof(*st));
+ if (!st) return NULL;
+ st->hash = eina_hash_string_superfast_new(_blob_free);
+ if (!st->hash) { free(st); return NULL; }
+ return st;
+}
+
+void
+e_wl_proxy_store_free(E_Wl_Proxy_Store *st)
+{
+ if (!st) return;
+ eina_hash_free(st->hash);
+ free(st);
+}
+
+static char *
+_key(const char *uuid, uint32_t uuid_len)
+{
+ char *k = malloc(uuid_len + 1);
+ if (!k) return NULL;
+ memcpy(k, uuid, uuid_len);
+ k[uuid_len] = '\0';
+ return k;
+}
+
+int
+e_wl_proxy_store_put(E_Wl_Proxy_Store *st, const char *uuid, uint32_t uuid_len,
+ const void *blob, uint32_t blob_len)
+{
+ _Blob *b;
+ char *k;
+
+ if (!st || !uuid || uuid_len == 0) return -1;
+ k = _key(uuid, uuid_len);
+ if (!k) return -1;
+
+ b = malloc(sizeof(*b));
+ if (!b) { free(k); return -1; }
+ b->len = blob_len;
+ b->data = "" ? malloc(blob_len) : NULL;
+ if (blob_len && !b->data) { free(b); free(k); return -1; }
+ if (blob_len) memcpy(b->data, blob, blob_len);
+
+ /* eina_hash_set returns the prior value WITHOUT invoking the free_cb, so a
+ * repeated put for the same uuid (every window move/resize/restack) would
+ * leak the old blob. Free it ourselves; _blob_free is NULL-safe. */
+ _blob_free(eina_hash_set(st->hash, k, b));
+ free(k);
+ return 0;
+}
+
+void
+e_wl_proxy_store_forget(E_Wl_Proxy_Store *st, const char *uuid, uint32_t uuid_len)
+{
+ char *k;
+ if (!st || !uuid || uuid_len == 0) return;
+ k = _key(uuid, uuid_len);
+ if (!k) return;
+ eina_hash_del_by_key(st->hash, k);
+ free(k);
+}
+
+const void *
+e_wl_proxy_store_get(E_Wl_Proxy_Store *st, const char *uuid, uint32_t uuid_len, uint32_t *len)
+{
+ _Blob *b;
+ char *k;
+ if (!st || !uuid || uuid_len == 0) return NULL;
+ k = _key(uuid, uuid_len);
+ if (!k) return NULL;
+ b = eina_hash_find(st->hash, k);
+ free(k);
+ if (!b) return NULL;
+ if (len) *len = b->len;
+ return b->data;
+}
+
+unsigned int
+e_wl_proxy_store_count(E_Wl_Proxy_Store *st)
+{
+ return st ? (unsigned int)eina_hash_population(st->hash) : 0;
+}
+
+typedef struct _Foreach_Ctx
+{
+ void (*cb)(const char *, const void *, uint32_t, void *);
+ void *data;
+} _Foreach_Ctx;
+
+static Eina_Bool
+_foreach_trampoline(const Eina_Hash *h EINA_UNUSED, const void *key, void *val, void *fdata)
+{
+ _Foreach_Ctx *c = fdata;
+ _Blob *b = val;
+ c->cb((const char *)key, b->data, b->len, c->data);
+ return EINA_TRUE;
+}
+
+void
+e_wl_proxy_store_foreach(E_Wl_Proxy_Store *st,
+ void (*cb)(const char *, const void *, uint32_t, void *), void *data)
+{
+ _Foreach_Ctx c = { cb, data };
+ if (!st) return;
+ eina_hash_foreach(st->hash, _foreach_trampoline, &c);
+}
diff --git a/src/bin/e_wl_proxy/e_wl_proxy_ctl_conn.c b/src/bin/e_wl_proxy/e_wl_proxy_ctl_conn.c
new file mode 100644
index 000000000..7d446144e
--- /dev/null
+++ b/src/bin/e_wl_proxy/e_wl_proxy_ctl_conn.c
@@ -0,0 +1,156 @@
+#include "config.h"
+#include "e_wl_proxy.h"
+#include <Ecore.h>
+#include <Eina.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+struct _E_Wl_Proxy_Ctl_Conn
+{
+ int fd;
+ Ecore_Fd_Handler *h;
+ E_Wl_Proxy_Store *st; /* shared, not owned */
+ Eina_Binbuf *in; /* accumulates partial frames */
+ Eina_Binbuf *out; /* pending bytes to write back (HELLO dump) */
+};
+
+static void
+_dump_cb(const char *uuid, const void *blob, uint32_t len, void *data)
+{
+ E_Wl_Proxy_Ctl_Conn *c = data;
+ uint32_t uuid_len = (uint32_t)strlen(uuid);
+ size_t cap = sizeof(E_Wl_Proxy_Ctl_Hdr) + uuid_len + len;
+ unsigned char *frame = malloc(cap);
+ ssize_t n;
+
+ if (!frame) return;
+ n = e_wl_proxy_ctl_serialize(E_WL_PROXY_CTL_WM_STATE, uuid, uuid_len,
+ blob, len, frame, cap);
+ if (n > 0) eina_binbuf_append_length(c->out, frame, (size_t)n);
+ free(frame);
+}
+
+static void
+_dispatch(E_Wl_Proxy_Ctl_Conn *c, const E_Wl_Proxy_Ctl_Msg *m)
+{
+ switch (m->type)
+ {
+ case E_WL_PROXY_CTL_WM_STATE:
+ e_wl_proxy_store_put(c->st, m->uuid, m->uuid_len, m->blob, m->blob_len);
+ break;
+ case E_WL_PROXY_CTL_WM_FORGET:
+ e_wl_proxy_store_forget(c->st, m->uuid, m->uuid_len);
+ break;
+ case E_WL_PROXY_CTL_HELLO:
+ e_wl_proxy_store_foreach(c->st, _dump_cb, c);
+ break;
+ case E_WL_PROXY_CTL_BYE:
+ default:
+ break;
+ }
+}
+
+static int
+_flush_out(E_Wl_Proxy_Ctl_Conn *c)
+{
+ size_t len = eina_binbuf_length_get(c->out);
+ const unsigned char *p = eina_binbuf_string_get(c->out);
+ size_t off = 0;
+
+ while (off < len)
+ {
+ ssize_t n = send(c->fd, p + off, len - off, MSG_NOSIGNAL);
+ if (n < 0)
+ {
+ if (errno == EINTR) continue;
+ if (errno == EAGAIN || errno == EWOULDBLOCK) break;
+ return -1;
+ }
+ off += (size_t)n;
+ }
+ if (off) eina_binbuf_remove(c->out, 0, off);
+ return 0;
+}
+
+static Eina_Bool
+_ctl_cb(void *data, Ecore_Fd_Handler *fdh)
+{
+ E_Wl_Proxy_Ctl_Conn *c = data;
+ char buf[4096];
+ ssize_t n;
+
+ if (ecore_main_fd_handler_active_get(fdh, ECORE_FD_ERROR)) goto dead;
+
+ if (ecore_main_fd_handler_active_get(fdh, ECORE_FD_WRITE))
+ {
+ if (_flush_out(c) < 0) goto dead;
+ }
+
+ if (ecore_main_fd_handler_active_get(fdh, ECORE_FD_READ))
+ {
+ n = recv(c->fd, buf, sizeof(buf), 0);
+ if (n == 0) goto dead; /* module disconnected */
+ if (n < 0)
+ {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) goto dead;
+ }
+ else
+ {
+ /* in-buffer is naturally bounded: the parser rejects any frame
+ * with uuid_len > MAX_UUID or blob_len > MAX_BLOB, so at most one
+ * max-size incomplete frame (~64 KB) is ever buffered here. */
+ eina_binbuf_append_length(c->in, (const unsigned char *)buf, (size_t)n);
+ for (;;)
+ {
+ E_Wl_Proxy_Ctl_Msg m;
+ size_t consumed = 0;
+ const unsigned char *ib = eina_binbuf_string_get(c->in);
+ size_t il = eina_binbuf_length_get(c->in);
+ int r = e_wl_proxy_ctl_parse(ib, il, &m, &consumed);
+ if (r == 0) break; /* need more */
+ if (r < 0) goto dead; /* malformed */
+ _dispatch(c, &m);
+ eina_binbuf_remove(c->in, 0, consumed);
+ }
+ }
+ }
+
+ {
+ int flags = ECORE_FD_READ | ECORE_FD_ERROR;
+ if (eina_binbuf_length_get(c->out) > 0) flags |= ECORE_FD_WRITE;
+ ecore_main_fd_handler_active_set(c->h, flags);
+ }
+ return ECORE_CALLBACK_RENEW;
+
+dead:
+ e_wl_proxy_ctl_conn_del(c);
+ return ECORE_CALLBACK_CANCEL;
+}
+
+E_Wl_Proxy_Ctl_Conn *
+e_wl_proxy_ctl_conn_add(int fd, E_Wl_Proxy_Store *st)
+{
+ E_Wl_Proxy_Ctl_Conn *c = calloc(1, sizeof(*c));
+ if (!c) { close(fd); return NULL; }
+ c->fd = fd;
+ c->st = st;
+ c->in = eina_binbuf_new();
+ c->out = eina_binbuf_new();
+ c->h = ecore_main_fd_handler_add(fd, ECORE_FD_READ | ECORE_FD_ERROR, _ctl_cb, c, NULL, NULL);
+ if (!c->in || !c->out || !c->h) { e_wl_proxy_ctl_conn_del(c); return NULL; }
+ return c;
+}
+
+void
+e_wl_proxy_ctl_conn_del(E_Wl_Proxy_Ctl_Conn *c)
+{
+ if (!c) return;
+ if (c->h) ecore_main_fd_handler_del(c->h);
+ if (c->fd >= 0) { close(c->fd); c->fd = -1; }
+ if (c->in) eina_binbuf_free(c->in);
+ if (c->out) eina_binbuf_free(c->out);
+ free(c);
+}
diff --git a/src/bin/e_wl_proxy_proto.h b/src/bin/e_wl_proxy_proto.h
new file mode 100644
index 000000000..202f76fa0
--- /dev/null
+++ b/src/bin/e_wl_proxy_proto.h
@@ -0,0 +1,27 @@
+#ifndef E_WL_PROXY_PROTO_H
+#define E_WL_PROXY_PROTO_H
+
+/* Wire protocol shared by e_wl_proxy (the executable) and the wl_proxy module.
+ * Host byte order: this is local IPC on one machine only. */
+
+#include <stdint.h>
+
+#define E_WL_PROXY_CTL_MAX_UUID 256
+#define E_WL_PROXY_CTL_MAX_BLOB (64 * 1024)
+
+typedef enum
+{
+ E_WL_PROXY_CTL_HELLO = 1, /* module->proxy: dump all stored blobs back */
+ E_WL_PROXY_CTL_WM_STATE = 2, /* both ways: {uuid, blob} upsert / replay */
+ E_WL_PROXY_CTL_WM_FORGET = 3, /* module->proxy: {uuid} drop */
+ E_WL_PROXY_CTL_BYE = 4, /* module->proxy: clean shutdown */
+} E_Wl_Proxy_Ctl_Type;
+
+typedef struct _E_Wl_Proxy_Ctl_Hdr
+{
+ uint32_t type;
+ uint32_t uuid_len;
+ uint32_t blob_len;
+} E_Wl_Proxy_Ctl_Hdr;
+
+#endif
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.