Skip to content

Commit

Permalink
Use pipe instead of eventfd for unix compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcusGrass authored and quininer committed Feb 29, 2024
1 parent 704cfd3 commit a716f79
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 39 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "x11-clipboard"
version = "0.9.1"
version = "0.9.2"
authors = ["quininer kel <quininer@live.com>"]
description = "x11 clipboard support for Rust."
repository = "https://github.com/quininer/x11-clipboard"
Expand Down
1 change: 1 addition & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub enum Error {
Timeout,
Owner,
UnexpectedType(Atom),
// Could change name on next major, since this uses pipes now.
EventFdCreate,
}

Expand Down
27 changes: 9 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ use std::time::{ Duration, Instant };
use std::sync::{ Arc, RwLock };
use std::sync::mpsc::{ Sender, channel };
use std::collections::HashMap;
use std::os::fd::AsRawFd;
use std::os::fd::OwnedFd;
use x11rb::connection::{Connection, RequestConnection};
use x11rb::{COPY_DEPTH_FROM_PARENT, CURRENT_TIME};
use x11rb::errors::ConnectError;
use x11rb::protocol::{Event, xfixes};
use x11rb::protocol::xproto::{AtomEnum, ConnectionExt, CreateWindowAux, EventMask, Property, WindowClass};
use error::Error;
use run::{create_eventfd, EventFd};
use run::{create_pipe_drop_fd, PipeDropFds};

pub const INCR_CHUNK_SIZE: usize = 4000;
const POLL_DURATION: u64 = 50;
Expand Down Expand Up @@ -76,20 +76,10 @@ pub struct Clipboard {
pub setter: Arc<Context>,
setmap: SetMap,
send: Sender<Atom>,
efd: EventFd,
// Relying on the Drop in OwnedFd to close the fd
_drop_fd: OwnedFd,
}

impl Drop for Clipboard {
fn drop(&mut self) {
// Need to write any 8 bytes that are not 0 to trigger a read-ready
const ANY: &[u8; 8] = &[1, 1, 1, 1, 1, 1, 1, 1];
unsafe {
// Safety: The FD is valid and owned, the buffer has a static lifetime and is not mutated
// Best attempt close stream on thread
let _ = libc::write(self.efd.0.as_raw_fd(), ANY.as_ptr() as *const libc::c_void, ANY.len());
}
}
}
pub struct Context {
pub connection: RustConnection,
pub screen: usize,
Expand Down Expand Up @@ -153,13 +143,14 @@ impl Clipboard {
let setmap = Arc::new(RwLock::new(HashMap::new()));
let setmap2 = Arc::clone(&setmap);

let efd = create_eventfd()?;
let efd_c = efd.clone();
let PipeDropFds {
read_pipe, write_pipe
} = create_pipe_drop_fd()?;
let (sender, receiver) = channel();
let max_length = setter.connection.maximum_request_bytes();
thread::spawn(move || run::run(setter2, setmap2, max_length, receiver, efd_c));
thread::spawn(move || run::run(setter2, setmap2, max_length, receiver, read_pipe));

Ok(Clipboard { getter, setter, setmap, send: sender, efd })
Ok(Clipboard { getter, setter, setmap, send: sender, _drop_fd: write_pipe })
}

fn process_event<T>(&self, buff: &mut Vec<u8>, selection: Atom, target: Atom, property: Atom, timeout: T, use_xfixes: bool, sequence_number: u64)
Expand Down
49 changes: 29 additions & 20 deletions src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,46 +26,55 @@ struct IncrState {
pos: usize
}

pub(crate) struct PipeDropFds {
pub(crate) read_pipe: OwnedFd,
pub(crate) write_pipe: OwnedFd,
}

#[derive(Clone)]
pub(crate) struct EventFd(pub(crate) Arc<OwnedFd>);

pub(crate) fn create_eventfd() -> Result<EventFd, Error>{
let event_fd_owned = unsafe {
// Docs: https://man7.org/linux/man-pages/man2/eventfd.2.html
// Safety: No pointer passing or other spookiness, used correctly according to the above docs
let event_fd_res = libc::eventfd(0, libc::EFD_CLOEXEC);
// Could check that it's bigger than STDOUT, STDERR, STDIN
if event_fd_res < 0 {
pub(crate) fn create_pipe_drop_fd() -> Result<PipeDropFds, Error>{
let pipe_drop_fds = unsafe {
// Docs Linux: https://man7.org/linux/man-pages/man2/pipe.2.html
// Posix: https://pubs.opengroup.org/onlinepubs/9699919799/
// Safety: See above docs, api expects a 2-long array of file descriptors, and flags
let mut pipes: [libc::c_int; 2] = [0, 0];
let pipe_create_res = libc::pipe(pipes.as_mut_ptr());
if pipe_create_res < 0 {
// Don't want to have to read from errno_location, just skip propagating errno.
return Err(Error::EventFdCreate);
}
// Safety: Trusting the OS to give a correct FD
OwnedFd::from_raw_fd(event_fd_res)
// Safety: Trusting the OS to give correct FDs
let read_pipe = OwnedFd::from_raw_fd(pipes[0]);
let write_pipe = OwnedFd::from_raw_fd(pipes[1]);
PipeDropFds {
read_pipe,
write_pipe,
}
};
Ok(EventFd(Arc::new(event_fd_owned)))
Ok(pipe_drop_fds)
}

pub(crate) fn run(context: Arc<Context>, setmap: SetMap, max_length: usize, receiver: Receiver<Atom>, evt_fd: EventFd) {
pub(crate) fn run(context: Arc<Context>, setmap: SetMap, max_length: usize, receiver: Receiver<Atom>, read_pipe: OwnedFd) {
let mut incr_map = HashMap::<Atom, Atom>::new();
let mut state_map = HashMap::<Atom, IncrState>::new();

let stream_fd = context.connection.stream().as_fd();
let borrowed_fd = evt_fd.0.as_fd();
// Poll both stream and eventfd for new Read-ready events
let borrowed_fd = read_pipe.as_fd();
// Poll stream for new Read-ready events, check if the other side of the pipe has been dropped
let mut pollfds: [libc::pollfd; 2] = [libc::pollfd {
fd: stream_fd.as_raw_fd(),
events: libc::POLLIN,
revents: 0,
}, libc::pollfd {
fd: borrowed_fd.as_raw_fd(),
events: libc::POLLIN,
// If the other end is dropped, this pipe will get a HUP on poll
events: libc::POLLHUP,
revents: 0,
}];
let len = pollfds.len();
loop {
unsafe {
// Docs: https://man7.org/linux/man-pages/man2/poll.2.html
// Docs Linux: https://man7.org/linux/man-pages/man2/poll.2.html
// Posix: https://pubs.opengroup.org/onlinepubs/9699919799/
// Safety: Passing in a mutable pointer that lives for the duration of the call, the length is
// set to the length of that pointer.
// Any negative value (-1 for example) means infinite timeout.
Expand All @@ -75,8 +84,8 @@ pub(crate) fn run(context: Arc<Context>, setmap: SetMap, max_length: usize, rece
return;
}
}
if pollfds[1].revents & libc::POLLIN != 0 {
// kill-signal on eventfd
if pollfds[1].revents & libc::POLLHUP != 0 {
// kill-signal on pollfd
return;
}
loop {
Expand Down

0 comments on commit a716f79

Please sign in to comment.