Skip to content

Commit

Permalink
Finish Wayland watcher copy implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Saveau committed Dec 8, 2024
1 parent 66484fd commit bb93bac
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 42 deletions.
4 changes: 3 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions watcher-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ categories = ["command-line-utilities", "development-tools", "filesystem"]
license.workspace = true

[dependencies]
log = { version = "0.4.22", default-features = false }
ringboard-sdk = { package = "clipboard-history-client-sdk", version = "0", path = "../client-sdk" }
rustc-hash = "2.0.0"
rustix = { version = "0.38.41", features = ["fs"] }

[dev-dependencies]
supercilex-tests = { version = "0.4.11", default-features = false, features = ["api"] }
65 changes: 65 additions & 0 deletions watcher-utils/api.golden
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,68 @@ impl<T> core::borrow::BorrowMut<T> for clipboard_history_watcher_utils::best_tar
pub fn clipboard_history_watcher_utils::best_target::BestMimeTypeFinder<Id>::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for clipboard_history_watcher_utils::best_target::BestMimeTypeFinder<Id>
pub fn clipboard_history_watcher_utils::best_target::BestMimeTypeFinder<Id>::from(t: T) -> T
pub mod clipboard_history_watcher_utils::deduplication
pub enum clipboard_history_watcher_utils::deduplication::CopyData<'a>
pub clipboard_history_watcher_utils::deduplication::CopyData::File(&'a std::fs::File)
pub clipboard_history_watcher_utils::deduplication::CopyData::Slice(&'a [u8])
impl<'a> core::clone::Clone for clipboard_history_watcher_utils::deduplication::CopyData<'a>
pub fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::clone(&self) -> clipboard_history_watcher_utils::deduplication::CopyData<'a>
impl<'a> core::fmt::Debug for clipboard_history_watcher_utils::deduplication::CopyData<'a>
pub fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl<'a> core::marker::Copy for clipboard_history_watcher_utils::deduplication::CopyData<'a>
impl<'a> core::marker::Freeze for clipboard_history_watcher_utils::deduplication::CopyData<'a>
impl<'a> core::marker::Send for clipboard_history_watcher_utils::deduplication::CopyData<'a>
impl<'a> core::marker::Sync for clipboard_history_watcher_utils::deduplication::CopyData<'a>
impl<'a> core::marker::Unpin for clipboard_history_watcher_utils::deduplication::CopyData<'a>
impl<'a> core::panic::unwind_safe::RefUnwindSafe for clipboard_history_watcher_utils::deduplication::CopyData<'a>
impl<'a> core::panic::unwind_safe::UnwindSafe for clipboard_history_watcher_utils::deduplication::CopyData<'a>
impl<T, U> core::convert::Into<U> for clipboard_history_watcher_utils::deduplication::CopyData<'a> where U: core::convert::From<T>
pub fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for clipboard_history_watcher_utils::deduplication::CopyData<'a> where U: core::convert::Into<T>
pub type clipboard_history_watcher_utils::deduplication::CopyData<'a>::Error = core::convert::Infallible
pub fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
impl<T, U> core::convert::TryInto<U> for clipboard_history_watcher_utils::deduplication::CopyData<'a> where U: core::convert::TryFrom<T>
pub type clipboard_history_watcher_utils::deduplication::CopyData<'a>::Error = <U as core::convert::TryFrom<T>>::Error
pub fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
impl<T> alloc::borrow::ToOwned for clipboard_history_watcher_utils::deduplication::CopyData<'a> where T: core::clone::Clone
pub type clipboard_history_watcher_utils::deduplication::CopyData<'a>::Owned = T
pub fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::clone_into(&self, target: &mut T)
pub fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::to_owned(&self) -> T
impl<T> core::any::Any for clipboard_history_watcher_utils::deduplication::CopyData<'a> where T: 'static + core::marker::Sized
pub fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::type_id(&self) -> core::any::TypeId
impl<T> core::borrow::Borrow<T> for clipboard_history_watcher_utils::deduplication::CopyData<'a> where T: core::marker::Sized
pub fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::borrow(&self) -> &T
impl<T> core::borrow::BorrowMut<T> for clipboard_history_watcher_utils::deduplication::CopyData<'a> where T: core::marker::Sized
pub fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::borrow_mut(&mut self) -> &mut T
impl<T> core::clone::CloneToUninit for clipboard_history_watcher_utils::deduplication::CopyData<'a> where T: core::clone::Clone
pub unsafe fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::clone_to_uninit(&self, dst: *mut u8)
impl<T> core::convert::From<T> for clipboard_history_watcher_utils::deduplication::CopyData<'a>
pub fn clipboard_history_watcher_utils::deduplication::CopyData<'a>::from(t: T) -> T
pub struct clipboard_history_watcher_utils::deduplication::CopyDeduplication
impl clipboard_history_watcher_utils::deduplication::CopyDeduplication
pub fn clipboard_history_watcher_utils::deduplication::CopyDeduplication::check(&mut self, hash: u64, data: clipboard_history_watcher_utils::deduplication::CopyData<'_>) -> core::option::Option<u64>
pub fn clipboard_history_watcher_utils::deduplication::CopyDeduplication::hash(data: clipboard_history_watcher_utils::deduplication::CopyData<'_>, len: u64) -> u64
pub fn clipboard_history_watcher_utils::deduplication::CopyDeduplication::new() -> core::result::Result<Self, clipboard_history_core::Error>
pub fn clipboard_history_watcher_utils::deduplication::CopyDeduplication::remember(&mut self, hash: u64, id: u64)
impl core::marker::Freeze for clipboard_history_watcher_utils::deduplication::CopyDeduplication
impl core::marker::Send for clipboard_history_watcher_utils::deduplication::CopyDeduplication
impl core::marker::Sync for clipboard_history_watcher_utils::deduplication::CopyDeduplication
impl core::marker::Unpin for clipboard_history_watcher_utils::deduplication::CopyDeduplication
impl core::panic::unwind_safe::RefUnwindSafe for clipboard_history_watcher_utils::deduplication::CopyDeduplication
impl core::panic::unwind_safe::UnwindSafe for clipboard_history_watcher_utils::deduplication::CopyDeduplication
impl<T, U> core::convert::Into<U> for clipboard_history_watcher_utils::deduplication::CopyDeduplication where U: core::convert::From<T>
pub fn clipboard_history_watcher_utils::deduplication::CopyDeduplication::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for clipboard_history_watcher_utils::deduplication::CopyDeduplication where U: core::convert::Into<T>
pub type clipboard_history_watcher_utils::deduplication::CopyDeduplication::Error = core::convert::Infallible
pub fn clipboard_history_watcher_utils::deduplication::CopyDeduplication::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
impl<T, U> core::convert::TryInto<U> for clipboard_history_watcher_utils::deduplication::CopyDeduplication where U: core::convert::TryFrom<T>
pub type clipboard_history_watcher_utils::deduplication::CopyDeduplication::Error = <U as core::convert::TryFrom<T>>::Error
pub fn clipboard_history_watcher_utils::deduplication::CopyDeduplication::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
impl<T> core::any::Any for clipboard_history_watcher_utils::deduplication::CopyDeduplication where T: 'static + core::marker::Sized
pub fn clipboard_history_watcher_utils::deduplication::CopyDeduplication::type_id(&self) -> core::any::TypeId
impl<T> core::borrow::Borrow<T> for clipboard_history_watcher_utils::deduplication::CopyDeduplication where T: core::marker::Sized
pub fn clipboard_history_watcher_utils::deduplication::CopyDeduplication::borrow(&self) -> &T
impl<T> core::borrow::BorrowMut<T> for clipboard_history_watcher_utils::deduplication::CopyDeduplication where T: core::marker::Sized
pub fn clipboard_history_watcher_utils::deduplication::CopyDeduplication::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for clipboard_history_watcher_utils::deduplication::CopyDeduplication
pub fn clipboard_history_watcher_utils::deduplication::CopyDeduplication::from(t: T) -> T
2 changes: 1 addition & 1 deletion watcher-utils/src/best_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl<Id: id::AsId<Id: Eq>> BestMimeTypeFinder<Id> {
chromium_custom
} else if mime.starts_with("text/") {
any_text
} else if mime.chars().next().map_or(true, char::is_lowercase) {
} else if mime.chars().next().is_none_or(char::is_lowercase) {
other
} else {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ impl CopyDeduplication {
})
}

#[must_use]
pub fn hash(data: CopyData, len: u64) -> u64 {
let mut data_hasher = FxHasher::default();
if len >= 4096 {
Expand Down
1 change: 1 addition & 0 deletions watcher-utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![feature(let_chains)]

pub mod best_target;
pub mod deduplication;
80 changes: 46 additions & 34 deletions wayland/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ use std::{
use error_stack::Report;
use log::{debug, error, info, trace, warn};
use ringboard_sdk::{
api::{AddRequest, connect_to_server},
api::{AddRequest, MoveToFrontRequest, connect_to_server},
core::{
Error, IoErr, create_tmp_file,
dirs::socket_file,
is_plaintext_mime,
protocol::{AddResponse, IdNotFoundError, MimeType, RingKind},
protocol::{AddResponse, IdNotFoundError, MimeType, MoveToFrontResponse, RingKind},
ring::Mmap,
},
};
use ringboard_watcher_utils::best_target::BestMimeTypeFinder;
use ringboard_watcher_utils::{
best_target::BestMimeTypeFinder,
deduplication::{CopyData, CopyDeduplication},
};
use rustc_hash::FxHasher;
use rustix::{
event::epoll,
Expand Down Expand Up @@ -149,6 +152,8 @@ fn run() -> Result<(), CliError> {

let mut epoll_events = epoll::EventVec::with_capacity(1 + OFFER_BUFFERS);

let mut deduplicator = CopyDeduplication::new()?;

info!("Starting event loop.");
loop {
if let Some(e) = app.inner.error {
Expand All @@ -169,8 +174,9 @@ fn run() -> Result<(), CliError> {
r => r.map_io_err(|| "Failed to wait for epoll events.")?,
};
for epoll::Event { flags: _, data } in &epoll_events {
const OFFER_BUFFERS_U64: u64 = OFFER_BUFFERS as u64;
match data.u64() {
4 => {
OFFER_BUFFERS_U64 => {
trace!("Wayland event received.");
let count = event_queue
.prepare_read()
Expand All @@ -181,10 +187,11 @@ fn run() -> Result<(), CliError> {
event_queue.dispatch_pending(&mut app)?;
trace!("Dispatched {count} events.");
}
idx @ 0..4 => app.inner.pending_offers.continue_transfer(
idx @ ..OFFER_BUFFERS_U64 => app.inner.pending_offers.continue_transfer(
&mut app.inner.tmp_file_unsupported,
&server,
&app.epoll,
&mut deduplicator,
usize::try_from(idx).unwrap(),
)?,
_ => unreachable!(),
Expand Down Expand Up @@ -318,7 +325,7 @@ impl PendingOffers {

let idx = usize::from(self.next) & (OFFER_BUFFERS - 1);
if let Some(id) = &self.offers[idx] {
warn!("Dropping old offer: {:?}", id.id());
warn!("Dropping old offer for peer {idx}: {:?}", id.id());
}

let Self {
Expand All @@ -336,7 +343,7 @@ impl PendingOffers {
}

fn add_mime(&mut self, offer: &ZwlrDataControlOfferV1, mime: String) {
let Ok(mime_type) = MimeType::from(mime.as_str()) else {
let Ok(mime_type) = MimeType::from(&mime) else {
warn!("Mime {mime:?} too long, ignoring.");
return;
};
Expand Down Expand Up @@ -365,21 +372,21 @@ impl PendingOffers {
return Ok(());
};

let Some(mime) = self.mimes[idx].pop_best() else {
warn!("No usable mimes returned, dropping offer.");
self.reset(idx);
return Ok(());
};
self.start_transfer_(tmp_file_unsupported, epoll, idx, mime)
self.start_transfer_(tmp_file_unsupported, epoll, idx)
}

fn start_transfer_(
&mut self,
tmp_file_unsupported: &mut bool,
epoll: impl AsFd,
idx: usize,
mime: String,
) -> Result<(), CliError> {
let Some(mime) = self.mimes[idx].pop_best() else {
warn!("No usable mimes returned, dropping offer.");
self.reset(idx);
return Ok(());
};

info!("Starting transfer for peer {idx} of mime {mime:?}.");
let mime_type = MimeType::from(&mime).unwrap();

Expand Down Expand Up @@ -426,21 +433,15 @@ impl PendingOffers {
tmp_file_unsupported: &mut bool,
server: impl AsFd,
epoll: impl AsFd,
deduplicator: &mut CopyDeduplication,
idx: usize,
) -> Result<(), CliError> {
let Self {
offers: _,
mimes,
transfers,
next: _,
} = self;

let Some(Transfer {
read,
data,
len,
mime,
}) = &mut transfers[idx]
}) = &mut self.transfers[idx]
else {
error!("Received poll notification for non-existent peer: {idx}.");
return Ok(());
Expand All @@ -457,24 +458,32 @@ impl PendingOffers {
}
let len = *len;

if len == 0
|| Mmap::new(&data, usize::try_from(len).unwrap())
.map_io_err(|| "Failed to mmap copy file")?
.iter()
.all(u8::is_ascii_whitespace)
{
let mmap;
if len == 0 || {
mmap = Mmap::new(&data, usize::try_from(len).unwrap())
.map_io_err(|| "Failed to mmap copy file")?;
mmap.iter().all(u8::is_ascii_whitespace)
} {
warn!("Dropping empty or blank selection for peer {idx} on mime {mime:?}.");
if let Some(mime) = mimes[idx].pop_best() {
self.start_transfer_(tmp_file_unsupported, epoll, idx, mime)?;
} else {
self.start_transfer_(tmp_file_unsupported, epoll, idx)?;
return Ok(());
}

let data_hash = CopyDeduplication::hash(CopyData::Slice(&mmap), len);
if let Some(existing) = deduplicator.check(data_hash, CopyData::Slice(&mmap)) {
info!("Promoting duplicate entry from peer {idx} on mime {mime:?} to front.");
if let MoveToFrontResponse::Success { id } =
MoveToFrontRequest::response(&server, existing, None)?
{
deduplicator.remember(data_hash, id);
self.reset(idx);
return Ok(());
}
return Ok(());
}

let AddResponse::Success { id } =
AddRequest::response_add_unchecked(&server, RingKind::Main, *mime, data)?;
let _ = id; // TODO
deduplicator.remember(data_hash, id);
info!("Transfer for peer {idx} on mime {mime:?} complete.");
self.reset(idx);

Expand Down Expand Up @@ -666,7 +675,10 @@ impl Dispatch<ZwlrDataControlDeviceV1, u32> for App {
Ok(())
};

this.inner.error = run().err();
let err = run().err();
if this.inner.error.is_none() {
this.inner.error = err;
}
}

event_created_child!(Self, ZwlrDataControlDeviceV1, [
Expand Down
1 change: 0 additions & 1 deletion x11/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ error-stack = { version = "0.5.0", default-features = false, features = ["std"]
log = { version = "0.4.22", features = ["release_max_level_info"] }
ringboard-sdk = { package = "clipboard-history-client-sdk", version = "0", path = "../client-sdk", features = ["error-stack", "config"] }
ringboard-watcher-utils = { package = "clipboard-history-watcher-utils", version = "0", path = "../watcher-utils" }
rustc-hash = "2.0.0"
rustix = { version = "0.38.41", features = ["fs", "time"] }
thiserror = "2.0.3"
toml = { version = "0.8.19", default-features = false, features = ["parse"] }
Expand Down
9 changes: 4 additions & 5 deletions x11/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ use ringboard_sdk::{
ring::Mmap,
},
};
use ringboard_watcher_utils::best_target::BestMimeTypeFinder;
use ringboard_watcher_utils::{
best_target::BestMimeTypeFinder,
deduplication::{CopyData, CopyDeduplication},
};
use rustix::{
event::epoll,
fs::{CWD, MemfdFlags, Mode, OFlags, memfd_create},
Expand Down Expand Up @@ -73,10 +76,6 @@ use x11rb::{
x11_utils::X11Error,
};

use crate::deduplication::{CopyData, CopyDeduplication};

mod deduplication;

#[derive(Error, Debug)]
enum CliError {
#[error("{0}")]
Expand Down

0 comments on commit bb93bac

Please sign in to comment.