From 13bd759ded655e851e5ddc9a2f25e739970881c5 Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:23:18 +0100 Subject: [PATCH 1/3] use calloop for daemon Should fix #3 because we no longer have the cooked loop --- Cargo.lock | 41 +++++-- clippyboard-daemon/Cargo.toml | 3 +- clippyboard-daemon/src/main.rs | 210 ++++++++++++++------------------- 3 files changed, 127 insertions(+), 127 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 260cb35..2c369df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -522,18 +522,43 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "calloop" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb9f6e1368bd4621d2c86baa7e37de77a938adf5221e5dd3d6133340101b309e" +dependencies = [ + "bitflags 2.9.4", + "polling", + "rustix 1.1.2", + "slab", + "tracing", +] + [[package]] name = "calloop-wayland-source" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "calloop", + "calloop 0.13.0", "rustix 0.38.44", "wayland-backend", "wayland-client", ] +[[package]] +name = "calloop-wayland-source" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" +dependencies = [ + "calloop 0.14.3", + "rustix 1.1.2", + "wayland-backend", + "wayland-client", +] + [[package]] name = "cc" version = "1.2.36" @@ -621,12 +646,13 @@ dependencies = [ name = "clippyboard-daemon" version = "0.1.0" dependencies = [ + "calloop 0.14.3", + "calloop-wayland-source 0.4.1", "ciborium", "clippyboard-shared", "ctrlc", "dirs", "eyre", - "rustix 1.1.2", "serde", "tracing", "tracing-subscriber", @@ -792,7 +818,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -2680,7 +2706,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -2818,8 +2844,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ "bitflags 2.9.4", - "calloop", - "calloop-wayland-source", + "calloop 0.13.0", + "calloop-wayland-source 0.3.0", "cursor-icon", "libc", "log", @@ -3070,6 +3096,7 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4055,7 +4082,7 @@ dependencies = [ "bitflags 2.9.4", "block2", "bytemuck", - "calloop", + "calloop 0.13.0", "cfg_aliases", "concurrent-queue", "core-foundation 0.9.4", diff --git a/clippyboard-daemon/Cargo.toml b/clippyboard-daemon/Cargo.toml index da414a1..7d58539 100644 --- a/clippyboard-daemon/Cargo.toml +++ b/clippyboard-daemon/Cargo.toml @@ -9,10 +9,11 @@ ciborium = "0.2.2" ctrlc = "3.5.0" dirs = "6.0.0" eyre = "0.6.12" -rustix = "1.1.2" serde = "1.0.219" tracing = { version = "0.1.41", features = ["attributes"] } tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } wayland-backend = { version = "0.3.11", features = ["client_system"] } wayland-client = "0.31.11" wayland-protocols = { version = "0.32.9", features = ["staging", "client"] } +calloop = "0.14.3" +calloop-wayland-source = "0.4.1" diff --git a/clippyboard-daemon/src/main.rs b/clippyboard-daemon/src/main.rs index 4705200..bab6cbd 100644 --- a/clippyboard-daemon/src/main.rs +++ b/clippyboard-daemon/src/main.rs @@ -1,17 +1,18 @@ +use calloop::EventLoop; +use calloop::Interest; +use calloop::Mode; +use calloop::generic::Generic; +use calloop_wayland_source::WaylandSource; use clippyboard_shared::HistoryItem; use eyre::Context; -use eyre::ContextCompat; use eyre::bail; -use rustix::event::PollFd; -use rustix::event::PollFlags; -use rustix::fs::OFlags; use std::collections::HashMap; use std::collections::HashSet; use std::convert::Infallible; use std::io; use std::io::ErrorKind; -use std::io::PipeReader; use std::io::{BufReader, BufWriter, PipeWriter, Read, Write}; +use std::ops::Deref; use std::os::fd::AsFd; use std::os::unix::net::{UnixListener, UnixStream}; use std::path::PathBuf; @@ -24,7 +25,6 @@ use tracing::error; use tracing::info; use tracing::warn; use tracing_subscriber::EnvFilter; -use wayland_client::EventQueue; use wayland_client::protocol::wl_registry::WlRegistry; use wayland_client::protocol::wl_seat::WlSeat; use wayland_client::{Dispatch, Proxy, QueueHandle, event_created_child}; @@ -43,14 +43,26 @@ const MAX_HISTORY_BYTE_SIZE: usize = 100_000_000; const MIME_TYPES: &[&str] = &["text/plain", "image/png", "image/jpg"]; -struct SharedState { +struct SharedStateInner { next_item_id: AtomicU64, items: Mutex>, - notify_write_send: PipeWriter, data_control_manager: OnceLock, data_control_devices: Mutex>, - qh: QueueHandle, + qh: QueueHandle, +} + +struct SharedState { + inner: Arc, + /// wl_seat that arrived before the data control manager so we weren't able to grab their device immediatly. + deferred_seats: Vec, +} + +impl Deref for SharedState { + type Target = SharedStateInner; + fn deref(&self) -> &Self::Target { + &self.inner + } } struct InProgressOffer { @@ -58,14 +70,7 @@ struct InProgressOffer { time: Duration, } -struct WlState { - shared_state: Arc, - - /// wl_seat that arrived before the data control manager so we weren't able to grab their device immediatly. - deferred_seats: Vec, -} - -impl Dispatch for WlState { +impl Dispatch for SharedState { fn event( state: &mut Self, proxy: &WlRegistry, @@ -84,14 +89,13 @@ impl Dispatch for WlState { info!("A new seat was connected"); let seat: WlSeat = proxy.bind(name, 1, qhandle, ()); - match state.shared_state.data_control_manager.get() { + match state.data_control_manager.get() { None => { state.deferred_seats.push(seat); } Some(manager) => { let device = manager.get_data_device(&seat, qhandle, ()); state - .shared_state .data_control_devices .lock() .unwrap() @@ -104,7 +108,7 @@ impl Dispatch for WlState { for seat in state.deferred_seats.drain(..) { let device = manager.get_data_device(&seat, qhandle, ()); state - .shared_state + .inner .data_control_devices .lock() .unwrap() @@ -112,7 +116,6 @@ impl Dispatch for WlState { } state - .shared_state .data_control_manager .set(manager) .expect("ext_data_control_manager_v1 already set, global appeared twice?"); @@ -120,18 +123,13 @@ impl Dispatch for WlState { } wayland_client::protocol::wl_registry::Event::GlobalRemove { name } => { // try to remove, if it's not a wl_seat it may not exist - state - .shared_state - .data_control_devices - .lock() - .unwrap() - .remove(&name); + state.data_control_devices.lock().unwrap().remove(&name); } _ => {} } } } -impl Dispatch for WlState { +impl Dispatch for SharedState { fn event( _state: &mut Self, _proxy: &ExtDataControlManagerV1, @@ -143,7 +141,7 @@ impl Dispatch for WlState { // no events at the time of writing } } -impl Dispatch for WlState { +impl Dispatch for SharedState { fn event( _state: &mut Self, _proxy: &WlSeat, @@ -155,7 +153,7 @@ impl Dispatch for WlState { // we don't care about anything about the seat } } -impl Dispatch for WlState { +impl Dispatch for SharedState { fn event( state: &mut Self, _proxy: &ExtDataControlDeviceV1, @@ -191,7 +189,7 @@ impl Dispatch for WlState { }; drop(mime_types); - let history_state = state.shared_state.clone(); + let history_state = state.inner.clone(); let time = offer_data.time; let (reader, writer) = std::io::pipe().unwrap(); @@ -240,7 +238,7 @@ impl Dispatch for WlState { } } - event_created_child!(WlState, ExtDataControlDeviceV1, [ + event_created_child!(SharedState, ExtDataControlDeviceV1, [ EVT_DATA_OFFER_OPCODE => (ExtDataControlOfferV1, InProgressOffer { mime_types: Default::default(), time: SystemTime::now() @@ -250,7 +248,7 @@ impl Dispatch for WlState { ]); } -impl Dispatch for WlState { +impl Dispatch for SharedState { fn event( _state: &mut Self, _proxy: &ExtDataControlOfferV1, @@ -268,7 +266,7 @@ impl Dispatch for WlState { } } -impl Dispatch for WlState { +impl Dispatch for SharedState { fn event( _state: &mut Self, proxy: &ExtDataControlSourceV1, @@ -302,15 +300,9 @@ impl Dispatch for WlState { } } -impl SharedState { - fn notify_wayland_request(&self) { - let _ = (&self.notify_write_send).write_all(&[0]); - } -} - fn do_copy_into_clipboard( entry: HistoryItem, - shared_state: &SharedState, + shared_state: &SharedStateInner, ) -> Result<(), eyre::Error> { for device in &*shared_state.data_control_devices.lock().unwrap() { let data_source = shared_state @@ -341,34 +333,8 @@ fn do_copy_into_clipboard( Ok(()) } -fn dispatch_wayland( - mut queue: EventQueue, - mut wl_state: WlState, - notify_write_recv: PipeReader, -) -> eyre::Result<()> { - loop { - queue - .dispatch_pending(&mut wl_state) - .wrap_err("dispatching Wayland events")?; - - let read_guard = queue - .prepare_read() - .wrap_err("preparing read from Wayland socket")?; - let _ = queue.flush(); - - let pollfd1_read = PollFd::from_borrowed_fd(read_guard.connection_fd(), PollFlags::IN); - let pollfd_signal = PollFd::from_borrowed_fd(notify_write_recv.as_fd(), PollFlags::IN); - - let _ = rustix::event::poll(&mut [pollfd1_read, pollfd_signal], None); - - read_guard - .read_without_dispatch() - .wrap_err("reading from wayland socket")?; - } -} - #[tracing::instrument(skip(peer, shared_state))] -fn handle_peer(mut peer: UnixStream, shared_state: &SharedState) -> eyre::Result<()> { +fn handle_peer(mut peer: UnixStream, shared_state: &SharedStateInner) -> eyre::Result<()> { let mut request = [0; 1]; let Ok(()) = peer.read_exact(&mut request) else { return Ok(()); @@ -396,7 +362,7 @@ struct OfferData(Arc<[u8]>); fn handle_copy_message( mut peer: UnixStream, - shared_state: &SharedState, + shared_state: &SharedStateInner, ) -> Result<(), eyre::Error> { let mut id = [0; 8]; peer.read_exact(&mut id).wrap_err("failed to read id")?; @@ -412,25 +378,21 @@ fn handle_copy_message( do_copy_into_clipboard(item, &shared_state).wrap_err("doing copy")?; - shared_state.notify_wayland_request(); - Ok(()) } -fn handle_clear_message(shared_state: &SharedState) -> eyre::Result<()> { +fn handle_clear_message(shared_state: &SharedStateInner) -> eyre::Result<()> { shared_state.items.lock().unwrap().clear(); for device in &*shared_state.data_control_devices.lock().unwrap() { device.1.set_selection(None); } - shared_state.notify_wayland_request(); - Ok(()) } fn read_fd_into_history( - history_state: &SharedState, + history_state: &SharedStateInner, time: std::time::Duration, mime: String, data_reader: impl Read, @@ -514,35 +476,27 @@ pub fn main_inner(socket_path: &PathBuf) -> eyre::Result { let conn = wayland_client::Connection::connect_to_env().wrap_err("connecting to the compositor")?; - let mut queue = conn.new_event_queue::(); + let mut queue = conn.new_event_queue::(); - let (notify_write_recv, notify_write_send) = std::io::pipe().expect("todo"); + let mut shared_state = SharedState { + inner: Arc::new(SharedStateInner { + next_item_id: AtomicU64::new(0), + items: Mutex::new(Vec::::new()), - let shared_state = Arc::new(SharedState { - next_item_id: AtomicU64::new(0), - items: Mutex::new(Vec::::new()), - notify_write_send, - - data_control_manager: OnceLock::new(), - data_control_devices: Mutex::new(HashMap::new()), - qh: queue.handle(), - }); - - let history_state2 = shared_state.clone(); - - let mut wl_state = WlState { + data_control_manager: OnceLock::new(), + data_control_devices: Mutex::new(HashMap::new()), + qh: queue.handle(), + }), deferred_seats: Vec::new(), - - shared_state: history_state2, }; conn.display().get_registry(&queue.handle(), ()); queue - .roundtrip(&mut wl_state) + .roundtrip(&mut shared_state) .wrap_err("failed to set up wayland state")?; - if wl_state.shared_state.data_control_manager.get().is_none() { + if shared_state.data_control_manager.get().is_none() { bail!( "{} not found, the ext-data-control-v1 Wayland extension is likely unsupported by your compositor.\n\ check https://wayland.app/protocols/ext-data-control-v1#compositor-support\ @@ -551,35 +505,53 @@ pub fn main_inner(socket_path: &PathBuf) -> eyre::Result { ); } - rustix::fs::fcntl_setfl(notify_write_recv.as_fd(), OFlags::NONBLOCK).expect("todo"); - rustix::fs::fcntl_setfl(conn.as_fd(), OFlags::NONBLOCK).expect("TODO"); + let mut event_loop = + EventLoop::::try_new().wrap_err("failed to initialize event_loop")?; - let socket_path_clone = socket_path.to_owned(); - std::thread::spawn(move || { - if let Err(err) = dispatch_wayland(queue, wl_state, notify_write_recv) { - error!("error on Wayland thread: {err:?}"); - cleanup(&socket_path_clone); - std::process::exit(1); - } - }); + WaylandSource::new(conn.clone(), queue) + .insert(event_loop.handle()) + .unwrap(); + + event_loop + .handle() + .insert_source( + Generic::new(socket, Interest::READ, Mode::Level), + |_, socket, shared_state| { + let peer = socket.accept(); + match peer { + Ok((peer, _)) => { + let history_state = shared_state.inner.clone(); + std::thread::spawn(move || { + let result = handle_peer(peer, &history_state); + if let Err(err) = result { + warn!("Error handling peer: {err:?}"); + } + }); + } + Err(err) => { + warn!("Error accepting peer: {err}"); + } + } + + Ok(calloop::PostAction::Continue) + }, + ) + .wrap_err("failed to register socket event source")?; info!("Listening on {}", socket_path.display()); - for peer in socket.incoming() { - match peer { - Ok(peer) => { - let history_state = shared_state.clone(); - std::thread::spawn(move || { - let result = handle_peer(peer, &history_state); - if let Err(err) = result { - warn!("Error handling peer: {err:?}"); - } - }); - } - Err(err) => { - warn!("Error accepting peer: {err}"); - } - } + let socket_path_clone = socket_path.clone(); + + let result = event_loop.run( + std::time::Duration::from_millis(100), + &mut shared_state, + |_| {}, + ); + + if let Err(err) = result { + error!("error on Wayland thread: {err:?}"); + cleanup(&socket_path_clone); + std::process::exit(1); } unreachable!("socket.incoming will never return None") From 75b6f3eef20c26abdd435fe0772359597e8d35b4 Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:31:38 +0100 Subject: [PATCH 2/3] Cleanup Should fix #4 because we now handle sigterm. --- Cargo.lock | 13 +---------- clippyboard-daemon/Cargo.toml | 3 +-- clippyboard-daemon/src/main.rs | 41 ++++++++++++++++++++-------------- 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c369df..eb31cc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -529,6 +529,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb9f6e1368bd4621d2c86baa7e37de77a938adf5221e5dd3d6133340101b309e" dependencies = [ "bitflags 2.9.4", + "nix", "polling", "rustix 1.1.2", "slab", @@ -650,7 +651,6 @@ dependencies = [ "calloop-wayland-source 0.4.1", "ciborium", "clippyboard-shared", - "ctrlc", "dirs", "eyre", "serde", @@ -783,17 +783,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" -[[package]] -name = "ctrlc" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881c5d0a13b2f1498e2306e82cbada78390e152d4b1378fb28a84f4dcd0dc4f3" -dependencies = [ - "dispatch", - "nix", - "windows-sys 0.61.0", -] - [[package]] name = "cursor-icon" version = "1.2.0" diff --git a/clippyboard-daemon/Cargo.toml b/clippyboard-daemon/Cargo.toml index 7d58539..181f0a2 100644 --- a/clippyboard-daemon/Cargo.toml +++ b/clippyboard-daemon/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] clippyboard-shared = { path = "../clippyboard-shared" } ciborium = "0.2.2" -ctrlc = "3.5.0" dirs = "6.0.0" eyre = "0.6.12" serde = "1.0.219" @@ -15,5 +14,5 @@ tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } wayland-backend = { version = "0.3.11", features = ["client_system"] } wayland-client = "0.31.11" wayland-protocols = { version = "0.32.9", features = ["staging", "client"] } -calloop = "0.14.3" +calloop = { version = "0.14.3", features = ["signals"] } calloop-wayland-source = "0.4.1" diff --git a/clippyboard-daemon/src/main.rs b/clippyboard-daemon/src/main.rs index bab6cbd..e6309d1 100644 --- a/clippyboard-daemon/src/main.rs +++ b/clippyboard-daemon/src/main.rs @@ -2,6 +2,8 @@ use calloop::EventLoop; use calloop::Interest; use calloop::Mode; use calloop::generic::Generic; +use calloop::signals::Signal; +use calloop::signals::Signals; use calloop_wayland_source::WaylandSource; use clippyboard_shared::HistoryItem; use eyre::Context; @@ -12,7 +14,6 @@ use std::convert::Infallible; use std::io; use std::io::ErrorKind; use std::io::{BufReader, BufWriter, PipeWriter, Read, Write}; -use std::ops::Deref; use std::os::fd::AsFd; use std::os::unix::net::{UnixListener, UnixStream}; use std::path::PathBuf; @@ -58,13 +59,6 @@ struct SharedState { deferred_seats: Vec, } -impl Deref for SharedState { - type Target = SharedStateInner; - fn deref(&self) -> &Self::Target { - &self.inner - } -} - struct InProgressOffer { mime_types: Mutex>, time: Duration, @@ -89,13 +83,14 @@ impl Dispatch for SharedState { info!("A new seat was connected"); let seat: WlSeat = proxy.bind(name, 1, qhandle, ()); - match state.data_control_manager.get() { + match state.inner.data_control_manager.get() { None => { state.deferred_seats.push(seat); } Some(manager) => { let device = manager.get_data_device(&seat, qhandle, ()); state + .inner .data_control_devices .lock() .unwrap() @@ -116,6 +111,7 @@ impl Dispatch for SharedState { } state + .inner .data_control_manager .set(manager) .expect("ext_data_control_manager_v1 already set, global appeared twice?"); @@ -123,7 +119,12 @@ impl Dispatch for SharedState { } wayland_client::protocol::wl_registry::Event::GlobalRemove { name } => { // try to remove, if it's not a wl_seat it may not exist - state.data_control_devices.lock().unwrap().remove(&name); + state + .inner + .data_control_devices + .lock() + .unwrap() + .remove(&name); } _ => {} } @@ -446,12 +447,6 @@ fn read_fd_into_history( fn main() -> eyre::Result<()> { let socket_path = clippyboard_shared::socket_path()?; - let socket_path2 = socket_path.clone(); - let _ = ctrlc::set_handler(move || { - cleanup(&socket_path2); - std::process::exit(130); // sigint - }); - let Err(err) = main_inner(&socket_path); if let Some(ioerr) = err.downcast_ref::() @@ -496,7 +491,7 @@ pub fn main_inner(socket_path: &PathBuf) -> eyre::Result { .roundtrip(&mut shared_state) .wrap_err("failed to set up wayland state")?; - if shared_state.data_control_manager.get().is_none() { + if shared_state.inner.data_control_manager.get().is_none() { bail!( "{} not found, the ext-data-control-v1 Wayland extension is likely unsupported by your compositor.\n\ check https://wayland.app/protocols/ext-data-control-v1#compositor-support\ @@ -512,6 +507,18 @@ pub fn main_inner(socket_path: &PathBuf) -> eyre::Result { .insert(event_loop.handle()) .unwrap(); + event_loop + .handle() + .insert_source( + Signals::new(&[Signal::SIGINT, Signal::SIGTERM]) + .wrap_err("failed to create signal listener")?, + |_, _, _| { + cleanup(socket_path); + std::process::exit(0); + }, + ) + .wrap_err("failed to register signalfd handlers")?; + event_loop .handle() .insert_source( From 91134a085402ce78238c86574d557f703715a111 Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:38:43 +0100 Subject: [PATCH 3/3] fix history size limit fixes #5 --- clippyboard-daemon/src/main.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/clippyboard-daemon/src/main.rs b/clippyboard-daemon/src/main.rs index e6309d1..e6a5774 100644 --- a/clippyboard-daemon/src/main.rs +++ b/clippyboard-daemon/src/main.rs @@ -11,6 +11,7 @@ use eyre::bail; use std::collections::HashMap; use std::collections::HashSet; use std::convert::Infallible; +use std::fmt::Display; use std::io; use std::io::ErrorKind; use std::io::{BufReader, BufWriter, PipeWriter, Read, Write}; @@ -39,8 +40,9 @@ use wayland_protocols::ext::data_control::v1::client::ext_data_control_offer_v1: use wayland_protocols::ext::data_control::v1::client::ext_data_control_source_v1; use wayland_protocols::ext::data_control::v1::client::ext_data_control_source_v1::ExtDataControlSourceV1; -const MAX_ENTRY_SIZE: u64 = 50_000_000; -const MAX_HISTORY_BYTE_SIZE: usize = 100_000_000; +const MEGABYTE: usize = 1024 * 1024; +const MAX_ENTRY_SIZE: usize = 50 * MEGABYTE; +const MAX_HISTORY_BYTE_SIZE: usize = 100 * MEGABYTE; const MIME_TYPES: &[&str] = &["text/plain", "image/png", "image/jpg"]; @@ -398,7 +400,7 @@ fn read_fd_into_history( mime: String, data_reader: impl Read, ) -> Result<(), eyre::Error> { - let mut data_reader = BufReader::new(data_reader).take(MAX_ENTRY_SIZE); + let mut data_reader = BufReader::new(data_reader).take(MAX_ENTRY_SIZE as u64); let mut data = Vec::new(); data_reader .read_to_end(&mut data) @@ -428,22 +430,31 @@ fn read_fd_into_history( running_total += item.data.len() + std::mem::size_of::(); if running_total > crate::MAX_HISTORY_BYTE_SIZE { cutoff = Some(idx); + break; } } if let Some(cutoff) = cutoff { info!( - "Dropping old {} items because limit of {} bytes was reached for the history", + "Dropping old {} items beca{} bytes was reached for the history", cutoff + 1, - crate::MAX_HISTORY_BYTE_SIZE + format_history_size(crate::MAX_HISTORY_BYTE_SIZE) ); items.splice(0..=cutoff, []); } info!( - "Successfully stored clipboard value of mime type {mime} (new history size {running_total})" + "Successfully stored clipboard value of mime type {mime} (new history size {})", + format_history_size(running_total) ); Ok(()) } +fn format_history_size(size: usize) -> impl Display { + if size < MEGABYTE { + return format!("{size}B"); + } + format!("{}MB", size / MEGABYTE) +} + fn main() -> eyre::Result<()> { let socket_path = clippyboard_shared::socket_path()?;