Multiple seat support and api refactor (#2)

* Multiple seat support and api refractor

* Remove the need for two GlobalManagers

* Remove unnecessary `Arc<Mutex<_>>` and `if let`s with `unwrap()`

* Use cloned display to create eventloop in `new_threaded()`

* Update docs

* Wrap display to event queue
This commit is contained in:
trimental 2019-02-13 23:48:50 +08:00 committed by GitHub
parent 198bedde23
commit a36d3e3e8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 193 additions and 64 deletions

View file

@ -6,7 +6,7 @@ use sctk::utils::{DoubleMemPool, MemPool};
use sctk::window::{ConceptFrame, Event as WEvent, Window}; use sctk::window::{ConceptFrame, Event as WEvent, Window};
use sctk::Environment; use sctk::Environment;
use sctk::reexports::client::protocol::{wl_shm, wl_surface}; use sctk::reexports::client::protocol::{wl_seat, wl_shm, wl_surface};
use sctk::reexports::client::{Display, NewProxy}; use sctk::reexports::client::{Display, NewProxy};
use andrew::shapes::rectangle; use andrew::shapes::rectangle;
@ -18,14 +18,23 @@ fn main() {
Display::connect_to_env().expect("Failed to connect to the wayland server."); Display::connect_to_env().expect("Failed to connect to the wayland server.");
let env = Environment::from_display(&*display, &mut event_queue).unwrap(); let env = Environment::from_display(&*display, &mut event_queue).unwrap();
let mut clipboard = smithay_clipboard::WaylandClipboard::new_threaded( let mut clipboard = smithay_clipboard::WaylandClipboard::new_threaded(&display);
display.get_display_ptr() as *mut std::ffi::c_void,
);
let cb_contents = Arc::new(Mutex::new(String::new())); let cb_contents = Arc::new(Mutex::new(String::new()));
let seat_name = Arc::new(Mutex::new(String::new()));
let seat_name_clone = seat_name.clone();
let seat = env let seat = env
.manager .manager
.instantiate_range(1, 6, NewProxy::implement_dummy) .instantiate_range(2, 6, move |proxy| {
proxy.implement_closure(
move |event, _| {
if let wl_seat::Event::Name { name } = event {
*seat_name_clone.lock().unwrap() = name
}
},
(),
)
})
.unwrap(); .unwrap();
let need_redraw = Arc::new(atomic::AtomicBool::new(false)); let need_redraw = Arc::new(atomic::AtomicBool::new(false));
@ -39,11 +48,15 @@ fn main() {
} = event } = event
{ {
if text == " " { if text == " " {
*cb_contents_clone.lock().unwrap() = dbg!(clipboard.load()); *cb_contents_clone.lock().unwrap() =
dbg!(clipboard.load(seat_name.lock().unwrap().clone()));
need_redraw_clone.store(true, atomic::Ordering::Relaxed) need_redraw_clone.store(true, atomic::Ordering::Relaxed)
} else if text == "s" { } else if text == "s" {
clipboard clipboard.store(
.store("This is an example text thats been copied to the wayland clipboard :)"); seat_name.lock().unwrap().clone(),
"This is an example text thats been copied to the wayland clipboard :)"
.to_string(),
);
} }
} }
}) })

View file

@ -15,8 +15,9 @@
#![warn(missing_docs)] #![warn(missing_docs)]
use std::collections::HashMap;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::os::raw::c_void; use std::ops::Deref;
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread::sleep; use std::thread::sleep;
@ -26,13 +27,17 @@ use sctk::data_device::DataDevice;
use sctk::data_device::DataSource; use sctk::data_device::DataSource;
use sctk::data_device::DataSourceEvent; use sctk::data_device::DataSourceEvent;
use sctk::keyboard::{map_keyboard_auto, Event as KbEvent}; use sctk::keyboard::{map_keyboard_auto, Event as KbEvent};
use sctk::reexports::client::Display; use sctk::reexports::client::protocol::{
wl_data_device_manager, wl_display::WlDisplay, wl_registry, wl_seat,
};
use sctk::reexports::client::{Display, EventQueue, GlobalEvent, GlobalManager};
use sctk::wayland_client::sys::client::wl_display; use sctk::wayland_client::sys::client::wl_display;
use sctk::Environment;
type SeatMap = HashMap<String, (Arc<Mutex<DataDevice>>, u32)>;
enum WaylandRequest { enum WaylandRequest {
Store(String), Store(String, String),
Load, Load(String),
Kill, Kill,
} }
@ -53,49 +58,158 @@ impl WaylandClipboard {
/// ///
/// Spawns a new thread to dispatch messages to the wayland server every /// Spawns a new thread to dispatch messages to the wayland server every
/// 50ms to ensure the server can read stored data /// 50ms to ensure the server can read stored data
pub fn new_threaded(wayland_display: *mut c_void) -> Self { pub fn new_threaded(display: &Display) -> Self {
let (request_send, request_recv) = mpsc::channel::<WaylandRequest>(); let (request_send, request_recv) = mpsc::channel::<WaylandRequest>();
let (load_send, load_recv) = mpsc::channel(); let (load_send, load_recv) = mpsc::channel();
let display = display.clone();
let wayland_display = unsafe { (wayland_display as *mut wl_display).as_mut().unwrap() };
std::thread::spawn(move || { std::thread::spawn(move || {
let (display, mut event_queue) = let mut event_queue = display.create_event_queue();
unsafe { Display::from_external_display(wayland_display as *mut wl_display) }; let display = (*display)
let env = Environment::from_display(&*display, &mut event_queue).unwrap(); .as_ref()
.make_wrapper(&event_queue.get_token())
let seat = env
.manager
.instantiate_range(1, 6, |seat| seat.implement_dummy())
.unwrap(); .unwrap();
Self::clipboard_thread(&display, &mut event_queue, request_recv, load_send);
});
let device = DataDevice::init_for_seat(&env.data_device_manager, &seat, |_| {}); WaylandClipboard {
request_send,
load_recv,
}
}
let enter_serial = Arc::new(Mutex::new(None)); /// Creates a new WaylandClipboard object from a mutable `wl_display` ptr
let my_enter_serial = enter_serial.clone(); ///
let _keyboard = map_keyboard_auto(&seat, move |event, _| { /// Spawns a new thread to dispatch messages to the wayland server every
if let KbEvent::Enter { serial, .. } = event { /// 50ms to ensure the server can read stored data
*(my_enter_serial.lock().unwrap()) = Some(serial); pub unsafe fn new_threaded_from_external(display_ptr: *mut wl_display) -> Self {
let (request_send, request_recv) = mpsc::channel::<WaylandRequest>();
let (load_send, load_recv) = mpsc::channel();
let display = display_ptr.as_mut().unwrap();
std::thread::spawn(move || {
let (display, mut event_queue) = Display::from_external_display(display);
Self::clipboard_thread(&display, &mut event_queue, request_recv, load_send);
});
WaylandClipboard {
request_send,
load_recv,
}
}
fn implement_seat(
id: u32,
version: u32,
seat_map: Arc<Mutex<SeatMap>>,
data_device_manager: &wl_data_device_manager::WlDataDeviceManager,
reg: &wl_registry::WlRegistry,
) {
let seat_name = Arc::new(Mutex::new(String::new()));
let seat_name_clone = seat_name.clone();
let seat = reg
.bind::<wl_seat::WlSeat, _>(version, id, move |proxy| {
proxy.implement_closure(
move |event, _| {
if let wl_seat::Event::Name { name } = event {
*seat_name_clone.lock().unwrap() = name
}
},
(),
)
})
.unwrap();
let device = Arc::new(Mutex::new(DataDevice::init_for_seat(
data_device_manager,
&seat,
|_| {},
)));
map_keyboard_auto(&seat, move |event, _| match event {
KbEvent::Enter { serial, .. } => {
seat_map
.lock()
.unwrap()
.insert(seat_name.lock().unwrap().clone(), (device.clone(), serial));
}
KbEvent::Leave { .. } => {
seat_map.lock().unwrap().remove(&*seat_name.lock().unwrap());
}
_ => {}
})
.unwrap();
}
fn clipboard_thread(
display: &WlDisplay,
event_queue: &mut EventQueue,
request_recv: mpsc::Receiver<WaylandRequest>,
load_send: mpsc::Sender<String>,
) {
let seat_map = Arc::new(Mutex::new(SeatMap::new()));
let data_device_manager = Arc::new(Mutex::new(None));
let mut unimplemented_seats = Vec::new();
let data_device_manager_clone = data_device_manager.clone();
let seat_map_clone = seat_map.clone();
GlobalManager::new_with_cb(&display, move |event, reg| {
if let GlobalEvent::New {
id,
ref interface,
version,
} = event
{
if "wl_seat" == interface.as_str() && version >= 2 {
if let Some(ref data_device_manager) =
data_device_manager_clone.lock().unwrap().deref()
{
Self::implement_seat(
id,
version,
seat_map_clone.clone(),
data_device_manager,
&reg,
);
} else {
unimplemented_seats.push((id, version));
}
} else if "wl_data_device_manager" == interface.as_str() {
*data_device_manager_clone.lock().unwrap() = Some(
reg.bind::<wl_data_device_manager::WlDataDeviceManager, _>(
version,
id,
|proxy| proxy.implement_dummy(),
)
.unwrap(),
);
for (id, version) in &unimplemented_seats {
Self::implement_seat(
*id,
*version,
seat_map_clone.clone(),
data_device_manager_clone.lock().unwrap().as_ref().unwrap(),
&reg,
);
}
} }
}); }
});
loop { loop {
if let Ok(request) = request_recv.try_recv() { if let Ok(request) = request_recv.try_recv() {
match request { match request {
WaylandRequest::Load => { WaylandRequest::Load(seat_name) => {
if let Some((device, _)) = seat_map.lock().unwrap().get(&seat_name) {
// Load // Load
let mut reader = None; let mut reader = None;
device.with_selection(|offer| { device.lock().unwrap().with_selection(|offer| {
if let Some(offer) = offer { if let Some(offer) = offer {
offer.with_mime_types(|types| { offer.with_mime_types(|types| {
for t in types { if types.contains(&"text/plain;charset=utf-8".to_string()) {
if t == "text/plain;charset=utf-8" { reader = Some(
reader = Some( offer
offer .receive("text/plain;charset=utf-8".into())
.receive("text/plain;charset=utf-8".into()) .unwrap(),
.unwrap(), );
);
}
} }
}); });
} }
@ -109,9 +223,13 @@ impl WaylandClipboard {
load_send.send("".to_string()).unwrap(); load_send.send("".to_string()).unwrap();
} }
} }
WaylandRequest::Store(contents) => { }
WaylandRequest::Store(seat_name, contents) => {
if let Some((device, enter_serial)) =
seat_map.lock().unwrap().get(&seat_name)
{
let data_source = DataSource::new( let data_source = DataSource::new(
&env.data_device_manager, data_device_manager.lock().unwrap().as_ref().unwrap(),
&["text/plain;charset=utf-8"], &["text/plain;charset=utf-8"],
move |source_event| { move |source_event| {
if let DataSourceEvent::Send { mut pipe, .. } = source_event { if let DataSourceEvent::Send { mut pipe, .. } = source_event {
@ -119,41 +237,39 @@ impl WaylandClipboard {
} }
}, },
); );
if let Some(enter_serial) = *enter_serial.lock().unwrap() { device
device.set_selection(&Some(data_source), enter_serial); .lock()
} .unwrap()
.set_selection(&Some(data_source), *enter_serial);
event_queue.sync_roundtrip().unwrap(); event_queue.sync_roundtrip().unwrap();
} }
WaylandRequest::Kill => break,
} }
WaylandRequest::Kill => break,
} }
event_queue.dispatch_pending().unwrap();
sleep(Duration::from_millis(50));
} }
}); event_queue.dispatch_pending().unwrap();
sleep(Duration::from_millis(50));
WaylandClipboard {
request_send,
load_recv,
} }
} }
/// Returns text from the wayland clipboard /// Returns text from the wayland clipboard
/// ///
/// Only works when the window connected to the WlDisplay has /// Must be provided with a seat name and that seat must be in
/// keyboard focus /// focus to work
pub fn load(&mut self) -> String { pub fn load<S: Into<String>>(&mut self, seat_name: S) -> String {
self.request_send.send(WaylandRequest::Load).unwrap(); self.request_send
.send(WaylandRequest::Load(seat_name.into()))
.unwrap();
self.load_recv.recv().unwrap() self.load_recv.recv().unwrap()
} }
/// Stores text in the wayland clipboard /// Stores text in the wayland clipboard
/// ///
/// Only works when the window connected to the WlDisplay has /// Must be provided with a seat name and that seat must be in
/// keyboard focus /// focus to work
pub fn store<S: Into<String>>(&mut self, text: S) { pub fn store<S: Into<String>>(&mut self, seat_name: S, text: S) {
self.request_send self.request_send
.send(WaylandRequest::Store(text.into())) .send(WaylandRequest::Store(seat_name.into(), text.into()))
.unwrap() .unwrap()
} }
} }