diff --git a/Cargo.lock b/Cargo.lock index c7679ce..9779e3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2229,6 +2229,7 @@ dependencies = [ "ratatui-image", "rklipd", "serde", + "serde_json", "uuid", ] @@ -2241,6 +2242,8 @@ dependencies = [ "directories", "image", "rusqlite", + "serde", + "serde_json", "uuid", "wayland-clipboard-listener", ] diff --git a/Cargo.toml b/Cargo.toml index 3a023c6..8413d63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,6 @@ image = "0.25.9" ratatui = "0.30.0" ratatui-image = { version = "10.0.6", features = ["crossterm"] } rklipd = {path = "rklipd"} -serde = "1.0.228" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.149" uuid = "1.22.0" diff --git a/rklipd/Cargo.lock b/rklipd/Cargo.lock index 4634b24..d4b5fd3 100644 --- a/rklipd/Cargo.lock +++ b/rklipd/Cargo.lock @@ -1303,6 +1303,8 @@ dependencies = [ "directories", "image", "rusqlite", + "serde", + "serde_json", "uuid", "wayland-clipboard-listener", ] @@ -1370,6 +1372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", + "serde_derive", ] [[package]] diff --git a/rklipd/Cargo.toml b/rklipd/Cargo.toml index bf9ccd6..4830f73 100644 --- a/rklipd/Cargo.toml +++ b/rklipd/Cargo.toml @@ -11,6 +11,8 @@ uuid = {version = "1.22.0", features = ["v4", "serde"]} rusqlite = "0.38.0" wayland-clipboard-listener = "0.6.0" directories = "6.0.0" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.149" [features] x11 = [] diff --git a/rklipd/src/ipc.rs b/rklipd/src/ipc.rs new file mode 100644 index 0000000..57f6f4d --- /dev/null +++ b/rklipd/src/ipc.rs @@ -0,0 +1,74 @@ +use crate::database::Database; +use crate::models::ClipboardData; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::io::{Read, Write}; +use std::os::unix::net::UnixListener; +use std::path::Path; +use std::sync::{Arc, Mutex}; + +// --- LE CONTRAT (Protocole) --- +#[derive(Serialize, Deserialize, Debug)] +pub enum IpcRequest { + GetHistory { limit: usize }, +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum IpcResponse { + History(Vec), +} + +pub fn start_server(db: Arc>, socket_path: &Path) { + if socket_path.exists() { + let _ = fs::remove_file(socket_path); + } + + let listener = match UnixListener::bind(socket_path) { + Ok(l) => l, + Err(e) => { + eprintln!("Error while creating socket {}", e); + return; + } + }; + println!("ipc server listening {:?}", socket_path); + + for stream in listener.incoming() { + match stream { + Ok(mut stream) => { + let db_clone = Arc::clone(&db); + + std::thread::spawn(move || { + let mut buffer = String::new(); + + if stream.read_to_string(&mut buffer).is_ok() { + if let Ok(request) = serde_json::from_str::(&buffer) { + match request { + IpcRequest::GetHistory { limit } => { + let db_lock = db_clone.lock().unwrap(); + + // TODO Implem read_history(limit) + let history = db_lock.read_history().unwrap_or_default(); + + let items: Vec = history + .into_iter() + .rev() + .take(limit) + .map(|entry| match entry.content { + ClipboardData::Text(t) => t, + ClipboardData::Image(img) => format!("{}.jpg", img.id), + }) + .collect(); + + let response = IpcResponse::History(items); + let response_json = serde_json::to_string(&response).unwrap(); + let _ = stream.write_all(response_json.as_bytes()); + } + } + } + } + }); + } + Err(e) => eprintln!("Erreur de connexion IPC: {}", e), + } + } +} diff --git a/rklipd/src/main.rs b/rklipd/src/main.rs index cbe3f23..302d6b9 100644 --- a/rklipd/src/main.rs +++ b/rklipd/src/main.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, Mutex}; mod clipboard; mod database; +mod ipc; mod models; mod monitor; mod ws; @@ -18,8 +19,13 @@ fn main() -> Result<(), Box> { let db = Arc::new(Mutex::new(Database::init(&dir_path_str)?)); - // println!("{:#?}", db.lock().unwrap().read_history()); + let socket_path = dir_path.join("rklip.sock"); + let db_for_ipc = Arc::clone(&db); + std::thread::spawn(move || { + crate::ipc::start_server(db_for_ipc, &socket_path); + }); + println!("rklipd starting..."); monitor::start(db, clipboard)?; Ok(()) diff --git a/src/app.rs b/src/app.rs index a4e7694..9f6fa4b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,7 +1,7 @@ +use crate::ipc; use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2}; use ratatui::widgets::ListState; use ratatui_image::{picker::Picker, protocol}; -use std::path::Path; #[derive(PartialEq)] pub enum Mode { @@ -27,13 +27,9 @@ impl App { pub fn new() -> Self { let mut list_state = ListState::default(); list_state.select(Some(0)); - let items = vec![ - "Ceci est un texte copiƩ.".to_string(), - "https://github.com/ratatui-org/ratatui".to_string(), - "30426b4d-26e0-45af-9fa4-25f4476387a8.jpg".to_string(), - "35789d6a-dea4-46de-90da-aee693a16031.jpg".to_string(), - "fn main() {\n println!(\"Hello\");\n}".to_string(), - ]; + + let items = ipc::fetch_history(100) + .unwrap_or_else(|| vec!["rklipd deamon unaccessible".to_string()]); let picker = Picker::from_query_stdio().unwrap_or_else(|_| Picker::halfblocks()); diff --git a/src/ipc.rs b/src/ipc.rs index e69de29..e9a59e3 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -0,0 +1,37 @@ +use serde::{Deserialize, Serialize}; +use std::io::{Read, Write}; +use std::os::unix::net::UnixStream; + +#[derive(Serialize, Deserialize, Debug)] +pub enum IpcRequest { + GetHistory { limit: usize }, +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum IpcResponse { + History(Vec), +} + +pub fn fetch_history(limit: usize) -> Option> { + let base_dir = directories::ProjectDirs::from("com", "zefad", "rklipd")? + .data_dir() + .to_path_buf(); + let socket_path = base_dir.join("rklip.sock"); + + if let Ok(mut stream) = UnixStream::connect(&socket_path) { + let req = IpcRequest::GetHistory { limit }; + let req_json = serde_json::to_string(&req).unwrap(); + + let _ = stream.write_all(req_json.as_bytes()); + let _ = stream.shutdown(std::net::Shutdown::Write); + + let mut response_buffer = String::new(); + if stream.read_to_string(&mut response_buffer).is_ok() { + if let Ok(IpcResponse::History(items)) = serde_json::from_str(&response_buffer) { + return Some(items); + } + } + } + + None +}