Merge pull request #14 from daxpedda/x11rb-improvements-2

More `x11rb` improvements
This commit is contained in:
Héctor Ramón 2021-03-11 00:56:50 +01:00 committed by GitHub
commit a451ebe895
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 56 additions and 107 deletions

View file

@ -25,6 +25,7 @@ clipboard_x11 = { version = "0.3", path = "./x11" }
clipboard_wayland = { version = "0.2", path = "./wayland" } clipboard_wayland = { version = "0.2", path = "./wayland" }
[dev-dependencies] [dev-dependencies]
rand = "0.8"
winit = "0.23" winit = "0.23"
[workspace] [workspace]

54
examples/big_file.rs Normal file
View file

@ -0,0 +1,54 @@
use rand::distributions::{Alphanumeric, Distribution};
use window_clipboard::Clipboard;
use winit::{
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() {
let mut rng = rand::thread_rng();
let data: String = Alphanumeric
.sample_iter(&mut rng)
.take(10_000_000)
.map(char::from)
.collect();
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("Press G to start the test!")
.build(&event_loop)
.unwrap();
let mut clipboard =
Clipboard::connect(&window).expect("Connect to clipboard");
clipboard.write(data.clone()).unwrap();
event_loop.run(move |event, _, control_flow| match event {
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::G),
state: ElementState::Released,
..
},
..
},
..
} => {
let new_data = clipboard.read().expect("Read data");
assert_eq!(data, new_data, "Data is equal");
println!("Data copied successfully!");
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
_ => *control_flow = ControlFlow::Wait,
});
}

View file

@ -11,7 +11,6 @@ use x11rb::rust_connection::RustConnection as Connection;
use x11rb::wrapper::ConnectionExt; use x11rb::wrapper::ConnectionExt;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::mpsc;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use std::thread; use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -23,7 +22,6 @@ pub struct Clipboard {
reader: Context, reader: Context,
writer: Arc<Context>, writer: Arc<Context>,
selections: Arc<RwLock<HashMap<Atom, (Atom, Vec<u8>)>>>, selections: Arc<RwLock<HashMap<Atom, (Atom, Vec<u8>)>>>,
worker: mpsc::Sender<Atom>,
} }
impl Clipboard { impl Clipboard {
@ -32,12 +30,10 @@ impl Clipboard {
let reader = Context::new(None)?; let reader = Context::new(None)?;
let writer = Arc::new(Context::new(None)?); let writer = Arc::new(Context::new(None)?);
let selections = Arc::new(RwLock::new(HashMap::new())); let selections = Arc::new(RwLock::new(HashMap::new()));
let (sender, receiver) = mpsc::channel();
let worker = Worker { let worker = Worker {
context: Arc::clone(&writer), context: Arc::clone(&writer),
selections: Arc::clone(&selections), selections: Arc::clone(&selections),
receiver,
}; };
thread::spawn(move || worker.run()); thread::spawn(move || worker.run());
@ -46,7 +42,6 @@ impl Clipboard {
reader, reader,
writer, writer,
selections, selections,
worker: sender,
}) })
} }
@ -66,8 +61,6 @@ impl Clipboard {
let selection = self.writer.atoms.clipboard; let selection = self.writer.atoms.clipboard;
let target = self.writer.atoms.utf8_string; let target = self.writer.atoms.utf8_string;
let _ = self.worker.send(selection)?;
self.selections self.selections
.write() .write()
.map_err(|_| Error::SelectionLocked)? .map_err(|_| Error::SelectionLocked)?
@ -344,34 +337,13 @@ impl Context {
pub struct Worker { pub struct Worker {
context: Arc<Context>, context: Arc<Context>,
selections: Arc<RwLock<HashMap<Atom, (Atom, Vec<u8>)>>>, selections: Arc<RwLock<HashMap<Atom, (Atom, Vec<u8>)>>>,
receiver: mpsc::Receiver<Atom>,
}
struct IncrState {
selection: Atom,
requestor: Atom,
property: Atom,
pos: usize,
} }
impl Worker { impl Worker {
pub const INCR_CHUNK_SIZE: usize = 4000; pub const INCR_CHUNK_SIZE: usize = 4000;
pub fn run(self) { pub fn run(self) {
use x11rb::connection::RequestConnection;
let mut incr_map = HashMap::new();
let mut state_map = HashMap::new();
let max_length = self.context.connection.maximum_request_bytes() * 4;
while let Ok(event) = self.context.connection.wait_for_event() { while let Ok(event) = self.context.connection.wait_for_event() {
while let Ok(selection) = self.receiver.try_recv() {
if let Some(property) = incr_map.remove(&selection) {
state_map.remove(&property);
}
}
match event { match event {
Event::SelectionRequest(event) => { Event::SelectionRequest(event) => {
let selections = match self.selections.read().ok() { let selections = match self.selections.read().ok() {
@ -396,7 +368,7 @@ impl Worker {
&data, &data,
) )
.expect("Change property"); .expect("Change property");
} else if value.len() < max_length - 24 { } else {
let _ = self.context.connection.change_property8( let _ = self.context.connection.change_property8(
xproto::PropMode::REPLACE, xproto::PropMode::REPLACE,
event.requestor, event.requestor,
@ -405,34 +377,6 @@ impl Worker {
value, value,
) )
.expect("Change property"); .expect("Change property");
} else {
let _ = xproto::change_window_attributes(
&self.context.connection,
event.requestor,
&xproto::ChangeWindowAttributesAux::new()
.event_mask(xproto::EventMask::PROPERTY_CHANGE),
)
.expect("Change window attributes");
self.context.connection.change_property32(
xproto::PropMode::REPLACE,
event.requestor,
event.property,
self.context.atoms.incr,
&[],
)
.expect("Change property");
incr_map.insert(event.selection, event.property);
state_map.insert(
event.property,
IncrState {
selection: event.selection,
requestor: event.requestor,
property: event.property,
pos: 0,
},
);
} }
let _ = xproto::send_event( let _ = xproto::send_event(
@ -454,57 +398,7 @@ impl Worker {
let _ = self.context.connection.flush(); let _ = self.context.connection.flush();
} }
Event::PropertyNotify(event) => {
if event.state != xproto::Property::DELETE {
continue;
}
let is_end = {
let state = match state_map.get_mut(&event.atom) {
Some(state) => state,
None => continue,
};
let selections = match self.selections.read().ok() {
Some(selections) => selections,
None => continue,
};
let &(target, ref value) =
match selections.get(&state.selection) {
Some(key_value) => key_value,
None => continue,
};
let len = std::cmp::min(
Self::INCR_CHUNK_SIZE,
value.len() - state.pos,
);
let _ = self.context.connection.change_property8(
xproto::PropMode::REPLACE,
state.requestor,
state.property,
target,
&value[state.pos..][..len],
)
.expect("Change property");
state.pos += len;
len == 0
};
if is_end {
state_map.remove(&event.atom);
}
self.context.connection.flush().expect("Flush connection");
}
Event::SelectionClear(event) => { Event::SelectionClear(event) => {
if let Some(property) = incr_map.remove(&event.selection) {
state_map.remove(&property);
}
if let Ok(mut write_setmap) = self.selections.write() { if let Ok(mut write_setmap) = self.selections.write() {
write_setmap.remove(&event.selection); write_setmap.remove(&event.selection);
} }