1
1
mirror of https://github.com/wez/wezterm.git synced 2024-08-17 02:00:25 +03:00

Use PrimarySelectionHandler provided by SCTK

Also converts other DataDevice-handling code to use Read/WritePipe
instead of the FileDescriptor API.
This commit is contained in:
V 2024-04-08 07:05:59 +02:00 committed by Wez Furlong
parent b23a424e21
commit 84e8c966a1
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
6 changed files with 114 additions and 85 deletions

View File

@ -1,17 +1,14 @@
use anyhow::{anyhow, Error, bail};
use filedescriptor::{FileDescriptor, Pipe};
use anyhow::{anyhow, bail};
use smithay_client_toolkit as toolkit;
use toolkit::globals::GlobalData;
use wayland_client::{Dispatch, event_created_child};
use wayland_client::globals::{GlobalList, BindError};
use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1;
use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_device_v1::{ZwpPrimarySelectionDeviceV1, self, Event as PrimarySelectionDeviceEvent};
use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_offer_v1::{ZwpPrimarySelectionOfferV1, Event as PrimarySelectionOfferEvent};
use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_source_v1::{ZwpPrimarySelectionSourceV1, Event as PrimarySelectionSourceEvent};
use std::io::Write;
use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
use std::os::fd::AsRawFd;
use std::sync::{Arc, Mutex};
use toolkit::reexports::client::protocol::wl_data_offer::WlDataOffer;
use toolkit::data_device_manager::data_offer::SelectionOffer;
use toolkit::data_device_manager::{ReadPipe, WritePipe};
use toolkit::primary_selection::device::PrimarySelectionDeviceHandler;
use toolkit::primary_selection::selection::PrimarySelectionSourceHandler;
use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1;
use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1;
use crate::{Clipboard, ConnectionOps};
@ -20,7 +17,7 @@ use super::state::WaylandState;
#[derive(Default)]
pub struct CopyAndPaste {
data_offer: Option<WlDataOffer>,
data_offer: Option<SelectionOffer>,
}
impl std::fmt::Debug for CopyAndPaste {
@ -36,41 +33,31 @@ impl CopyAndPaste {
Arc::new(Mutex::new(Default::default()))
}
pub(super) fn get_clipboard_data(
&mut self,
clipboard: Clipboard,
) -> anyhow::Result<FileDescriptor> {
pub(super) fn get_clipboard_data(&mut self, clipboard: Clipboard) -> anyhow::Result<ReadPipe> {
let conn = crate::Connection::get().unwrap().wayland();
let wayland_state = conn.wayland_state.borrow();
let primary_selection = if let Clipboard::PrimarySelection = clipboard {
wayland_state.primary_selection_manager.as_ref()
wayland_state.primary_selection_device.as_ref()
} else {
None
};
match primary_selection {
Some(primary_selection) => {
let inner = primary_selection.inner.lock().unwrap();
let offer = inner
.offer
.as_ref()
let offer = primary_selection
.data()
.selection_offer()
.ok_or_else(|| anyhow!("no primary selection offer"))?;
let pipe = Pipe::new().map_err(Error::msg)?;
offer.receive(TEXT_MIME_TYPE.to_string(), unsafe {
BorrowedFd::borrow_raw(pipe.write.as_raw_fd())
});
Ok(pipe.read)
let pipe = offer.receive(TEXT_MIME_TYPE.to_string())?;
Ok(pipe)
}
None => {
let offer = self
.data_offer
.as_ref()
.ok_or_else(|| anyhow!("no data offer"))?;
let pipe = Pipe::new().map_err(Error::msg)?;
offer.receive(TEXT_MIME_TYPE.to_string(), unsafe {
BorrowedFd::borrow_raw(pipe.write.as_raw_fd())
});
Ok(pipe.read)
let pipe = offer.receive(TEXT_MIME_TYPE.to_string())?;
Ok(pipe)
}
}
}
@ -82,18 +69,16 @@ impl CopyAndPaste {
let last_serial = *wayland_state.last_serial.borrow();
let primary_selection = if let Clipboard::PrimarySelection = clipboard {
wayland_state.primary_selection_manager.as_ref()
wayland_state.primary_selection_device.as_ref()
} else {
None
};
match primary_selection {
Some(primary_selection) => {
let manager = &primary_selection.manager;
let selection_device = wayland_state.primary_select_device.as_ref().unwrap();
let source = manager.create_source(&qh, PrimarySelectionManagerData::default());
source.offer(TEXT_MIME_TYPE.to_string());
selection_device.set_selection(Some(&source), last_serial);
let manager = wayland_state.primary_selection_manager.as_ref().unwrap();
let source = manager.create_selection_source(&qh, [TEXT_MIME_TYPE]);
source.set_selection(&primary_selection, last_serial);
wayland_state
.primary_selection_source
.replace((source, data));
@ -109,7 +94,7 @@ impl CopyAndPaste {
}
}
pub(super) fn confirm_selection(&mut self, offer: WlDataOffer) {
pub(super) fn confirm_selection(&mut self, offer: SelectionOffer) {
self.data_offer.replace(offer);
}
}
@ -126,14 +111,22 @@ impl WaylandState {
}
}
pub(super) fn write_selection_to_pipe(fd: FileDescriptor, text: &str) {
pub(super) fn write_selection_to_pipe(fd: WritePipe, text: &str) {
if let Err(e) = write_pipe_with_timeout(fd, text.as_bytes()) {
log::error!("while sending primary selection to pipe: {}", e);
}
}
fn write_pipe_with_timeout(mut file: FileDescriptor, data: &[u8]) -> anyhow::Result<()> {
file.set_non_blocking(true)?;
fn write_pipe_with_timeout(mut file: WritePipe, data: &[u8]) -> anyhow::Result<()> {
// set non-blocking I/O on the pipe
// (adapted from FileDescriptor::set_non_blocking_impl in /filedescriptor/src/unix.rs)
if unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK) } != 0 {
bail!(
"failed to change non-blocking mode: {}",
std::io::Error::last_os_error()
)
}
let mut pfd = libc::pollfd {
fd: file.as_raw_fd(),
events: libc::POLLOUT,
@ -161,6 +154,8 @@ fn write_pipe_with_timeout(mut file: FileDescriptor, data: &[u8]) -> anyhow::Res
Ok(())
}
/*
// Smithay has their own primary selection handler in 0.18
// Some code borrowed from https://github.com/Smithay/client-toolkit/commit/4a5c4f59f640bc588a55277261bbed1bd2abea98
pub(super) struct PrimarySelectionManagerState {
@ -308,3 +303,47 @@ impl Dispatch<ZwpPrimarySelectionDeviceV1, PrimarySelectionManagerData, WaylandS
}
}
}
*/
impl PrimarySelectionDeviceHandler for WaylandState {
fn selection(
&mut self,
_conn: &wayland_client::Connection,
_qh: &wayland_client::QueueHandle<Self>,
_primary_selection_device: &ZwpPrimarySelectionDeviceV1,
) {
// TODO: do we need to do anything here?
}
}
impl PrimarySelectionSourceHandler for WaylandState {
fn send_request(
&mut self,
_conn: &wayland_client::Connection,
_qh: &wayland_client::QueueHandle<Self>,
source: &ZwpPrimarySelectionSourceV1,
mime: String,
write_pipe: toolkit::data_device_manager::WritePipe,
) {
if mime != TEXT_MIME_TYPE {
return;
};
if let Some((ps_source, data)) = &self.primary_selection_source {
if ps_source.inner() != source {
return;
}
write_selection_to_pipe(write_pipe, data);
}
}
fn cancelled(
&mut self,
_conn: &wayland_client::Connection,
_qh: &wayland_client::QueueHandle<Self>,
source: &ZwpPrimarySelectionSourceV1,
) {
self.primary_selection_source.take();
source.destroy();
}
}

View File

@ -1,6 +1,3 @@
use std::os::fd::{FromRawFd, IntoRawFd};
use filedescriptor::FileDescriptor;
use smithay_client_toolkit::data_device_manager::data_device::DataDeviceHandler;
use smithay_client_toolkit::data_device_manager::data_offer::DataOfferHandler;
use smithay_client_toolkit::data_device_manager::data_source::DataSourceHandler;
@ -59,7 +56,6 @@ impl DataDeviceHandler for WaylandState {
.unwrap();
let window_id = SurfaceUserData::from_wl(&offer.surface).window_id;
let offer = offer.inner().clone();
pstate.drag_and_drop.offer = Some(SurfaceAndOffer { window_id, offer });
}
@ -110,10 +106,7 @@ impl DataDeviceHandler for WaylandState {
}
if let Some(copy_and_paste) = self.resolve_copy_and_paste() {
copy_and_paste
.lock()
.unwrap()
.confirm_selection(offer.inner().clone());
copy_and_paste.lock().unwrap().confirm_selection(offer);
}
}
@ -191,7 +184,6 @@ impl DataSourceHandler for WaylandState {
if cp_source.inner() != source {
return;
}
let fd = unsafe { FileDescriptor::from_raw_fd(fd.into_raw_fd()) };
write_selection_to_pipe(fd, data);
}
}

View File

@ -1,10 +1,9 @@
use crate::wayland::read_pipe_with_timeout;
use crate::ConnectionOps;
use filedescriptor::{FileDescriptor, Pipe};
use smithay_client_toolkit as toolkit;
use std::os::fd::{AsRawFd, BorrowedFd};
use std::path::PathBuf;
use toolkit::reexports::client::protocol::wl_data_offer::WlDataOffer;
use toolkit::data_device_manager::data_offer::DragOffer;
use toolkit::data_device_manager::ReadPipe;
use url::Url;
use super::data_device::URI_MIME_TYPE;
@ -17,12 +16,12 @@ pub struct DragAndDrop {
pub(super) struct SurfaceAndOffer {
pub(super) window_id: usize,
pub(super) offer: WlDataOffer,
pub(super) offer: DragOffer,
}
pub(super) struct SurfaceAndPipe {
pub(super) window_id: usize,
pub(super) read: FileDescriptor,
pub(super) read: ReadPipe,
}
impl DragAndDrop {
@ -30,18 +29,15 @@ impl DragAndDrop {
/// returning that surface and pipe descriptor.
pub(super) fn create_pipe_for_drop(&mut self) -> Option<SurfaceAndPipe> {
let SurfaceAndOffer { window_id, offer } = self.offer.take()?;
let pipe = Pipe::new()
.map_err(|err| log::error!("Unable to create pipe: {:#}", err))
let read = offer
.receive(URI_MIME_TYPE.to_string())
.map_err(|err| log::error!("Unable to receive data: {:#}", err))
.ok()?;
offer.receive(URI_MIME_TYPE.to_string(), unsafe {
BorrowedFd::borrow_raw(pipe.write.as_raw_fd())
});
let read = pipe.read;
offer.finish();
Some(SurfaceAndPipe { window_id, read })
}
pub(super) fn read_paths_from_pipe(read: FileDescriptor) -> Option<Vec<PathBuf>> {
pub(super) fn read_paths_from_pipe(read: ReadPipe) -> Option<Vec<PathBuf>> {
read_pipe_with_timeout(read)
.map_err(|err| {
log::error!("Error while reading pipe from drop result: {:#}", err);

View File

@ -3,7 +3,6 @@ use smithay_client_toolkit::seat::{Capability, SeatHandler, SeatState};
use wayland_client::protocol::wl_seat::WlSeat;
use wayland_client::{Connection, QueueHandle};
use crate::wayland::copy_and_paste::PrimarySelectionManagerData;
use crate::wayland::keyboard::KeyboardData;
use crate::wayland::pointer::PointerUserData;
use crate::wayland::SurfaceUserData;
@ -56,11 +55,11 @@ impl SeatHandler for WaylandState {
let data_device = data_device_manager.get_data_device(qh, &seat);
self.data_device.replace(data_device);
let primary_select_device = self.primary_selection_manager.as_ref().map(|m| {
m.manager
.get_device(&seat, qh, PrimarySelectionManagerData::default())
});
self.primary_select_device = primary_select_device;
let primary_selection_device = self
.primary_selection_manager
.as_ref()
.map(|m| m.get_selection_device(qh, &seat));
self.primary_selection_device = primary_selection_device;
}
}

View File

@ -9,6 +9,9 @@ use smithay_client_toolkit::data_device_manager::data_source::CopyPasteSource;
use smithay_client_toolkit::data_device_manager::DataDeviceManagerState;
use smithay_client_toolkit::globals::GlobalData;
use smithay_client_toolkit::output::{OutputHandler, OutputState};
use smithay_client_toolkit::primary_selection::device::PrimarySelectionDevice;
use smithay_client_toolkit::primary_selection::selection::PrimarySelectionSource;
use smithay_client_toolkit::primary_selection::PrimarySelectionManagerState;
use smithay_client_toolkit::reexports::protocols_wlr::output_management::v1::client::zwlr_output_head_v1::ZwlrOutputHeadV1;
use smithay_client_toolkit::reexports::protocols_wlr::output_management::v1::client::zwlr_output_manager_v1::ZwlrOutputManagerV1;
use smithay_client_toolkit::reexports::protocols_wlr::output_management::v1::client::zwlr_output_mode_v1::ZwlrOutputModeV1;
@ -20,23 +23,18 @@ use smithay_client_toolkit::shm::slot::SlotPool;
use smithay_client_toolkit::shm::{Shm, ShmHandler};
use smithay_client_toolkit::subcompositor::SubcompositorState;
use smithay_client_toolkit::{
delegate_compositor, delegate_data_device, delegate_output, delegate_pointer, delegate_registry, delegate_seat, delegate_shm, delegate_subcompositor, delegate_xdg_shell, delegate_xdg_window, registry_handlers
delegate_compositor, delegate_data_device, delegate_output, delegate_pointer, delegate_primary_selection, delegate_registry, delegate_seat, delegate_shm, delegate_subcompositor, delegate_xdg_shell, delegate_xdg_window, registry_handlers
};
use wayland_client::backend::ObjectId;
use wayland_client::globals::GlobalList;
use wayland_client::protocol::wl_keyboard::WlKeyboard;
use wayland_client::protocol::wl_output::WlOutput;
use wayland_client::{delegate_dispatch, Connection, QueueHandle};
use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1;
use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1;
use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1;
use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1;
use wayland_protocols::wp::text_input::zv3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3;
use wayland_protocols::wp::text_input::zv3::client::zwp_text_input_v3::ZwpTextInputV3;
use crate::x11::KeyboardWithFallback;
use super::copy_and_paste::{PrimarySelectionManagerData, PrimarySelectionManagerState};
use super::inputhandler::{TextInputData, TextInputState};
use super::pointer::{PendingMouse, PointerUserData};
use super::{OutputManagerData, OutputManagerState, SurfaceUserData, WaylandWindowInner};
@ -69,8 +67,8 @@ pub(super) struct WaylandState {
pub(super) data_device: Option<DataDevice>,
pub(super) copy_paste_source: Option<(CopyPasteSource, String)>,
pub(super) primary_selection_manager: Option<PrimarySelectionManagerState>,
pub(super) primary_select_device: Option<ZwpPrimarySelectionDeviceV1>,
pub(super) primary_selection_source: Option<(ZwpPrimarySelectionSourceV1, String)>,
pub(super) primary_selection_device: Option<PrimarySelectionDevice>,
pub(super) primary_selection_source: Option<(PrimarySelectionSource, String)>,
pub(super) shm: Shm,
pub(super) mem_pool: RefCell<SlotPool>,
}
@ -111,7 +109,7 @@ impl WaylandState {
data_device: None,
copy_paste_source: None,
primary_selection_manager: PrimarySelectionManagerState::bind(globals, qh).ok(),
primary_select_device: None,
primary_selection_device: None,
primary_selection_source: None,
shm,
mem_pool: RefCell::new(mem_pool),
@ -169,14 +167,11 @@ delegate_pointer!(WaylandState, pointer: [PointerUserData]);
delegate_xdg_shell!(WaylandState);
delegate_xdg_window!(WaylandState);
delegate_primary_selection!(WaylandState);
delegate_dispatch!(WaylandState: [ZwpTextInputManagerV3: GlobalData] => TextInputState);
delegate_dispatch!(WaylandState: [ZwpTextInputV3: TextInputData] => TextInputState);
delegate_dispatch!(WaylandState: [ZwlrOutputManagerV1: GlobalData] => OutputManagerState);
delegate_dispatch!(WaylandState: [ZwlrOutputHeadV1: OutputManagerData] => OutputManagerState);
delegate_dispatch!(WaylandState: [ZwlrOutputModeV1: OutputManagerData] => OutputManagerState);
delegate_dispatch!(WaylandState: [ZwpPrimarySelectionDeviceManagerV1: GlobalData] => PrimarySelectionManagerState);
delegate_dispatch!(WaylandState: [ZwpPrimarySelectionDeviceV1: PrimarySelectionManagerData] => PrimarySelectionManagerState);
delegate_dispatch!(WaylandState: [ZwpPrimarySelectionSourceV1: PrimarySelectionManagerData] => PrimarySelectionManagerState);
delegate_dispatch!(WaylandState: [ZwpPrimarySelectionOfferV1: PrimarySelectionManagerData] => PrimarySelectionManagerState);

View File

@ -14,13 +14,13 @@ use anyhow::{anyhow, bail};
use async_io::Timer;
use async_trait::async_trait;
use config::ConfigHandle;
use filedescriptor::FileDescriptor;
use promise::{Future, Promise};
use raw_window_handle::{
HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
WaylandDisplayHandle, WaylandWindowHandle,
};
use smithay_client_toolkit::compositor::{CompositorHandler, SurfaceData, SurfaceDataExt};
use smithay_client_toolkit::data_device_manager::ReadPipe;
use smithay_client_toolkit::reexports::csd_frame::{
DecorationsFrame, FrameAction, ResizeEdge, WindowState as SCTKWindowState,
};
@ -463,10 +463,18 @@ pub(crate) struct PendingEvent {
pub(crate) window_state: Option<WindowState>,
}
pub(crate) fn read_pipe_with_timeout(mut file: FileDescriptor) -> anyhow::Result<String> {
pub(crate) fn read_pipe_with_timeout(mut file: ReadPipe) -> anyhow::Result<String> {
let mut result = Vec::new();
file.set_non_blocking(true)?;
// set non-blocking I/O on the pipe
// (adapted from FileDescriptor::set_non_blocking_impl in /filedescriptor/src/unix.rs)
if unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK) } != 0 {
bail!(
"failed to change non-blocking mode: {}",
std::io::Error::last_os_error()
)
}
let mut pfd = libc::pollfd {
fd: file.as_raw_fd(),
events: libc::POLLIN,