From 0c5b9d26e102d036d1d4f9e2b5ded7b512fb2321 Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Tue, 16 Sep 2025 20:32:15 +0200 Subject: [PATCH] we're gaming --- Cargo.lock | 136 ++++++++++++++----- Cargo.toml | 5 + src/daemon.rs | 359 +++++++++++++++++++++++++++++++++++++++---------- src/display.rs | 10 +- src/main.rs | 2 +- 5 files changed, 407 insertions(+), 105 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c98c49e..9049c24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -303,7 +303,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -338,7 +338,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -484,7 +484,7 @@ checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -610,9 +610,19 @@ dependencies = [ "egui_extras", "eyre", "serde", + "wayland-client", + "wayland-protocols", "wl-clipboard-rs", ] +[[package]] +name = "codegen" +version = "0.1.0" +dependencies = [ + "heck", + "strong-xml", +] + [[package]] name = "codespan-reporting" version = "0.12.0" @@ -765,7 +775,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -967,7 +977,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -988,7 +998,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -1091,7 +1101,7 @@ checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -1149,7 +1159,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -1200,7 +1210,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -1577,6 +1587,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "jetscii" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47f142fe24a9c9944451e8349de0a56af5f3e7226dc46f3ed4d4ecc0b85af75e" + [[package]] name = "jni" version = "0.21.1" @@ -1636,6 +1652,12 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.175" @@ -1923,7 +1945,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -2346,7 +2368,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn", + "syn 2.0.106", "unicase", ] @@ -2377,7 +2399,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -2687,7 +2709,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -2698,7 +2720,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -2821,6 +2843,30 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +[[package]] +name = "strong-xml" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d19fb3a618e2f1039e32317c9f525e6d45c55af704ec7c429aa74412419bebf" +dependencies = [ + "jetscii", + "lazy_static", + "memchr", + "strong-xml-derive", + "xmlparser", +] + +[[package]] +name = "strong-xml-derive" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c781f499321613b112be5d9338189ef1ed19689a01edd23d923ea57ad5c7e1" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "strum" version = "0.26.3" @@ -2840,7 +2886,18 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.106", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] @@ -2862,7 +2919,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -2913,7 +2970,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -2924,7 +2981,7 @@ checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -3012,7 +3069,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -3158,7 +3215,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -3193,7 +3250,7 @@ checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3505,6 +3562,13 @@ dependencies = [ "web-sys", ] +[[package]] +name = "willy" +version = "0.1.0" +dependencies = [ + "dirs", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3613,7 +3677,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -3624,7 +3688,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -3635,7 +3699,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -3646,7 +3710,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -4088,6 +4152,12 @@ version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "yoke" version = "0.8.0" @@ -4108,7 +4178,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "synstructure", ] @@ -4163,7 +4233,7 @@ checksum = "dc6821851fa840b708b4cbbaf6241868cabc85a2dc22f426361b0292bfc0b836" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "zbus-lockstep", "zbus_xml", "zvariant", @@ -4178,7 +4248,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.106", "zbus_names", "zvariant", "zvariant_utils", @@ -4226,7 +4296,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -4246,7 +4316,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "synstructure", ] @@ -4280,7 +4350,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -4321,7 +4391,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.106", "zvariant_utils", ] @@ -4334,6 +4404,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 2.0.106", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index b984bd8..51c87f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[workspace] +members = [".", "willy", "willy/codegen"] + [package] name = "clippyboard" version = "0.1.0" @@ -10,4 +13,6 @@ eframe = "0.32.2" egui_extras = { version = "0.32.2", features = ["image"] } eyre = "0.6.12" serde = "1.0.219" +wayland-client = "0.31.11" +wayland-protocols = { version = "0.32.9", features = ["staging"] } wl-clipboard-rs = "0.9.2" diff --git a/src/daemon.rs b/src/daemon.rs index 7425894..6a63ee4 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -1,56 +1,307 @@ -use super::Entry; +use super::HistoryItem; use super::MAX_ENTRY_SIZE; +use eframe::egui::ahash::HashSet; use eyre::Context; +use std::collections::HashMap; use std::io::BufWriter; use std::io::Read; +use std::os::fd::AsFd; use std::os::unix::net::UnixListener; use std::os::unix::net::UnixStream; use std::path::PathBuf; use std::sync::Arc; use std::sync::Mutex; use std::sync::atomic::AtomicU64; +use std::time::Duration; use std::time::SystemTime; +use wayland_client::Dispatch; +use wayland_client::Proxy; +use wayland_client::backend::ObjectId; +use wayland_client::event_created_child; +use wayland_client::globals::GlobalListContents; +use wayland_client::globals::registry_queue_init; +use wayland_client::protocol::wl_registry::WlRegistry; +use wayland_client::protocol::wl_seat::WlSeat; +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, +}; +use wayland_protocols::ext::data_control::v1::client::ext_data_control_manager_v1::ExtDataControlManagerV1; +use wayland_protocols::ext::data_control::v1::client::ext_data_control_offer_v1; +use wayland_protocols::ext::data_control::v1::client::ext_data_control_offer_v1::ExtDataControlOfferV1; use wl_clipboard_rs::paste::ClipboardType; use wl_clipboard_rs::paste::MimeType; use wl_clipboard_rs::paste::Seat; -pub(crate) fn handle_peer( - mut peer: UnixStream, - next_id: Arc, - last_copied: Arc, - items: Arc>>, -) -> eyre::Result<()> { +struct HistoryState { + next_item_id: Arc, + last_copied_item_id: Arc, + items: Arc>>, +} + +struct InProgressOffer { + mime_types: HashSet, + time: Duration, +} + +#[derive(Debug)] +struct CurrentSelection { + mime_types: HashSet, + offer: ExtDataControlOfferV1, + time: Duration, +} + +struct WlState { + history_state: Arc, + + offers: HashMap, + current_primary_selection: Option, + current_selection: Option, +} + +impl Dispatch for WlState { + fn event( + _state: &mut Self, + _proxy: &WlRegistry, + _event: ::Event, + _data: &GlobalListContents, + _conn: &wayland_client::Connection, + _qhandle: &wayland_client::QueueHandle, + ) { + } +} +impl Dispatch for WlState { + fn event( + _state: &mut Self, + _proxy: &ExtDataControlManagerV1, + _event: ::Event, + _data: &(), + _conn: &wayland_client::Connection, + _qhandle: &wayland_client::QueueHandle, + ) { + } +} +impl Dispatch for WlState { + fn event( + _state: &mut Self, + _proxy: &WlSeat, + _event: ::Event, + _data: &(), + _conn: &wayland_client::Connection, + _qhandle: &wayland_client::QueueHandle, + ) { + } +} +impl Dispatch for WlState { + fn event( + state: &mut Self, + _proxy: &ExtDataControlDeviceV1, + event: ::Event, + _data: &(), + _conn: &wayland_client::Connection, + _qhandle: &wayland_client::QueueHandle, + ) { + match event { + // A new offer is being prepared, register it and don't do anything yet + ext_data_control_device_v1::Event::DataOffer { id } => { + state.offers.insert( + id.id(), + InProgressOffer { + mime_types: Default::default(), + time: SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap(), + }, + ); + } + + // The selection has been confirmed, we just properly got a new offer that we should use. + ext_data_control_device_v1::Event::Selection { id } => { + let new_offer = match id { + Some(id) => { + let offer = state.offers.remove(&id.id()); + + offer.map(|offer| CurrentSelection { + offer: id, + mime_types: offer.mime_types, + time: offer.time, + }) + } + None => None, + }; + + if let Some(current) = &state.current_selection { + current.offer.destroy(); + } + + state.current_selection = new_offer; + + if let Some(offer) = &state.current_selection { + let Some(mime) = ["text/plain", "image/png"] + .iter() + .find(|mime| offer.mime_types.contains(**mime)) + else { + eprintln!( + "WARN: No supported mime type found. Found mime types: {:?}", + offer.mime_types + ); + return; + }; + + let (reader, writer) = std::io::pipe().unwrap(); + offer.offer.receive(mime.to_string(), writer.as_fd()); + + let history_state = state.history_state.clone(); + let mime = mime.to_string(); + let time = offer.time; + std::thread::spawn(move || { + let result = + do_read_clipboard_into_history(&history_state, time, mime, reader); + if let Err(err) = result { + eprintln!("WARN: Failed to read clipboard: {:?}", err) + } + }); + } + } + ext_data_control_device_v1::Event::PrimarySelection { id } => { + let new_offer = match id { + Some(id) => { + let offer = state.offers.remove(&id.id()); + + offer.map(|offer| CurrentSelection { + offer: id, + mime_types: offer.mime_types, + time: offer.time, + }) + } + None => None, + }; + + if let Some(current) = &state.current_primary_selection { + current.offer.destroy(); + } + + state.current_primary_selection = new_offer; + } + _ => {} + } + } + + event_created_child!(WlState, ExtDataControlDeviceV1, [ + EVT_DATA_OFFER_OPCODE => (ExtDataControlOfferV1, ()), + ]); +} + +impl Dispatch for WlState { + fn event( + state: &mut Self, + proxy: &ExtDataControlOfferV1, + event: ::Event, + _data: &(), + _conn: &wayland_client::Connection, + _qhandle: &wayland_client::QueueHandle, + ) { + match event { + ext_data_control_offer_v1::Event::Offer { mime_type } => { + if let Some(offer) = state.offers.get_mut(&proxy.id()) { + offer.mime_types.insert(mime_type); + } + } + _ => {} + } + } +} + +pub fn main(socket_path: &PathBuf) -> eyre::Result<()> { + let _ = std::fs::remove_file(&socket_path); // lol + let socket = UnixListener::bind(&socket_path) + .wrap_err_with(|| format!("binding path {}", socket_path.display()))?; + + let conn = + wayland_client::Connection::connect_to_env().wrap_err("connecting to the compositor")?; + + let (globals, mut queue) = + registry_queue_init::(&conn).wrap_err("initializing wayland connection")?; + + let data_manager = globals.bind::(&queue.handle(), 1..=1, ()).wrap_err("getting ext_data_control_manager_v1, is ext-data-control-v1 not supported by the compositor?")?; + + let seat = globals + .bind::(&queue.handle(), 1..=1, ()) + .wrap_err("getting seat")?; + + let data_device = data_manager.get_data_device(&seat, &queue.handle(), ()); + + let history_state = Arc::new(HistoryState { + next_item_id: Arc::new(AtomicU64::new(0)), + last_copied_item_id: Arc::new(AtomicU64::new(u64::MAX)), + items: Arc::new(Mutex::new(Vec::::new())), + // for deduplication because the event stream will tell us that we just copied something :) + }); + + let history_state2 = history_state.clone(); + std::thread::spawn(move || { + let mut state = WlState { + offers: HashMap::new(), + current_primary_selection: None, + current_selection: None, + + history_state: history_state2, + }; + loop { + queue.blocking_dispatch(&mut state); + } + }); + + println!("INFO: Listening on {}", socket_path.display()); + + for peer in socket.incoming() { + match peer { + Ok(peer) => { + let history_state = history_state.clone(); + std::thread::spawn(move || { + let result = handle_peer(peer, &history_state); + if let Err(err) = result { + eprintln!("ERROR: Error handling peer: {err:?}"); + } + }); + } + Err(err) => { + eprintln!("ERROR: Error accepting peer: {err}"); + } + } + } + + Ok(()) +} + +fn handle_peer(mut peer: UnixStream, history_state: &HistoryState) -> eyre::Result<()> { let mut request = [0; 1]; let Ok(()) = peer.read_exact(&mut request) else { return Ok(()); }; match request[0] { super::MESSAGE_STORE => { - handle_store(next_id, last_copied, &items).wrap_err("handling store message")?; + handle_store(history_state).wrap_err("handling store message")?; } super::MESSAGE_READ => { - let items = items.lock().unwrap(); + let items = history_state.items.lock().unwrap(); ciborium::into_writer(items.as_slice(), BufWriter::new(peer)) .wrap_err("writing items to socket")?; } super::MESSAGE_COPY => { - handle_copy(peer, last_copied, items).wrap_err("handling copy message")?; + handle_copy(peer, history_state).wrap_err("handling copy message")?; } _ => {} }; Ok(()) } -fn handle_copy( - mut peer: UnixStream, - last_copied: Arc, - items: Arc>>, -) -> Result<(), eyre::Error> { +fn handle_copy(mut peer: UnixStream, history_state: &HistoryState) -> Result<(), eyre::Error> { let mut id = [0; 8]; peer.read_exact(&mut id).wrap_err("failed to read id")?; let id = u64::from_le_bytes(id); - let mut items = items.lock().unwrap(); + let mut items = history_state.items.lock().unwrap(); let Some(idx) = items.iter().position(|item| item.id == id) else { return Ok(()); }; @@ -64,18 +315,16 @@ fn handle_copy( wl_clipboard_rs::copy::Source::Bytes(entry.data.into_boxed_slice()), wl_clipboard_rs::copy::MimeType::Specific(entry.mime), ); - last_copied.store(entry.id, std::sync::atomic::Ordering::Relaxed); + history_state + .last_copied_item_id + .store(entry.id, std::sync::atomic::Ordering::Relaxed); if let Err(err) = result { println!("WARNING: Copy failed: {err:?}"); } Ok(()) } -fn handle_store( - next_id: Arc, - last_copied: Arc, - items: &Arc>>, -) -> Result<(), eyre::Error> { +fn handle_store(history_state: &HistoryState) -> Result<(), eyre::Error> { let time = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap(); @@ -99,20 +348,30 @@ fn handle_store( ) .wrap_err("getting contents")?; - let mut data_reader = data_readear.take(MAX_ENTRY_SIZE); + do_read_clipboard_into_history(&history_state, time, mime.to_string(), data_readear)?; + Ok(()) +} + +fn do_read_clipboard_into_history( + history_state: &HistoryState, + time: std::time::Duration, + mime: String, + data_reader: impl Read, +) -> Result<(), eyre::Error> { + let mut data_reader = data_reader.take(MAX_ENTRY_SIZE); let mut data = Vec::new(); data_reader .read_to_end(&mut data) .wrap_err("reading content data")?; - - let new_entry = Entry { - id: next_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed), + let new_entry = HistoryItem { + id: history_state + .next_item_id + .fetch_add(1, std::sync::atomic::Ordering::Relaxed), mime: mime.to_string(), data, created_time: u64::try_from(time.as_millis()).unwrap(), }; - - let mut items = items.lock().unwrap(); + let mut items = history_state.items.lock().unwrap(); if items .last() .is_some_and(|last| last.mime == new_entry.mime && last.data == new_entry.data) @@ -120,8 +379,9 @@ fn handle_store( println!("INFO: Skipping store of new item because it is identical to last one"); return Ok(()); } - - let last_copied = last_copied.load(std::sync::atomic::Ordering::Relaxed); + let last_copied = history_state + .last_copied_item_id + .load(std::sync::atomic::Ordering::Relaxed); if let Some(item) = items.iter().find(|item| item.id == last_copied) && item.mime == new_entry.mime && item.data == new_entry.data @@ -129,13 +389,11 @@ fn handle_store( println!("INFO: Skipping store of new item because the copy came from us"); return Ok(()); } - items.push(new_entry); - let mut running_total = 0; let mut cutoff = None; for (idx, item) in items.iter().enumerate().rev() { - running_total += item.data.len() + std::mem::size_of::(); + running_total += item.data.len() + std::mem::size_of::(); if running_total > crate::MAX_HISTORY_BYTE_SIZE { cutoff = Some(idx); } @@ -148,43 +406,8 @@ fn handle_store( ); items.splice(0..=cutoff, []); } - println!( "INFO: Successfully stored clipboard value of mime type {mime} (new history size {running_total})" ); Ok(()) } - -pub fn main(socket_path: &PathBuf) -> eyre::Result<()> { - let _ = std::fs::remove_file(&socket_path); // lol - let socket = UnixListener::bind(&socket_path) - .wrap_err_with(|| format!("binding path {}", socket_path.display()))?; - - let next_id = Arc::new(AtomicU64::new(0)); - let items = Arc::new(Mutex::new(Vec::::new())); - // for deduplication because the event stream will tell us that we just copied something :) - let last_copied = Arc::new(AtomicU64::new(u64::MAX)); - - println!("INFO: Listening on {}", socket_path.display()); - - for peer in socket.incoming() { - match peer { - Ok(peer) => { - let next_id = next_id.clone(); - let items = items.clone(); - let last_copied = last_copied.clone(); - std::thread::spawn(move || { - let result = handle_peer(peer, next_id, last_copied, items); - if let Err(err) = result { - eprintln!("ERROR: Error handling peer: {err:?}"); - } - }); - } - Err(err) => { - eprintln!("ERROR: Error accepting peer: {err}"); - } - } - } - - Ok(()) -} diff --git a/src/display.rs b/src/display.rs index 058b459..bfdd689 100644 --- a/src/display.rs +++ b/src/display.rs @@ -12,10 +12,10 @@ use std::{ time::Instant, }; -use super::Entry; +use super::HistoryItem; pub(crate) struct App { - pub(crate) items: Vec, + pub(crate) items: Vec, pub(crate) selected_idx: usize, pub(crate) socket: UnixStream, } @@ -85,6 +85,10 @@ impl eframe::App for App { ui.add_space(10.0); + ui.label(format!("MIME type: {}", item.mime)); + + ui.add_space(10.0); + match item.mime.as_str() { "text/plain" => { ui.label(str::from_utf8(&item.data).unwrap_or("")); @@ -117,7 +121,7 @@ pub fn main(socket_path: &Path) -> eyre::Result<()> { println!("INFO: Reading clipboard history from socket"); let start = Instant::now(); - let mut items: Vec = + let mut items: Vec = ciborium::from_reader(BufReader::new(socket)).wrap_err("reading items from socket")?; println!( "INFO: Read clipboard history from socket in {:?}", diff --git a/src/main.rs b/src/main.rs index b57f71f..04f7ce7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ const MAX_ENTRY_SIZE: u64 = 50_000_000; const MAX_HISTORY_BYTE_SIZE: usize = 100_000_000; #[derive(Clone, serde::Deserialize, serde::Serialize)] -struct Entry { +struct HistoryItem { id: u64, mime: String, data: Vec,