Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use pipe instead of eventfd for unix compatibility #49

Merged
merged 1 commit into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <[email protected]>"]
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