Introduce IPC socket

This commit is contained in:
Candifloss 2025-12-31 15:25:07 +05:30
parent 8959cce9f9
commit a31baa3670
5 changed files with 85 additions and 7 deletions

View File

@ -21,6 +21,7 @@ toml = "0.9.8"
popcorn-conf = { path = "../popcorn-conf" } popcorn-conf = { path = "../popcorn-conf" }
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
popcorn-proto = { path = "../popcorn-proto" } popcorn-proto = { path = "../popcorn-proto" }
bincode = "1.3.3"
[build-dependencies] [build-dependencies]
slint-build = "1.14.1" slint-build = "1.14.1"

View File

@ -0,0 +1,56 @@
use std::fs;
use std::io::Read;
use std::os::unix::net::{UnixListener, UnixStream};
use std::path::PathBuf;
use std::sync::mpsc::Sender;
use dirs::runtime_dir;
use popcorn_proto::OsdArgs;
/// Start a Unix socket server and forward received messages to the UI thread.
pub fn start_ipc(tx: Sender<OsdArgs>) -> std::io::Result<()> {
let sock_path = socket_path();
// Remove leftover socket from a previous run.
let _ = fs::remove_file(&sock_path);
// Create parent directory if missing.
if let Some(parent) = sock_path.parent() {
fs::create_dir_all(parent)?;
}
// Bind the Unix domain socket.
let listener = UnixListener::bind(&sock_path)?;
// Run the IPC accept loop in a background thread.
std::thread::spawn(move || {
// Accept short-lived connections.
for stream in listener.incoming() {
if let Ok(stream) = stream {
// Read one message per connection.
if let Ok(args) = read_message(stream) {
// Forward to UI thread via channel.
let _ = tx.send(args);
}
}
}
});
Ok(())
}
/// Read a single serialized OsdArgs message from a socket.
fn read_message(mut stream: UnixStream) -> bincode::Result<OsdArgs> {
let mut buf = Vec::new();
// Read until client closes the connection.
stream.read_to_end(&mut buf)?;
// Deserialize binary message.
bincode::deserialize(&buf)
}
/// Compute the per-user runtime socket path.
fn socket_path() -> PathBuf {
// Prefer XDG runtime dir. Fallback to /tmp.
let base = runtime_dir().unwrap_or_else(|| PathBuf::from("/tmp"));
base.join("candywidgets").join("popcorn.sock")
}

View File

@ -1,18 +1,38 @@
mod ipc;
mod osd; mod osd;
use osd::OsdUi; use osd::OsdUi;
use popcorn_conf::PopcornConfig; use popcorn_conf::PopcornConfig;
use popcorn_proto::OsdArgs;
use std::sync::mpsc;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
// Load configuration once at startup.
let cfg = PopcornConfig::load_or_default(); let cfg = PopcornConfig::load_or_default();
// Create the OSD UI on the main (UI) thread.
let osd = OsdUi::new(&cfg)?; let osd = OsdUi::new(&cfg)?;
// start IPC loop here // Channel used to send IPC messages to the UI thread.
// for each message: let (tx, rx) = mpsc::channel::<OsdArgs>();
// osd.update(&args, &cfg)
// reset timer
let _ = slint::run_event_loop(); // Start IPC server in a background thread.
ipc::start_ipc(tx)?;
// Poll IPC messages on the UI thread.
slint::Timer::default().start(
slint::TimerMode::Repeated,
std::time::Duration::from_millis(16),
move || {
// Process all pending messages.
while let Ok(args) = rx.try_recv() {
let _ = osd.update(&args, &cfg);
}
},
);
// Run the Slint event loop.
slint::run_event_loop();
Ok(()) Ok(())
} }

View File

@ -12,5 +12,6 @@ keywords = ["OSD", "popup", "desktop", "cli"]
license = "GPL-3+" license = "GPL-3+"
[dependencies] [dependencies]
bincode = "1.3.3"
clap = { version = "4.5.53", features = ["derive"] } clap = { version = "4.5.53", features = ["derive"] }
popcorn-proto = { path = "../popcorn-proto" } popcorn-proto = { path = "../popcorn-proto" }

View File

@ -1,8 +1,8 @@
mod args; mod args;
use args::CliArgs; use args::CliArgs;
use popcorn_proto::OsdArgs;
use clap::Parser; use clap::Parser;
use popcorn_proto::OsdArgs;
fn main() { fn main() {
let cli = CliArgs::parse(); let cli = CliArgs::parse();