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 8f1a3244d8fafa6e46aba4e5ef0c45fb9297c669
Author: [email protected] <[email protected]>
AuthorDate: Sat Jun 13 11:40:50 2026 -0600
feat(e_wl_proxy): stable per-toplevel session uuids
Window position restore needs a stable identity for each toplevel that
survives a compositor crash. Enlightenment already restores per-window state
keyed on a session-recovery uuid, but that protocol mints a fresh uuid on each
get_uuid, so it does not survive a restart. This adds the proxy-side support to
mint a stable uuid per toplevel and drive Enlightenment's
zwp_e_session_recovery.set_uuid on its behalf:
- deterministic uuid generation from (pid, connection, sequence);
- the set_uuid request / create_uuid event wire codec;
- a per-connection table mapping surfaces and their xdg roles to uuids,
tracking which toplevels still need a uuid injected.
This is the data layer only; the connection state machine wires it into the
live and recovery paths.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
src/bin/e_wl_proxy/e_wl_proxy_session.c | 200 ++++++++++++++++++++++++++++++++
src/bin/e_wl_proxy/e_wl_proxy_session.h | 73 ++++++++++++
2 files changed, 273 insertions(+)
diff --git a/src/bin/e_wl_proxy/e_wl_proxy_session.c b/src/bin/e_wl_proxy/e_wl_proxy_session.c
new file mode 100644
index 000000000..df798d7bc
--- /dev/null
+++ b/src/bin/e_wl_proxy/e_wl_proxy_session.c
@@ -0,0 +1,200 @@
+#include "config.h"
+#include "e_wl_proxy_session.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static inline size_t _pad4(size_t n) { return (n + 3) & ~((size_t)3); }
+
+size_t
+e_wl_proxy_session_uuid(char *out, size_t cap, int pid, unsigned int conn_id,
+ unsigned int seq)
+{
+ int n = snprintf(out, cap, "wlproxy-%08x-%08x-%08x",
+ (unsigned)pid, conn_id, seq);
+ if (n <= 0 || (size_t)n >= cap) { if (cap) out[0] = '\0'; return 0; }
+ return (size_t)n;
+}
+
+size_t
+e_wl_proxy_session_build_set_uuid(unsigned char *buf, size_t cap,
+ uint32_t sr_id, uint32_t surface_id,
+ const char *uuid)
+{
+ size_t slen = uuid ? strlen(uuid) + 1 : 1; /* include NUL */
+ size_t size = 8 + 4 + 4 + _pad4(slen);
+ uint32_t w;
+
+ if (!buf || size > cap || size > 0xffff) return 0;
+ memcpy(buf, &sr_id, 4);
+ w = ((uint32_t)size << 16) | 1u; /* opcode 1 = set_uuid */
+ memcpy(buf + 4, &w, 4);
+ memcpy(buf + 8, &surface_id, 4);
+ w = (uint32_t)slen;
+ memcpy(buf + 12, &w, 4);
+ memset(buf + 16, 0, _pad4(slen)); /* zero pad (incl NUL) */
+ if (uuid) memcpy(buf + 16, uuid, slen - 1);
+ return size;
+}
+
+int
+e_wl_proxy_session_parse_create_uuid(const void *body, size_t body_len,
+ uint32_t *surface_id, char *uuid_out,
+ size_t uuid_cap)
+{
+ const unsigned char *p = body;
+ uint32_t surface, slen;
+
+ if (body_len < 8) return -1; /* surface(4) + strlen(4) */
+ memcpy(&surface, p, 4);
+ memcpy(&slen, p + 4, 4);
+ if (slen == 0 || (size_t)8 + slen > body_len) return -1;
+ if (slen > uuid_cap) return -1;
+ memcpy(uuid_out, p + 8, slen - 1);
+ uuid_out[slen - 1] = '\0';
+ if (surface_id) *surface_id = surface;
+ return 0;
+}
+
+/* ---- per-connection window-identity table ---- */
+
+typedef struct _Win
+{
+ uint32_t surface_id;
+ char uuid[E_WL_PROXY_SESSION_UUID_MAX];
+ Eina_Bool is_toplevel;
+ Eina_Bool uuid_sent;
+} Win;
+
+struct _E_Wl_Proxy_Session
+{
+ int pid;
+ unsigned int conn_id;
+ unsigned int seq; /* uuid generation counter */
+ Eina_Hash *wins; /* surface_id -> Win* */
+ Eina_Hash *xdg2surf; /* xdg_surface_id -> surface_id (as uintptr) */
+};
+
+static Win *
+_win_ensure(E_Wl_Proxy_Session *s, uint32_t surface_id)
+{
+ Win *w = eina_hash_find(s->wins, &surface_id);
+ if (w) return w;
+ w = calloc(1, sizeof(*w));
+ if (!w) return NULL;
+ w->surface_id = surface_id;
+ eina_hash_add(s->wins, &surface_id, w);
+ return w;
+}
+
+E_Wl_Proxy_Session *
+e_wl_proxy_session_new(int pid, unsigned int conn_id)
+{
+ E_Wl_Proxy_Session *s = calloc(1, sizeof(*s));
+ if (!s) return NULL;
+ s->pid = pid; s->conn_id = conn_id;
+ s->wins = eina_hash_int32_new(free);
+ s->xdg2surf = eina_hash_int32_new(NULL);
+ if (!s->wins || !s->xdg2surf) { e_wl_proxy_session_free(s); return NULL; }
+ return s;
+}
+
+void
+e_wl_proxy_session_free(E_Wl_Proxy_Session *s)
+{
+ if (!s) return;
+ if (s->wins) eina_hash_free(s->wins);
+ if (s->xdg2surf) eina_hash_free(s->xdg2surf);
+ free(s);
+}
+
+void
+e_wl_proxy_session_link_xdg(E_Wl_Proxy_Session *s, uint32_t xdg_id, uint32_t surface_id)
+{
+ if (!s) return;
+ eina_hash_set(s->xdg2surf, &xdg_id, (void *)(uintptr_t)surface_id);
+}
+
+uint32_t
+e_wl_proxy_session_mark_toplevel(E_Wl_Proxy_Session *s, uint32_t xdg_id)
+{
+ uintptr_t sid;
+ Win *w;
+ if (!s) return 0;
+ sid = (uintptr_t)eina_hash_find(s->xdg2surf, &xdg_id);
+ if (!sid) return 0;
+ w = _win_ensure(s, (uint32_t)sid);
+ if (!w) return 0;
+ w->is_toplevel = EINA_TRUE;
+ if (w->uuid[0] == '\0')
+ e_wl_proxy_session_uuid(w->uuid, sizeof(w->uuid), s->pid, s->conn_id, s->seq++);
+ return (uint32_t)sid;
+}
+
+void
+e_wl_proxy_session_capture_uuid(E_Wl_Proxy_Session *s, uint32_t surface_id, const char *uuid)
+{
+ Win *w;
+ if (!s || !uuid) return;
+ w = _win_ensure(s, surface_id);
+ if (!w) return;
+ w->is_toplevel = EINA_TRUE;
+ snprintf(w->uuid, sizeof(w->uuid), "%s", uuid);
+ w->uuid_sent = EINA_TRUE; /* client drove it; don't inject our own */
+}
+
+const char *
+e_wl_proxy_session_commit_needs_uuid(E_Wl_Proxy_Session *s, uint32_t surface_id)
+{
+ Win *w = s ? eina_hash_find(s->wins, &surface_id) : NULL;
+ if (!w || !w->is_toplevel || w->uuid_sent || w->uuid[0] == '\0') return NULL;
+ w->uuid_sent = EINA_TRUE;
+ return w->uuid;
+}
+
+void
+e_wl_proxy_session_drop_surface(E_Wl_Proxy_Session *s, uint32_t surface_id)
+{
+ if (s) eina_hash_del(s->wins, &surface_id, NULL);
+}
+
+void
+e_wl_proxy_session_drop_xdg(E_Wl_Proxy_Session *s, uint32_t xdg_id)
+{
+ if (s) eina_hash_del(s->xdg2surf, &xdg_id, NULL);
+}
+
+void
+e_wl_proxy_session_reset_sent(E_Wl_Proxy_Session *s)
+{
+ Eina_Iterator *it;
+ Win *w;
+ if (!s) return;
+ it = eina_hash_iterator_data_new(s->wins);
+ EINA_ITERATOR_FOREACH(it, w) w->uuid_sent = EINA_FALSE;
+ eina_iterator_free(it);
+}
+
+const char *
+e_wl_proxy_session_uuid_of(E_Wl_Proxy_Session *s, uint32_t surface_id)
+{
+ Win *w = s ? eina_hash_find(s->wins, &surface_id) : NULL;
+ return (w && w->uuid[0]) ? w->uuid : NULL;
+}
+
+typedef struct _TlCtx { void (*cb)(uint32_t, const char *, void *); void *data; } TlCtx;
+static Eina_Bool
+_tl_each(const Eina_Hash *h EINA_UNUSED, const void *key EINA_UNUSED, void *val, void *fdata)
+{
+ Win *w = val; TlCtx *c = fdata;
+ if (w->is_toplevel && w->uuid[0]) c->cb(w->surface_id, w->uuid, c->data);
+ return EINA_TRUE;
+}
+
+void
+e_wl_proxy_session_foreach_toplevel(E_Wl_Proxy_Session *s,
+ void (*cb)(uint32_t, const char *, void *), void *data)
+{
+ TlCtx c = { cb, data };
+ if (s && cb) eina_hash_foreach(s->wins, _tl_each, &c);
+}
diff --git a/src/bin/e_wl_proxy/e_wl_proxy_session.h b/src/bin/e_wl_proxy/e_wl_proxy_session.h
new file mode 100644
index 000000000..1bbb09e99
--- /dev/null
+++ b/src/bin/e_wl_proxy/e_wl_proxy_session.h
@@ -0,0 +1,73 @@
+#ifndef E_WL_PROXY_SESSION_H
+#define E_WL_PROXY_SESSION_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <Eina.h>
+
+/* The longest uuid string we store/transmit (E treats it as an opaque key).
+ * 37 = 36 chars + NUL, matching libuuid's unparse buffer. */
+#define E_WL_PROXY_SESSION_UUID_MAX 37
+
+/* ---- pure helpers (no state) ---- */
+
+/* Write a stable, unique window uuid into out[0..cap) as a NUL-terminated
+ * string. Unique per (pid, conn_id, seq); opaque to E. Returns the string
+ * length (excluding NUL), or 0 if cap is too small. */
+size_t e_wl_proxy_session_uuid(char *out, size_t cap,
+ int pid, unsigned int conn_id, unsigned int seq);
+
+/* Build a zwp_e_session_recovery.set_uuid(surface, uuid) request (opcode 1) into
+ * buf[0..cap): header{sr_id, size, op=1} + surface(4) + string(uuid). Both ids
+ * are already in backend space. Returns the message length, or 0 if it would
+ * not fit. */
+size_t e_wl_proxy_session_build_set_uuid(unsigned char *buf, size_t cap,
+ uint32_t sr_id, uint32_t surface_id,
+ const char *uuid);
+
+/* Parse a zwp_e_session_recovery.create_uuid(surface, uuid) event BODY (the
+ * bytes after the 8-byte header). Writes the surface id to *surface_id and the
+ * uuid into uuid_out[0..uuid_cap). Returns 0 on success, -1 if malformed. */
+int e_wl_proxy_session_parse_create_uuid(const void *body, size_t body_len,
+ uint32_t *surface_id,
+ char *uuid_out, size_t uuid_cap);
+
+/* ---- per-connection window-identity table (client-space ids) ---- */
+typedef struct _E_Wl_Proxy_Session E_Wl_Proxy_Session;
+
+/* `pid`/`conn_id` seed the uuid generator; ids are client-space wl_surface ids. */
+E_Wl_Proxy_Session *e_wl_proxy_session_new(int pid, unsigned int conn_id);
+void e_wl_proxy_session_free(E_Wl_Proxy_Session *s);
+
+/* get_xdg_surface(new_id xdg_surface, object wl_surface): record the link. */
+void e_wl_proxy_session_link_xdg(E_Wl_Proxy_Session *s, uint32_t xdg_id, uint32_t surface_id);
+
+/* get_toplevel on xdg_surface `xdg_id`: mark its wl_surface a toplevel and (if
+ * not already) generate its uuid. Returns the wl_surface id, or 0 if unknown. */
+uint32_t e_wl_proxy_session_mark_toplevel(E_Wl_Proxy_Session *s, uint32_t xdg_id);
+
+/* Record a client-driven uuid (captured from create_uuid) for a wl_surface, and
+ * mark it a toplevel. Marks it uuid_sent so the proxy won't inject its own. */
+void e_wl_proxy_session_capture_uuid(E_Wl_Proxy_Session *s, uint32_t surface_id,
+ const char *uuid);
+
+/* For a wl_surface that received a commit: if it is a toplevel whose uuid has
+ * not yet been sent to E, returns its uuid and marks it sent; else NULL. */
+const char *e_wl_proxy_session_commit_needs_uuid(E_Wl_Proxy_Session *s, uint32_t surface_id);
+
+/* Forget a destroyed wl_surface (or xdg_surface). No-op if absent. */
+void e_wl_proxy_session_drop_surface(E_Wl_Proxy_Session *s, uint32_t surface_id);
+void e_wl_proxy_session_drop_xdg(E_Wl_Proxy_Session *s, uint32_t xdg_id);
+
+/* Clear the "uuid_sent" flag on every toplevel (call at the start of a recovery
+ * so set_uuid is re-emitted to the fresh E). */
+void e_wl_proxy_session_reset_sent(E_Wl_Proxy_Session *s);
+
+/* Invoke cb(surface_id, uuid, data) for every toplevel with a uuid. */
+void e_wl_proxy_session_foreach_toplevel(E_Wl_Proxy_Session *s,
+ void (*cb)(uint32_t surface_id, const char *uuid, void *data), void *data);
+
+/* test/query: uuid of a wl_surface, or NULL. */
+const char *e_wl_proxy_session_uuid_of(E_Wl_Proxy_Session *s, uint32_t surface_id);
+
+#endif
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.