try try try

This commit is contained in:
nora 2025-09-17 21:49:14 +02:00
parent ea487d598c
commit 0df0d40f7c
3 changed files with 234 additions and 28 deletions

140
Cargo.lock generated
View file

@ -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",
]

View file

@ -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"

View file

@ -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<Vec<HistoryItem>>,
select_history_send: tokio::sync::mpsc::Sender<HistoryItem>,
data_control_manager: OnceLock<ExtDataControlManagerV1>,
data_control_devices: Mutex<HashMap</*seat global name */ u32, ExtDataControlDeviceV1>>,
qh: QueueHandle<WlState>,
d: WlDisplay,
}
struct InProgressOffer {
@ -61,6 +70,19 @@ struct WlState {
current_selection: Option<CurrentSelection>,
}
impl Dispatch<WlCallback, String> for WlState {
fn event(
_state: &mut Self,
_proxy: &WlCallback,
_event: <WlCallback as Proxy>::Event,
data: &String,
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<Self>,
) {
info!("Received sync back {data}");
}
}
impl Dispatch<WlRegistry, ()> for WlState {
fn event(
state: &mut Self,
@ -199,7 +221,7 @@ impl Dispatch<ExtDataControlDeviceV1, ()> 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<ExtDataControlDeviceV1, ()> 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::<WlState>();
let (select_history_send, mut select_history_recv) =
tokio::sync::mpsc::channel::<HistoryItem>(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::<HistoryItem>::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 || {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
loop {
let result = queue
.blocking_dispatch(&mut wl_state)
.wrap_err("handling wayland");
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 {
warn!("Received error from Wayland: {:?}", err);
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