git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit5deed3ece05bfa9adad9e3c016c1847e4f4a2e1a
parentceaeaf5373
authorLucas Galante <[email protected]>
date2026-07-07 12:35
feat(config,ipc): typed config accessors and a shared IPC socket module

config: add get_bool/get_f32/get_i64/get_string/get_color/find_key over the
cached config, collapsing per-crate load_config + .pointer() accessors.
ipc: add socket_path(prefix) + send_command(prefix, cmd) for the
/tmp/<prefix>-<WAYLAND_DISPLAY>.sock convention duplicated across daemons.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

 src/config.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ipc.rs    | 29 +++++++++++++++++++++++++++++
 src/lib.rs    |  1 +
 3 files changed, 84 insertions(+)

diff --git a/src/config.rs b/src/config.rs
index 2e5824f..b7cc9ab 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -514,6 +514,60 @@ pub fn cached_config_content() -> String {
     CONFIG_CACHE.read().map(|c| c.raw_content.clone()).unwrap_or_default()
 }
 
+// ── Typed accessors over the cached config ──────────────────────────────────
+// Each reads the mtime-cached config and extracts a value at a JSON pointer
+// (e.g. "/notifications/enable"), returning the default when absent or mistyped.
+
+/// Read a boolean at `pointer` from the cached config, or `default`.
+pub fn get_bool(pointer: &str, default: bool) -> bool {
+    cached_config().pointer(pointer).and_then(|v| v.as_bool()).unwrap_or(default)
+}
+
+/// Read an f32 at `pointer` from the cached config, or `default`.
+pub fn get_f32(pointer: &str, default: f32) -> f32 {
+    cached_config()
+        .pointer(pointer)
+        .and_then(|v| v.as_f64())
+        .map(|f| f as f32)
+        .unwrap_or(default)
+}
+
+/// Read an i64 at `pointer` from the cached config, or `default`.
+pub fn get_i64(pointer: &str, default: i64) -> i64 {
+    cached_config().pointer(pointer).and_then(|v| v.as_i64()).unwrap_or(default)
+}
+
+/// Read a string at `pointer` from the cached config.
+pub fn get_string(pointer: &str) -> Option<String> {
+    cached_config().pointer(pointer).and_then(|v| v.as_str()).map(|s| s.to_string())
+}
+
+/// Read a hex color string at `pointer` and parse it to raw sRGB RGBA (`[0,1]`).
+/// Apply [`crate::color::srgb_to_linear`] if your render target expects linear.
+pub fn get_color(pointer: &str) -> Option<[f32; 4]> {
+    get_string(pointer).as_deref().and_then(crate::color::parse_hex_rgba)
+}
+
+/// Recursively search a JSON value for the first entry whose object key equals
+/// `key`, returning a reference to its value. Depth-first over objects and arrays.
+pub fn find_key<'a>(val: &'a serde_json::Value, key: &str) -> Option<&'a serde_json::Value> {
+    match val {
+        serde_json::Value::Object(map) => {
+            if let Some(found) = map.get(key) {
+                return Some(found);
+            }
+            for v in map.values() {
+                if let Some(found) = find_key(v, key) {
+                    return Some(found);
+                }
+            }
+            None
+        }
+        serde_json::Value::Array(arr) => arr.iter().find_map(|v| find_key(v, key)),
+        _ => None,
+    }
+}
+
 
 
 fn perform_rolling_backup(path: &str) {
diff --git a/src/ipc.rs b/src/ipc.rs
new file mode 100644
index 0000000..ac2d678
--- /dev/null
+++ b/src/ipc.rs
@@ -0,0 +1,29 @@
+//! Helpers for the CCE Unix-socket IPC convention: `/tmp/<prefix>-<WAYLAND_DISPLAY>.sock`.
+
+use std::io::{Read, Write};
+use std::os::unix::net::UnixStream;
+
+/// Path of a CCE IPC socket for `prefix`, keyed by `$WAYLAND_DISPLAY`.
+///
+/// `socket_path("cce")` → `/tmp/cce-<display>.sock` (the compositor control socket);
+/// `socket_path("cce-status-interface")` → the status socket. Falls back to
+/// `/tmp/<prefix>.sock` when `$WAYLAND_DISPLAY` is unset.
+pub fn socket_path(prefix: &str) -> String {
+    match std::env::var("WAYLAND_DISPLAY") {
+        Ok(d) if !d.is_empty() => format!("/tmp/{}-{}.sock", prefix, d),
+        _ => format!("/tmp/{}.sock", prefix),
+    }
+}
+
+/// Connect to the `prefix` socket, send `command` (newline-terminated), and
+/// return the reply text. Errors if the socket can't be reached.
+pub fn send_command(prefix: &str, command: &str) -> std::io::Result<String> {
+    let mut stream = UnixStream::connect(socket_path(prefix))?;
+    stream.write_all(command.as_bytes())?;
+    if !command.ends_with('\n') {
+        stream.write_all(b"\n")?;
+    }
+    let mut reply = String::new();
+    stream.read_to_string(&mut reply)?;
+    Ok(reply)
+}
diff --git a/src/lib.rs b/src/lib.rs
index ec961ce..3a3de1e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,6 +10,7 @@ pub mod backend;
 pub mod context;
 pub mod process;
 pub mod file_dialog;
+pub mod ipc;
 
 pub mod colors {
     pub use crate::color::*;