Fix clipboard dying after TTY switch

This commit is contained in:
Kirill Chibisov 2020-03-03 08:14:36 +03:00
parent b97ebc4321
commit b783b2250c
2 changed files with 283 additions and 235 deletions

View file

@ -4,6 +4,7 @@
- Fix crash when receiving non-utf8 data - Fix crash when receiving non-utf8 data
- **Breaking** `load` and `load_primary` now return `Result<String>` to indicate errors - **Breaking** `load` and `load_primary` now return `Result<String>` to indicate errors
- Fix clipboard dying after TTY switch
## 0.3.7 -- 2020-02-27 ## 0.3.7 -- 2020-02-27

View file

@ -13,9 +13,13 @@ use nix::unistd::{close, pipe2};
use sctk::data_device::{DataDevice, DataSource, DataSourceEvent}; use sctk::data_device::{DataDevice, DataSource, DataSourceEvent};
use sctk::keyboard::{map_keyboard_auto, Event as KbEvent}; use sctk::keyboard::{map_keyboard_auto, Event as KbEvent};
use sctk::reexports::client::protocol::{ use sctk::reexports::client::protocol::{
wl_data_device_manager, wl_display::WlDisplay, wl_pointer::Event as PtrEvent, wl_registry, wl_data_device_manager,
wl_seat, wl_display::WlDisplay,
wl_pointer::Event as PtrEvent,
wl_registry,
wl_seat::{self, Capability},
}; };
use sctk::reexports::client::{Display, EventQueue, GlobalEvent, GlobalManager, NewProxy}; use sctk::reexports::client::{Display, EventQueue, GlobalEvent, GlobalManager, NewProxy};
use sctk::reexports::protocols::misc::gtk_primary_selection::client::{ use sctk::reexports::protocols::misc::gtk_primary_selection::client::{
gtk_primary_selection_device::Event as GtkPrimarySelectionDeviceEvent, gtk_primary_selection_device::Event as GtkPrimarySelectionDeviceEvent,
@ -38,7 +42,7 @@ use sctk::wayland_client::sys::client::wl_display;
type SeatMap = HashMap< type SeatMap = HashMap<
String, String,
( (
Arc<Mutex<DataDevice>>, Arc<Mutex<Option<DataDevice>>>,
u32, u32,
Arc<Mutex<Option<PrimarySelectionDevice>>>, Arc<Mutex<Option<PrimarySelectionDevice>>>,
Arc<Mutex<Option<PrimarySelectionOffer>>>, Arc<Mutex<Option<PrimarySelectionOffer>>>,
@ -298,10 +302,13 @@ fn clipboard_thread(
.get(&seat_name.unwrap_or_else(|| last_seat_name.lock().unwrap().clone())) .get(&seat_name.unwrap_or_else(|| last_seat_name.lock().unwrap().clone()))
.map_or(Ok(String::new()), |seat| { .map_or(Ok(String::new()), |seat| {
let mut reader = None; let mut reader = None;
seat.0.lock().unwrap().with_selection(|offer| { if let Some(device) = seat.0.lock().unwrap().as_ref() {
device.with_selection(|offer| {
if let Some(offer) = offer { if let Some(offer) = offer {
offer.with_mime_types(|types| { offer.with_mime_types(|types| {
if types.contains(&"text/plain;charset=utf-8".to_string()) { if types
.contains(&"text/plain;charset=utf-8".to_string())
{
reader = Some( reader = Some(
offer offer
.receive("text/plain;charset=utf-8".into()) .receive("text/plain;charset=utf-8".into())
@ -311,7 +318,9 @@ fn clipboard_thread(
}); });
} }
}); });
event_queue.sync_roundtrip().unwrap(); event_queue.sync_roundtrip().unwrap();
}
reader.map_or(Ok(String::new()), |mut reader| { reader.map_or(Ok(String::new()), |mut reader| {
let mut contents = String::new(); let mut contents = String::new();
if let Err(err) = reader.read_to_string(&mut contents) { if let Err(err) = reader.read_to_string(&mut contents) {
@ -335,6 +344,7 @@ fn clipboard_thread(
if let Some((device, enter_serial, _, _, _, _)) = seat_map if let Some((device, enter_serial, _, _, _, _)) = seat_map
.get(&seat_name.unwrap_or_else(|| last_seat_name.lock().unwrap().clone())) .get(&seat_name.unwrap_or_else(|| last_seat_name.lock().unwrap().clone()))
{ {
if let Some(device) = device.lock().unwrap().as_ref() {
let data_source = DataSource::new( let data_source = DataSource::new(
data_device_manager.lock().unwrap().as_ref().unwrap(), data_device_manager.lock().unwrap().as_ref().unwrap(),
&["text/plain;charset=utf-8"], &["text/plain;charset=utf-8"],
@ -344,14 +354,13 @@ fn clipboard_thread(
} }
}, },
); );
device
.lock() device.set_selection(&Some(data_source), *enter_serial);
.unwrap()
.set_selection(&Some(data_source), *enter_serial);
event_queue.sync_roundtrip().unwrap(); event_queue.sync_roundtrip().unwrap();
} }
} }
}
// Load text from primary clipboard // Load text from primary clipboard
ThreadRequest::LoadPrimary(seat_name) => { ThreadRequest::LoadPrimary(seat_name) => {
event_queue.sync_roundtrip().unwrap(); event_queue.sync_roundtrip().unwrap();
@ -539,131 +548,146 @@ fn implement_seat(
primary_device_manager: Arc<Mutex<Option<PrimarySelectionDeviceMgr>>>, primary_device_manager: Arc<Mutex<Option<PrimarySelectionDeviceMgr>>>,
gtk_primary_device_manager: Arc<Mutex<Option<GtkPrimarySelectionDeviceManager>>>, gtk_primary_device_manager: Arc<Mutex<Option<GtkPrimarySelectionDeviceManager>>>,
) { ) {
let device = Arc::new(Mutex::new(None));
let device_clone = device.clone();
let seat_name = Arc::new(Mutex::new(String::new())); let seat_name = Arc::new(Mutex::new(String::new()));
let seat_name_clone = seat_name.clone(); let seat_name_clone = seat_name.clone();
let seat_map_clone = seat_map.clone();
let primary_device = Arc::new(Mutex::new(None));
let primary_offer = Arc::new(Mutex::new(None));
let primary_device_clone = primary_device.clone();
let primary_offer_clone = primary_offer.clone();
let gtk_primary_device = Arc::new(Mutex::new(None));
let gtk_primary_offer = Arc::new(Mutex::new(None));
let gtk_primary_device_clone = gtk_primary_device.clone();
let gtk_primary_offer_clone = gtk_primary_offer.clone();
let mut pointer = None;
let mut keyboard = None;
// Register the seat // Register the seat
let seat = reg let seat = reg
.bind::<wl_seat::WlSeat, _>(version, id, move |proxy| { .bind::<wl_seat::WlSeat, _>(version, id, move |proxy| {
proxy.implement_closure( proxy.implement_closure(
move |event, _| { move |event, seat| match event {
if let wl_seat::Event::Name { name } = event { wl_seat::Event::Name { name } => *seat_name_clone.lock().unwrap() = name,
*seat_name_clone.lock().unwrap() = name wl_seat::Event::Capabilities { capabilities } => {
} if capabilities.contains(Capability::Pointer) {
}, if pointer.is_none() {
(), let device_clone = device_clone.clone();
)
})
.unwrap();
// Create a device for the seat let primary_device_clone = primary_device_clone.clone();
let device = Arc::new(Mutex::new(DataDevice::init_for_seat(
data_device_manager,
&seat,
|_| {},
)));
let primary_offer = Arc::new(Mutex::new(None));
let primary_offer_clone = primary_offer.clone();
let gtk_primary_offer = Arc::new(Mutex::new(None));
let gtk_primary_offer_clone = gtk_primary_offer.clone();
let seat_map_clone = seat_map.clone();
let seat_name_clone = seat_name.clone();
let (primary_device, gtk_primary_device) = if let Some(manager) =
&*primary_device_manager.lock().unwrap()
{
(
Arc::new(Mutex::new(
manager
.get_device(&seat, |proxy| {
let primary_offer_clone = primary_offer_clone.clone(); let primary_offer_clone = primary_offer_clone.clone();
proxy.implement_closure(
move |event, _| {
if let ZwpPrimarySelectionDeviceEvent::DataOffer { offer } = event {
*primary_offer_clone.lock().unwrap() =
Some(offer.implement_dummy());
let map_contents = seat_map_clone let gtk_primary_device_clone = gtk_primary_device_clone.clone();
.lock()
.unwrap()
.get(&seat_name_clone.lock().unwrap().clone())
.cloned();
if let Some(map_contents) = map_contents {
seat_map_clone.lock().unwrap().insert(
seat_name_clone.lock().unwrap().clone(),
(
map_contents.0.clone(),
map_contents.1,
map_contents.2.clone(),
primary_offer_clone.clone(),
Arc::new(Mutex::new(None)),
Arc::new(Mutex::new(None)),
),
);
}
}
},
(),
)
})
.ok(),
)),
Arc::new(Mutex::new(None)),
)
} else if let Some(manager) = &*gtk_primary_device_manager.lock().unwrap() {
(
Arc::new(Mutex::new(None)),
Arc::new(Mutex::new(
manager
.get_device(&seat, |proxy| {
let gtk_primary_offer_clone = gtk_primary_offer_clone.clone(); let gtk_primary_offer_clone = gtk_primary_offer_clone.clone();
proxy.implement_closure(
move |event, _| {
if let GtkPrimarySelectionDeviceEvent::DataOffer { offer } = event {
*gtk_primary_offer_clone.lock().unwrap() =
Some(offer.implement_dummy());
let map_contents = seat_map_clone let last_seat_name_clone = last_seat_name.clone();
let seat_map_clone = seat_map_clone.clone();
let seat_name_clone = seat_name_clone.clone();
pointer = Some(
seat.get_pointer(move |pointer| {
pointer.implement_closure(
move |evt, _| {
// Set this seat as the last to send an event
*last_seat_name_clone.lock().unwrap() =
seat_name_clone.lock().unwrap().clone();
// Get serials from recieved events from the seat
// pointer
match evt {
PtrEvent::Enter { serial, .. } => {
if let Some(seat) =
seat_map_clone.lock().unwrap().get_mut(
&seat_name_clone
.lock() .lock()
.unwrap() .unwrap()
.get(&seat_name_clone.lock().unwrap().clone()) .clone(),
.cloned(); )
if let Some(map_contents) = map_contents { {
// Update serial if "seat" is already
// presented
seat.1 = serial;
return;
}
seat_map_clone.lock().unwrap().insert( seat_map_clone.lock().unwrap().insert(
seat_name_clone.lock().unwrap().clone(), seat_name_clone.lock().unwrap().clone(),
( (
map_contents.0.clone(), device_clone.clone(),
map_contents.1, serial,
Arc::new(Mutex::new(None)), primary_device_clone.clone(),
Arc::new(Mutex::new(None)), primary_offer_clone.clone(),
map_contents.4.clone(), gtk_primary_device_clone.clone(),
gtk_primary_offer_clone.clone(), gtk_primary_offer_clone.clone(),
), ),
); );
} }
PtrEvent::Button { serial, .. } => {
if let Some(seat) =
seat_map_clone.lock().unwrap().get_mut(
&seat_name_clone
.lock()
.unwrap()
.clone(),
)
{
// Update serial if seat is already
// presented
seat.1 = serial;
return;
}
// This is for consistency with
// `PtrEvent::Enter`
seat_map_clone.lock().unwrap().insert(
seat_name_clone.lock().unwrap().clone(),
(
device_clone.clone(),
serial,
primary_device_clone.clone(),
primary_offer_clone.clone(),
gtk_primary_device_clone.clone(),
gtk_primary_offer_clone.clone(),
),
);
}
_ => {}
} }
}, },
(), (),
) )
}) })
.ok(), .unwrap(),
)), );
) }
} else { } else if let Some(pointer) = pointer.take() {
(Arc::new(Mutex::new(None)), Arc::new(Mutex::new(None))) // Release old pointer
}; pointer.release();
}
let seat_map_clone = seat_map.clone(); if capabilities.contains(Capability::Keyboard) {
let device_clone = device.clone(); if keyboard.is_none() {
let primary_device_clone = primary_device.clone(); let device_clone = device_clone.clone();
let primary_device_clone = primary_device_clone.clone();
let primary_offer_clone = primary_offer_clone.clone(); let primary_offer_clone = primary_offer_clone.clone();
let gtk_primary_device_clone = gtk_primary_device.clone();
let gtk_primary_device_clone = gtk_primary_device_clone.clone();
let gtk_primary_offer_clone = gtk_primary_offer_clone.clone(); let gtk_primary_offer_clone = gtk_primary_offer_clone.clone();
let seat_name_clone = seat_name.clone();
let last_seat_name_clone = last_seat_name.clone(); let last_seat_name_clone = last_seat_name.clone();
let seat_map_clone = seat_map_clone.clone();
let seat_name_clone = seat_name_clone.clone();
keyboard = Some(
map_keyboard_auto(&seat, move |event, _| { map_keyboard_auto(&seat, move |event, _| {
// Set this seat as the last to send an event // Set this seat as the last to send an event
*last_seat_name_clone.lock().unwrap() = seat_name_clone.lock().unwrap().clone(); *last_seat_name_clone.lock().unwrap() =
seat_name_clone.lock().unwrap().clone();
// Get serials from recieved events from the seat keyboard // Get serials from recieved events from the seat keyboard
match event { match event {
@ -702,70 +726,93 @@ fn implement_seat(
_ => {} _ => {}
} }
}) })
.unwrap(); .unwrap(),
seat.get_pointer(|pointer| {
pointer.implement_closure(
move |evt, _| {
// Set this seat as the last to send an event
*last_seat_name.lock().unwrap() = seat_name.lock().unwrap().clone();
// Get serials from recieved events from the seat pointer
match evt {
PtrEvent::Enter { serial, .. } => {
if let Some(seat) = seat_map
.lock()
.unwrap()
.get_mut(&seat_name.lock().unwrap().clone())
{
// Update serial if "seat" is already presented
seat.1 = serial;
return;
}
seat_map.lock().unwrap().insert(
seat_name.lock().unwrap().clone(),
(
device.clone(),
serial,
primary_device.clone(),
primary_offer.clone(),
gtk_primary_device.clone(),
gtk_primary_offer.clone(),
),
); );
} }
PtrEvent::Button { serial, .. } => { } else if let Some(keyboard) = keyboard.take() {
if let Some(seat) = seat_map // Release old keyboard
.lock() keyboard.release();
.unwrap()
.get_mut(&seat_name.lock().unwrap().clone())
{
// Update serial if seat is already presented
seat.1 = serial;
return;
} }
// This is for consistency with `PtrEvent::Enter`
seat_map.lock().unwrap().insert(
seat_name.lock().unwrap().clone(),
(
device.clone(),
serial,
primary_device.clone(),
primary_offer.clone(),
gtk_primary_device.clone(),
gtk_primary_offer.clone(),
),
);
}
_ => {}
} }
_ => (),
}, },
(), (),
) )
}) })
.unwrap(); .unwrap();
// Create a device for the seat
*device.lock().unwrap() = Some(DataDevice::init_for_seat(
data_device_manager,
&seat,
|_| {},
));
if let Some(manager) = &*primary_device_manager.lock().unwrap() {
*primary_device.lock().unwrap() = manager
.get_device(&seat, |proxy| {
proxy.implement_closure(
move |event, _| {
if let ZwpPrimarySelectionDeviceEvent::DataOffer { offer } = event {
*primary_offer.lock().unwrap() = Some(offer.implement_dummy());
let map_contents = seat_map
.lock()
.unwrap()
.get(&seat_name.lock().unwrap().clone())
.cloned();
if let Some(map_contents) = map_contents {
seat_map.lock().unwrap().insert(
seat_name.lock().unwrap().clone(),
(
map_contents.0.clone(),
map_contents.1,
map_contents.2,
primary_offer.clone(),
Arc::new(Mutex::new(None)),
Arc::new(Mutex::new(None)),
),
);
}
}
},
(),
)
})
.ok();
} else if let Some(manager) = &*gtk_primary_device_manager.lock().unwrap() {
*gtk_primary_device.lock().unwrap() = manager
.get_device(&seat, |proxy| {
proxy.implement_closure(
move |event, _| {
if let GtkPrimarySelectionDeviceEvent::DataOffer { offer } = event {
*gtk_primary_offer.lock().unwrap() = Some(offer.implement_dummy());
let map_contents = seat_map
.lock()
.unwrap()
.get(&seat_name.lock().unwrap().clone())
.cloned();
if let Some(map_contents) = map_contents {
seat_map.lock().unwrap().insert(
seat_name.lock().unwrap().clone(),
(
map_contents.0.clone(),
map_contents.1,
Arc::new(Mutex::new(None)),
Arc::new(Mutex::new(None)),
map_contents.4,
gtk_primary_offer.clone(),
),
);
}
}
},
(),
)
})
.ok();
}
} }
// Normalize \r and \r\n into \n. // Normalize \r and \r\n into \n.