diff --git a/Cargo.lock b/Cargo.lock index cc635f6..944389b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,15 @@ dependencies = [ "winit", ] +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + [[package]] name = "adler2" version = "2.0.1" @@ -270,7 +279,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 1.0.8", + "rustix 1.1.2", "slab", "windows-sys 0.60.2", ] @@ -301,7 +310,7 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "rustix 1.0.8", + "rustix 1.1.2", ] [[package]] @@ -327,7 +336,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 1.0.8", + "rustix 1.1.2", "signal-hook-registry", "slab", "windows-sys 0.60.2", @@ -412,6 +421,21 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -618,7 +642,9 @@ dependencies = [ "eframe", "egui_extras", "eyre", + "rustix 1.1.2", "serde", + "tokio", "tracing", "tracing-subscriber", "wayland-client", @@ -1242,7 +1268,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" dependencies = [ - "rustix 1.0.8", + "rustix 1.1.2", "windows-targets 0.52.6", ] @@ -1269,6 +1295,12 @@ dependencies = [ "wasi 0.14.4+wasi-0.2.4", ] +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + [[package]] name = "gl_generator" version = "0.14.0" @@ -1590,6 +1622,17 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "libc", +] + [[package]] name = "jni" version = "0.21.1" @@ -1696,9 +1739,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -1819,6 +1862,17 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + [[package]] name = "moxcms" version = "0.7.5" @@ -2243,6 +2297,15 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -2469,7 +2532,7 @@ dependencies = [ "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.0.8", + "rustix 1.1.2", "windows-sys 0.60.2", ] @@ -2646,6 +2709,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2673,14 +2742,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags 2.9.4", "errno", "libc", - "linux-raw-sys 0.9.4", + "linux-raw-sys 0.11.0", "windows-sys 0.60.2", ] @@ -2857,6 +2926,16 @@ dependencies = [ "serde", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" @@ -2937,7 +3016,7 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.8", + "rustix 1.1.2", "windows-sys 0.60.2", ] @@ -3048,6 +3127,37 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "slab", + "socket2", + "tokio-macros", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -3323,7 +3433,7 @@ checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 1.0.8", + "rustix 1.1.2", "scoped-tls", "smallvec", "wayland-sys", @@ -3336,7 +3446,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ "bitflags 2.9.4", - "rustix 1.0.8", + "rustix 1.1.2", "wayland-backend", "wayland-scanner", ] @@ -3358,7 +3468,7 @@ version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" dependencies = [ - "rustix 1.0.8", + "rustix 1.1.2", "wayland-client", "xcursor", ] @@ -4155,7 +4265,7 @@ dependencies = [ "libc", "libloading", "once_cell", - "rustix 1.0.8", + "rustix 1.1.2", "x11rb-protocol", ] diff --git a/Cargo.toml b/Cargo.toml index 10e1f06..7874f7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,9 @@ dirs = "6.0.0" eframe = "0.32.2" egui_extras = { version = "0.32.2", features = ["image"] } eyre = "0.6.12" +rustix = "1.1.2" serde = "1.0.219" +tokio = { version = "1.47.1", features = ["full", "sync"] } tracing = { version = "0.1.41", features = ["attributes"] } tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } wayland-client = "0.31.11" diff --git a/src/daemon.rs b/src/daemon.rs index a9f463c..498ba15 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -3,7 +3,11 @@ use super::MAX_ENTRY_SIZE; use eframe::egui::ahash::HashSet; use eyre::Context; use eyre::bail; +use rustix::fs::OFlags; +use rustix::fs::fcntl_setfl; +use rustix::io::FdFlags; use std::collections::HashMap; +use std::future::poll_fn; use std::io::{BufReader, BufWriter, PipeWriter, Read, Write}; use std::os::fd::AsFd; use std::os::unix::net::{UnixListener, UnixStream}; @@ -11,12 +15,15 @@ use std::path::PathBuf; use std::sync::{Arc, Mutex, OnceLock, atomic::AtomicU64}; use std::time::Duration; use std::time::SystemTime; +use tracing::error; use tracing::info; use tracing::warn; use tracing_subscriber::EnvFilter; -use wayland_client::{Dispatch, Proxy, QueueHandle, backend::ObjectId, event_created_child}; +use wayland_client::protocol::wl_callback::WlCallback; +use wayland_client::protocol::wl_display::WlDisplay; use wayland_client::protocol::wl_registry::WlRegistry; use wayland_client::protocol::wl_seat::WlSeat; +use wayland_client::{Dispatch, Proxy, QueueHandle, backend::ObjectId, event_created_child}; use wayland_protocols::ext::data_control::v1::client::ext_data_control_device_v1; use wayland_protocols::ext::data_control::v1::client::ext_data_control_device_v1::{ EVT_DATA_OFFER_OPCODE, ExtDataControlDeviceV1, @@ -32,10 +39,12 @@ struct SharedState { // for deduplication because the event stream will tell us that we just copied something :) last_copied_item_id: AtomicU64, items: Mutex>, + select_history_send: tokio::sync::mpsc::Sender, data_control_manager: OnceLock, data_control_devices: Mutex>, qh: QueueHandle, + d: WlDisplay, } struct InProgressOffer { @@ -61,6 +70,19 @@ struct WlState { current_selection: Option, } +impl Dispatch for WlState { + fn event( + _state: &mut Self, + _proxy: &WlCallback, + _event: ::Event, + data: &String, + _conn: &wayland_client::Connection, + _qhandle: &QueueHandle, + ) { + info!("Received sync back {data}"); + } +} + impl Dispatch for WlState { fn event( state: &mut Self, @@ -199,7 +221,7 @@ impl Dispatch for WlState { state.current_selection = new_offer; - if let Some(offer) = &state.current_selection { + if let Some(offer) = state.current_selection.take() { let Some(mime) = ["text/plain", "image/png"] .iter() .find(|mime| offer.mime_types.contains(**mime)) @@ -223,6 +245,8 @@ impl Dispatch for WlState { if let Err(err) = result { warn!("Failed to read clipboard: {:?}", err) } + + offer.offer.destroy(); }); } } @@ -333,16 +357,30 @@ pub fn main(socket_path: &PathBuf) -> eyre::Result<()> { let conn = wayland_client::Connection::connect_to_env().wrap_err("connecting to the compositor")?; + rustix::fs::fcntl_setfl(conn.as_fd(), OFlags::NONBLOCK).expect("TODO"); + let mut queue = conn.new_event_queue::(); + let (select_history_send, mut select_history_recv) = + tokio::sync::mpsc::channel::(1); + let shared_state = Arc::new(SharedState { next_item_id: AtomicU64::new(0), last_copied_item_id: AtomicU64::new(u64::MAX), items: Mutex::new(Vec::::new()), + select_history_send, data_control_manager: OnceLock::new(), data_control_devices: Mutex::new(HashMap::new()), qh: queue.handle(), + d: conn.display(), + }); + + shared_state.items.lock().unwrap().push(HistoryItem { + id: 3548235782, + mime: "text/plain".into(), + data: b"meow".to_vec().into(), + created_time: 0, }); let history_state2 = shared_state.clone(); @@ -372,14 +410,50 @@ pub fn main(socket_path: &PathBuf) -> eyre::Result<()> { } std::thread::spawn(move || { - loop { - let result = queue - .blocking_dispatch(&mut wl_state) - .wrap_err("handling wayland"); - if let Err(err) = result { - warn!("Received error from Wayland: {:?}", err); - } - } + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + loop { + info!("about to cook"); + + queue.dispatch_pending(&mut wl_state).expect("todo"); + + let read_guard = queue.prepare_read().expect("todo"); + + let fd = + tokio::io::unix::AsyncFd::new(read_guard.connection_fd()).expect("todo"); + + info!("gonna wait, maybe forever"); + + tokio::select! { + result = fd.readable() => { + info!("we are ready to read!"); + if let Err(err) = result { + error!("Received error from Wayland: {:?}", err); + std::process::exit(1); + } + + drop(fd); + + read_guard.read().expect("todo"); + } + item = select_history_recv.recv() => { + info!("received thing from channel"); + match item { + None => { + error!("IPC socket thread hung up"); + std::process::exit(1); + } + Some(item) => { + do_copy_into_clipboard(item, &wl_state.shared_state); + } + } + } + } + } + }); }); info!("Listening on {}", socket_path.display()); @@ -437,16 +511,28 @@ fn handle_copy(mut peer: UnixStream, shared_state: &SharedState) -> Result<(), e }; let entry = items.remove(idx); items.push(entry.clone()); + shared_state.select_history_send.try_send(entry)?; + Ok(()) +} - drop(items); - +fn do_copy_into_clipboard( + entry: HistoryItem, + shared_state: &SharedState, +) -> Result<(), eyre::Error> { for device in &*shared_state.data_control_devices.lock().unwrap() { + shared_state + .d + .sync(&shared_state.qh, "before create_data_source".into()); let data_source = shared_state .data_control_manager .get() .expect("data manger not found") .create_data_source(&shared_state.qh, OfferData(entry.data.clone())); + shared_state + .d + .sync(&shared_state.qh, "after create_data_source".into()); + if entry.mime == "text/plain" { // Just like wl_clipboard_rs, we also offer some extra mimes for text. let text_mimes = [ @@ -459,13 +545,21 @@ fn handle_copy(mut peer: UnixStream, shared_state: &SharedState) -> Result<(), e for mime in text_mimes { data_source.offer(mime.to_string()); } + } else { + data_source.offer(entry.mime.clone()); } - data_source.offer(entry.mime.clone()); + shared_state + .d + .sync(&shared_state.qh, "before set_selection".into()); info!("setting the selection"); device.1.set_selection(Some(&data_source)); + + shared_state + .d + .sync(&shared_state.qh, "setting the selection".into()); } shared_state