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 e4bb78019c5f4d8121c9f7e5f7ff78cfea23c5e5
Author: [email protected] <[email protected]>
AuthorDate: Sat Jun 13 11:41:14 2026 -0600

    feat(wl_proxy): module mirroring window WM-state for crash recovery
    
    A companion module that runs inside Enlightenment when behind the proxy. It
    connects to the proxy's control socket and mirrors each session-recovery
    window's WM-state (geometry, desktop, stacking order, focus) to the proxy's
    crash-safe store as it changes, keyed by the window's stable uuid.
    
    On E_CLIENT_HOOK_UUID_SET it restores a recovered window's state from the
    store, replaying stacking and focus in the captured order. The control socket
    is non-blocking so a wedged proxy can never freeze Enlightenment. Enabled by
    the wl-proxy build option (default on).
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
 meson_options.txt                          |   4 +
 src/modules/meson.build                    |   1 +
 src/modules/wl_proxy/e_mod_main.c          | 370 +++++++++++++++++++++++++++++
 src/modules/wl_proxy/e_wl_proxy_recv.c     |  42 ++++
 src/modules/wl_proxy/e_wl_proxy_recv.h     |  15 ++
 src/modules/wl_proxy/e_wl_proxy_wm.h       |  29 +++
 src/modules/wl_proxy/e_wl_proxy_wm_codec.c |  48 ++++
 src/modules/wl_proxy/meson.build           |   9 +
 8 files changed, 518 insertions(+)

diff --git a/meson_options.txt b/meson_options.txt
index 70b30bb30..e7d81b7cc 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -98,6 +98,10 @@ option('wl-weekeyboard',
 	type: 'boolean',
 	value: true,
 	description: 'enable internal wayland vkbd: (default=true)')
+option('wl-proxy',
+	type: 'boolean',
+	value: true,
+	description: 'enable wayland proxy module: (default=true)')
 
 option('systemd',
 	type: 'boolean',
diff --git a/src/modules/meson.build b/src/modules/meson.build
index 68dfc263a..ad2821293 100644
--- a/src/modules/meson.build
+++ b/src/modules/meson.build
@@ -70,6 +70,7 @@ mods = [
   'wl_text_input',
   'wl_desktop_shell',
   'wl_weekeyboard',
+  'wl_proxy',
 ### XXX: disabled for now
 #  'wl_fb'
   'pants',
diff --git a/src/modules/wl_proxy/e_mod_main.c b/src/modules/wl_proxy/e_mod_main.c
new file mode 100644
index 000000000..739295e84
--- /dev/null
+++ b/src/modules/wl_proxy/e_mod_main.c
@@ -0,0 +1,370 @@
+#include "e.h"
+#include "e_wl_proxy_wm.h"
+#include "e_wl_proxy_proto.h"
+#include "e_wl_proxy_recv.h"
+#include <Ecore.h>
+#include <Eina.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+E_API E_Module_Api e_modapi = { E_MODULE_API_VERSION, "WL Proxy" };
+
+static int _ctl_fd = -1;
+static Eina_List    *_handlers = NULL;
+/* monotonic; supplies stack_seq/focus_seq. Persists across a module reload
+ * (E never dlclose()s the DSO) — harmless, since the proxy resets on our BYE. */
+static unsigned int  _seq = 0;
+
+static Ecore_Fd_Handler *_ctl_h = NULL;
+static Eina_Binbuf      *_inbuf = NULL;
+static Eina_Hash        *_states = NULL;  /* uuid -> E_Wl_Proxy_Wm_State* */
+static E_Client_Hook    *_uuid_hook = NULL;
+
+typedef struct _Restored
+{
+   E_Client    *ec;
+   unsigned int stack_seq;
+   unsigned int focus_seq;
+} Restored;
+
+static Eina_List *_restored = NULL;   /* Restored* in arrival order */
+
+static int
+_ctl_connect(void)
+{
+   const char *runtime = getenv("XDG_RUNTIME_DIR");
+   const char *name = getenv("E_WL_PROXY_SOCK");
+   struct sockaddr_un addr;
+   char path[256];
+   int fd, n;
+
+   if (!runtime || !name || !name[0]) return -1;
+   n = snprintf(path, sizeof(path), "%s/%s", runtime, name);
+   if (n <= 0 || (size_t)n >= sizeof(path)) return -1;
+   if (strlen(path) >= sizeof(addr.sun_path)) return -1;
+
+   fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+   if (fd < 0) return -1;
+   memset(&addr, 0, sizeof(addr));
+   addr.sun_family = AF_UNIX;
+   strcpy(addr.sun_path, path);
+   /* Non-blocking: a wedged proxy must never block E's main loop. AF_UNIX
+    * stream connect completes synchronously, so this returns 0 or fails. */
+   if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(fd); return -1; }
+   return fd;
+}
+
+/* Send one control frame. Returns 0 on success, -1 on error (drops the conn). */
+static int
+_ctl_send(E_Wl_Proxy_Ctl_Type type, const char *uuid, const void *blob, uint32_t blob_len)
+{
+   E_Wl_Proxy_Ctl_Hdr hdr;
+   uint32_t uuid_len = uuid ? (uint32_t)strlen(uuid) : 0;
+   struct iovec iov[3];
+   struct msghdr msg;
+   size_t total;
+   int n = 0;
+   ssize_t r;
+
+   if (_ctl_fd < 0) return -1;
+   if (uuid_len > E_WL_PROXY_CTL_MAX_UUID) return -1;   /* keep within proxy limits */
+   if (blob_len > E_WL_PROXY_CTL_MAX_BLOB) return -1;
+   hdr.type = (uint32_t)type;
+   hdr.uuid_len = uuid_len;
+   hdr.blob_len = blob_len;
+   total = sizeof(hdr) + uuid_len + blob_len;
+
+   iov[n].iov_base = &hdr; iov[n].iov_len = sizeof(hdr); n++;
+   if (uuid_len) { iov[n].iov_base = (void *)uuid; iov[n].iov_len = uuid_len; n++; }
+   if (blob_len) { iov[n].iov_base = (void *)blob; iov[n].iov_len = blob_len; n++; }
+
+   memset(&msg, 0, sizeof(msg));
+   msg.msg_iov = iov;
+   msg.msg_iovlen = n;
+   do { r = sendmsg(_ctl_fd, &msg, MSG_NOSIGNAL); } while (r < 0 && errno == EINTR);
+
+   if (r == (ssize_t)total) return 0;                   /* whole frame out */
+   if ((r < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
+     return -1;   /* transient backpressure: drop this frame, keep the fd; the
+                   * next event re-sends current state (idempotent in the store) */
+   /* partial send desyncs the framing, or a hard error: abandon the connection
+    * (mirroring goes dark until the module reloads — no reconnect by design). */
+   close(_ctl_fd); _ctl_fd = -1;
+   return -1;
+}
+
+static void
+_snapshot(E_Client *ec, E_Wl_Proxy_Wm_State *st)
+{
+   memset(st, 0, sizeof(*st));
+   st->x = ec->x; st->y = ec->y; st->w = ec->w; st->h = ec->h;
+   if (ec->desk) { st->desk_x = ec->desk->x; st->desk_y = ec->desk->y; }
+   if (ec->zone) st->zone = (int)ec->zone->num;
+   st->layer = (int)ec->layer;
+   st->maximized = (unsigned int)ec->maximized;
+   st->fullscreen = ec->fullscreen ? 1 : 0;
+   st->sticky = ec->sticky ? 1 : 0;
+   st->shaded = ec->shaded ? 1 : 0;
+   st->iconified = ec->iconic ? 1 : 0;
+}
+
+/* Encode and send the client's current WM state. bump_stack/bump_focus assign
+ * the next monotonic sequence so the consumer can restore z-order/focus. */
+static void
+_mirror(E_Client *ec, Eina_Bool bump_stack, Eina_Bool bump_focus)
+{
+   E_Wl_Proxy_Wm_State st;
+   void *blob;
+   int size = 0;
+
+   if (!ec || !ec->uuid) return;          /* only session-recovery windows */
+   if (_ctl_fd < 0) return;
+
+   _snapshot(ec, &st);
+   if (bump_stack) st.stack_seq = ++_seq;
+   if (bump_focus) st.focus_seq = ++_seq;
+
+   blob = e_wl_proxy_wm_encode(&st, &size);
+   if (!blob || size <= 0) { free(blob); return; }
+   _ctl_send(E_WL_PROXY_CTL_WM_STATE, ec->uuid, blob, (uint32_t)size);
+   free(blob);
+}
+
+static Eina_Bool
+_cb_property(void *d EINA_UNUSED, int t EINA_UNUSED, void *ev)
+{
+   E_Event_Client_Property *e = ev;   /* PROPERTY carries an extra field; use its type */
+   _mirror(e->ec, EINA_FALSE, EINA_FALSE);
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_cb_stack(void *d EINA_UNUSED, int t EINA_UNUSED, void *ev)
+{
+   E_Event_Client *e = ev;
+   _mirror(e->ec, EINA_TRUE, EINA_FALSE);
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_cb_focus_in(void *d EINA_UNUSED, int t EINA_UNUSED, void *ev)
+{
+   E_Event_Client *e = ev;
+   _mirror(e->ec, EINA_FALSE, EINA_TRUE);
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+/* window moved or resized: capture the new geometry. E emits MOVE/RESIZE as their
+ * own events (not PROPERTY), so without these the store keeps a stale position. */
+static Eina_Bool
+_cb_geom(void *d EINA_UNUSED, int t EINA_UNUSED, void *ev)
+{
+   E_Event_Client *e = ev;
+   _mirror(e->ec, EINA_FALSE, EINA_FALSE);
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_cb_remove(void *d EINA_UNUSED, int t EINA_UNUSED, void *ev)
+{
+   E_Event_Client *e = ev;
+   /* normal close while E is alive: drop the blob (a crash sends nothing,
+    * so the blob is retained and later restored). */
+   if (e->ec && e->ec->uuid && (_ctl_fd >= 0))
+     _ctl_send(E_WL_PROXY_CTL_WM_FORGET, e->ec->uuid, NULL, 0);
+   {
+      Eina_List *l;
+      Restored *r;
+      EINA_LIST_FOREACH(_restored, l, r)
+        if (r->ec == e->ec) { _restored = eina_list_remove_list(_restored, l); free(r); break; }
+   }
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static void
+_state_free(void *d)
+{
+   free(d);   /* E_Wl_Proxy_Wm_State, malloc'd by the codec decode */
+}
+
+static void
+_drain_inbuf(void)
+{
+   for (;;)
+     {
+        char *uuid = NULL;
+        E_Wl_Proxy_Wm_State *st = NULL;
+        size_t consumed = 0;
+        const unsigned char *b = eina_binbuf_string_get(_inbuf);
+        size_t len = eina_binbuf_length_get(_inbuf);
+        int r = e_wl_proxy_recv_frame(b, len, &uuid, &st, &consumed);
+        if (r == 0) break;                                  /* need more */
+        if (r < 0) { eina_binbuf_reset(_inbuf); break; }     /* malformed: drop */
+        if (uuid && st)
+          {
+             /* eina_hash_set returns the prior value WITHOUT calling the
+              * free_cb, so free it ourselves to avoid leaking on updates. */
+             E_Wl_Proxy_Wm_State *old = eina_hash_set(_states, uuid, st);
+             free(old);
+          }
+        else free(st);
+        free(uuid);
+        eina_binbuf_remove(_inbuf, 0, consumed);
+     }
+}
+
+static Eina_Bool
+_ctl_read_cb(void *d EINA_UNUSED, Ecore_Fd_Handler *fdh)
+{
+   char buf[4096];
+   ssize_t n;
+
+   if (ecore_main_fd_handler_active_get(fdh, ECORE_FD_ERROR)) goto dead;
+   n = recv(_ctl_fd, buf, sizeof(buf), 0);
+   if (n == 0) goto dead;
+   if (n < 0)
+     {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) return ECORE_CALLBACK_RENEW;
+        goto dead;
+     }
+   eina_binbuf_append_length(_inbuf, (const unsigned char *)buf, (size_t)n);
+   _drain_inbuf();
+   return ECORE_CALLBACK_RENEW;
+
+dead:
+   ecore_main_fd_handler_del(_ctl_h); _ctl_h = NULL;
+   /* close the fd too: leaving it open makes every later window event try (and
+    * fail) a sendmsg on a dead socket before _ctl_send finally closes it. */
+   if (_ctl_fd >= 0) { close(_ctl_fd); _ctl_fd = -1; }
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static int
+_by_stack(const void *a, const void *b)
+{
+   const Restored *ra = a, *rb = b;
+   return (ra->stack_seq > rb->stack_seq) - (ra->stack_seq < rb->stack_seq);
+}
+
+/* Re-stack all restored windows by ascending stack_seq (raising each in turn
+ * leaves the highest seq on top), then focus the max focus_seq. Called after
+ * each window is restored; converges as windows arrive. */
+static void
+_reorder(void)
+{
+   Eina_List *sorted, *l;
+   Restored *r, *focus_win = NULL;
+   unsigned int best_focus = 0;
+
+   sorted = eina_list_clone(_restored);
+   sorted = eina_list_sort(sorted, eina_list_count(sorted), _by_stack);
+   EINA_LIST_FOREACH(sorted, l, r)
+     {
+        if (r->ec && r->ec->frame) evas_object_raise(r->ec->frame);
+        if (r->focus_seq >= best_focus) { best_focus = r->focus_seq; focus_win = r; }
+     }
+   eina_list_free(sorted);
+
+   if (focus_win && focus_win->ec && (best_focus > 0))
+     e_client_activate(focus_win->ec, EINA_TRUE);
+}
+
+static void
+_apply(E_Client *ec, const E_Wl_Proxy_Wm_State *st)
+{
+   /* geometry (frame coords) — overrides E_Remember's possibly-stale baseline.
+    * For a maximized window this is the pre-maximize geometry; e_client_maximize
+    * below recomputes the maximized geometry, so this is harmlessly overridden. */
+   if (ec->frame)
+     evas_object_geometry_set(ec->frame, st->x, st->y, st->w, st->h);
+
+   /* virtual desktop */
+   if (ec->zone)
+     {
+        E_Desk *desk = e_desk_at_xy_get(ec->zone, st->desk_x, st->desk_y);
+        if (desk) e_client_desk_set(ec, desk);
+     }
+
+   /* maximize / fullscreen / shade / iconify */
+   if (st->maximized) e_client_maximize(ec, (E_Maximize)st->maximized);
+   if (st->fullscreen) e_client_fullscreen(ec, E_FULLSCREEN_RESIZE);
+   if (st->shaded) e_client_shade(ec, E_DIRECTION_UP);
+   if (st->iconified) e_client_iconify(ec);
+}
+
+static void
+_uuid_set_hook(void *d EINA_UNUSED, E_Client *ec)
+{
+   E_Wl_Proxy_Wm_State *st;
+   Restored *r;
+
+   if (!ec || !ec->uuid || !_states) return;
+   st = eina_hash_find(_states, ec->uuid);
+   if (!st) return;
+   _apply(ec, st);
+
+   r = calloc(1, sizeof(*r));
+   if (!r) return;
+   r->ec = ec; r->stack_seq = st->stack_seq; r->focus_seq = st->focus_seq;
+   _restored = eina_list_append(_restored, r);
+   /* snapshot consumed: drop it so a second hook fire is a no-op and the blob
+    * is freed (the window is now live and re-mirrored by the producer). */
+   eina_hash_del(_states, ec->uuid, NULL);
+   _reorder();
+}
+
+E_API void *
+e_modapi_init(E_Module *m)
+{
+   e_wl_proxy_wm_codec_init();
+   _ctl_fd = _ctl_connect();
+   if (_ctl_fd >= 0) _ctl_send(E_WL_PROXY_CTL_HELLO, NULL, NULL, 0);
+   _states = eina_hash_string_superfast_new(_state_free);
+   if (_ctl_fd >= 0)
+     {
+        _inbuf = eina_binbuf_new();
+        _ctl_h = ecore_main_fd_handler_add(_ctl_fd, ECORE_FD_READ | ECORE_FD_ERROR,
+                                           _ctl_read_cb, NULL, NULL, NULL);
+     }
+   _uuid_hook = e_client_hook_add(E_CLIENT_HOOK_UUID_SET, _uuid_set_hook, NULL);
+   /* a missing proxy is not fatal: the module simply mirrors nothing */
+   E_LIST_HANDLER_APPEND(_handlers, E_EVENT_CLIENT_PROPERTY, _cb_property, NULL);
+   E_LIST_HANDLER_APPEND(_handlers, E_EVENT_CLIENT_MOVE,     _cb_geom,     NULL);
+   E_LIST_HANDLER_APPEND(_handlers, E_EVENT_CLIENT_RESIZE,   _cb_geom,     NULL);
+   E_LIST_HANDLER_APPEND(_handlers, E_EVENT_CLIENT_STACK,    _cb_stack,    NULL);
+   E_LIST_HANDLER_APPEND(_handlers, E_EVENT_CLIENT_FOCUS_IN, _cb_focus_in, NULL);
+   E_LIST_HANDLER_APPEND(_handlers, E_EVENT_CLIENT_REMOVE,   _cb_remove,   NULL);
+   return m;
+}
+
+E_API int
+e_modapi_shutdown(E_Module *m EINA_UNUSED)
+{
+   if (_ctl_fd >= 0)
+     {
+        /* _ctl_send may itself close _ctl_fd (and set it to -1) on a hard send
+         * error, so re-check before closing to avoid a spurious close(-1). */
+        _ctl_send(E_WL_PROXY_CTL_BYE, NULL, NULL, 0);
+        if (_ctl_fd >= 0) { close(_ctl_fd); _ctl_fd = -1; }
+     }
+   E_FREE_LIST(_handlers, ecore_event_handler_del);
+   if (_uuid_hook) { e_client_hook_del(_uuid_hook); _uuid_hook = NULL; }
+   if (_ctl_h) { ecore_main_fd_handler_del(_ctl_h); _ctl_h = NULL; }
+   if (_inbuf) { eina_binbuf_free(_inbuf); _inbuf = NULL; }
+   { Restored *r; EINA_LIST_FREE(_restored, r) free(r); }
+   if (_states) { eina_hash_free(_states); _states = NULL; }
+   e_wl_proxy_wm_codec_shutdown();
+   return 1;
+}
+
+E_API int
+e_modapi_save(E_Module *m EINA_UNUSED)
+{
+   return 1;
+}
diff --git a/src/modules/wl_proxy/e_wl_proxy_recv.c b/src/modules/wl_proxy/e_wl_proxy_recv.c
new file mode 100644
index 000000000..d8ae12802
--- /dev/null
+++ b/src/modules/wl_proxy/e_wl_proxy_recv.c
@@ -0,0 +1,42 @@
+#include "config.h"
+#include "e_wl_proxy_recv.h"
+#include "e_wl_proxy_proto.h"
+#include <string.h>
+#include <stdlib.h>
+
+int
+e_wl_proxy_recv_frame(const void *buf, size_t len,
+                      char **uuid, E_Wl_Proxy_Wm_State **st, size_t *consumed)
+{
+   E_Wl_Proxy_Ctl_Hdr hdr;
+   size_t total;
+   const char *p = buf;
+
+   *uuid = NULL;
+   *st = NULL;
+
+   if (len < sizeof(hdr)) return 0;
+   memcpy(&hdr, buf, sizeof(hdr));
+   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;
+   *consumed = total;
+
+   if (hdr.type != E_WL_PROXY_CTL_WM_STATE) return 1;   /* ignore other types */
+
+   if (hdr.uuid_len)
+     {
+        *uuid = malloc(hdr.uuid_len + 1);
+        if (!*uuid) return 1;
+        memcpy(*uuid, p + sizeof(hdr), hdr.uuid_len);
+        (*uuid)[hdr.uuid_len] = '\0';
+     }
+   if (hdr.blob_len)
+     {
+        *st = e_wl_proxy_wm_decode(p + sizeof(hdr) + hdr.uuid_len, (int)hdr.blob_len);
+        if (!*st) { free(*uuid); *uuid = NULL; }
+     }
+   return 1;
+}
diff --git a/src/modules/wl_proxy/e_wl_proxy_recv.h b/src/modules/wl_proxy/e_wl_proxy_recv.h
new file mode 100644
index 000000000..ca35f0bf5
--- /dev/null
+++ b/src/modules/wl_proxy/e_wl_proxy_recv.h
@@ -0,0 +1,15 @@
+#ifndef E_WL_PROXY_RECV_H
+#define E_WL_PROXY_RECV_H
+
+#include <stddef.h>
+#include "e_wl_proxy_wm.h"
+
+/* Decode one control frame from buf[0..len). On a complete WM_STATE frame:
+ * sets *uuid (NUL-terminated, malloc'd — caller frees), *st (malloc'd decoded
+ * state — caller frees), *consumed to the frame size, returns 1. On a complete
+ * non-WM_STATE frame: sets uuid and st to NULL, *consumed to its size, returns 1.
+ * If more bytes are needed: returns 0. On a malformed frame: returns -1. */
+int e_wl_proxy_recv_frame(const void *buf, size_t len,
+                          char **uuid, E_Wl_Proxy_Wm_State **st, size_t *consumed);
+
+#endif
diff --git a/src/modules/wl_proxy/e_wl_proxy_wm.h b/src/modules/wl_proxy/e_wl_proxy_wm.h
new file mode 100644
index 000000000..b5dc94eaf
--- /dev/null
+++ b/src/modules/wl_proxy/e_wl_proxy_wm.h
@@ -0,0 +1,29 @@
+#ifndef E_WL_PROXY_WM_H
+#define E_WL_PROXY_WM_H
+
+/* A flat, self-sufficient snapshot of a window's WM state. Serialized to an
+ * Eet blob the proxy stores opaquely and returns on restore. Adding/removing
+ * a field is safe: Eet's descriptor versioning fills absent fields with 0. */
+typedef struct _E_Wl_Proxy_Wm_State
+{
+   int           x, y, w, h;     /* frame geometry */
+   int           desk_x, desk_y; /* virtual desktop */
+   int           zone;           /* zone (monitor) index */
+   int           layer;          /* E_Layer stacking layer */
+   unsigned int  maximized;      /* E_Maximize direction bitmask */
+   unsigned int  stack_seq;      /* bumped on raise; higher = nearer top */
+   unsigned int  focus_seq;      /* bumped on focus; max = focused window  */
+   unsigned char fullscreen;
+   unsigned char sticky;
+   unsigned char shaded;
+   unsigned char iconified;
+} E_Wl_Proxy_Wm_State;
+
+void                 e_wl_proxy_wm_codec_init(void);
+void                 e_wl_proxy_wm_codec_shutdown(void);
+/* Encode st into a freshly malloc'd blob; *size_out gets its length. Caller frees. */
+void                *e_wl_proxy_wm_encode(const E_Wl_Proxy_Wm_State *st, int *size_out);
+/* Decode a blob into a freshly malloc'd state. Caller frees. NULL on error. */
+E_Wl_Proxy_Wm_State *e_wl_proxy_wm_decode(const void *blob, int size);
+
+#endif
diff --git a/src/modules/wl_proxy/e_wl_proxy_wm_codec.c b/src/modules/wl_proxy/e_wl_proxy_wm_codec.c
new file mode 100644
index 000000000..796e201bd
--- /dev/null
+++ b/src/modules/wl_proxy/e_wl_proxy_wm_codec.c
@@ -0,0 +1,48 @@
+#include "config.h"
+#include "e_wl_proxy_wm.h"
+#include <Eet.h>
+#include <Eina.h>
+#include <stdlib.h>
+
+static Eet_Data_Descriptor *_edd = NULL;
+
+void
+e_wl_proxy_wm_codec_init(void)
+{
+   Eet_Data_Descriptor_Class eddc;
+
+   if (_edd) return;
+   EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, E_Wl_Proxy_Wm_State);
+   _edd = eet_data_descriptor_stream_new(&eddc);
+
+#define D(member, type) \
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edd, E_Wl_Proxy_Wm_State, #member, member, EET_T_##type)
+   D(x, INT); D(y, INT); D(w, INT); D(h, INT);
+   D(desk_x, INT); D(desk_y, INT);
+   D(zone, INT); D(layer, INT);
+   D(maximized, UINT); D(stack_seq, UINT); D(focus_seq, UINT);
+   D(fullscreen, UCHAR); D(sticky, UCHAR); D(shaded, UCHAR); D(iconified, UCHAR);
+#undef D
+}
+
+void
+e_wl_proxy_wm_codec_shutdown(void)
+{
+   if (!_edd) return;
+   eet_data_descriptor_free(_edd);
+   _edd = NULL;
+}
+
+void *
+e_wl_proxy_wm_encode(const E_Wl_Proxy_Wm_State *st, int *size_out)
+{
+   if (!_edd || !st) return NULL;
+   return eet_data_descriptor_encode(_edd, st, size_out);
+}
+
+E_Wl_Proxy_Wm_State *
+e_wl_proxy_wm_decode(const void *blob, int size)
+{
+   if (!_edd || !blob || size <= 0) return NULL;
+   return eet_data_descriptor_decode(_edd, blob, size);
+}
diff --git a/src/modules/wl_proxy/meson.build b/src/modules/wl_proxy/meson.build
new file mode 100644
index 000000000..f83267bd7
--- /dev/null
+++ b/src/modules/wl_proxy/meson.build
@@ -0,0 +1,9 @@
+src = ""
+  'e_mod_main.c',
+  'e_wl_proxy_wm_codec.c',
+  'e_wl_proxy_wm.h',
+  'e_wl_proxy_recv.c',
+  'e_wl_proxy_recv.h'
+)
+
+no_icon = true

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to