This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch feat/e-wl-proxy-planb
in repository enlightenment.

View the commit online.

commit 8b26a370081499f65e68a19c00ee5f697fc6b5af
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.

Reply via email to