Files
feresaria/crates/ferese-ipc/src/theme.rs
T
Samuel Onoja 3d950a0411 Add resolved appearance engine and paired theme settings
Resolve light, dark and automatic appearance centrally and publish live theme snapshots to native surfaces and the appearance portal. Add paired preset families, custom theme imports, accessibility transforms and contrast-safe transitions.

Refine the Settings gallery and Auto controls, move overrides under advanced customization, and share palette-aware switches and circular slider thumbs. Document configuration and include theme licenses and integration coverage.
2026-09-30 12:51:49 +01:00

90 lines
2.9 KiB
Rust

use std::io;
use std::net::Shutdown;
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
use std::time::Duration;
use ferese_config::theme::Snapshot;
use serde_json::{Value, json};
use crate::{Request, Response, VERSION, read_frame, write_frame};
pub fn socket_path() -> io::Result<PathBuf> {
std::env::var_os("XDG_RUNTIME_DIR")
.filter(|value| !value.is_empty())
.map(|directory| PathBuf::from(directory).join("ferese/control.sock"))
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "XDG_RUNTIME_DIR is not set"))
}
pub struct Connection {
stream: UnixStream,
id: u64,
}
impl Connection {
pub fn connect() -> io::Result<Self> {
let stream = UnixStream::connect(socket_path()?)?;
stream.set_write_timeout(Some(Duration::from_secs(2)))?;
stream.set_read_timeout(Some(Duration::from_secs(2)))?;
Ok(Self { stream, id: 0 })
}
pub fn cancellation(&self) -> io::Result<Cancellation> {
self.stream.try_clone().map(Cancellation)
}
pub fn call(&mut self, command: &str, args: Value) -> Result<Value, String> {
self.id = self.id.wrapping_add(1);
let request = Request {
version: VERSION,
id: self.id,
kind: "command".into(),
command: command.into(),
args,
};
write_frame(&mut self.stream, &request).map_err(|e| e.to_string())?;
let response: Response = read_frame(&mut self.stream).map_err(|e| e.to_string())?;
if response.id != self.id || response.version != VERSION {
return Err("Unexpected theme IPC response".into());
}
if let Some(error) = response.error {
return Err(error.message);
}
response.result.ok_or_else(|| "Missing theme snapshot".into())
}
pub fn get(&mut self) -> Result<Snapshot, String> {
let snapshot: Snapshot =
serde_json::from_value(self.call("theme-get", json!({}))?).map_err(|e| e.to_string())?;
if snapshot.version != ferese_config::theme::SCHEMA_VERSION {
return Err("Unsupported theme snapshot version".into());
}
Ok(snapshot)
}
pub fn watch(&mut self, revision: u64) -> Result<Snapshot, String> {
self.stream.set_read_timeout(None).map_err(|e| e.to_string())?;
let snapshot: Snapshot =
serde_json::from_value(self.call("theme-watch", json!({"since": revision}))?).map_err(|e| e.to_string())?;
if snapshot.version != ferese_config::theme::SCHEMA_VERSION {
return Err("Unsupported theme snapshot version".into());
}
Ok(snapshot)
}
}
pub struct Cancellation(UnixStream);
impl Drop for Cancellation {
fn drop(&mut self) {
let _ = self.0.shutdown(Shutdown::Both);
}
}
pub fn current() -> Snapshot {
Connection::connect()
.ok()
.and_then(|mut connection| connection.get().ok())
.unwrap_or_default()
}