diff --git a/window/src/os/wayland/copy_and_paste.rs b/window/src/os/wayland/copy_and_paste.rs index 42db0a5d6..23f07fb18 100644 --- a/window/src/os/wayland/copy_and_paste.rs +++ b/window/src/os/wayland/copy_and_paste.rs @@ -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, + data_offer: Option, } 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 { + pub(super) fn get_clipboard_data(&mut self, clipboard: Clipboard) -> anyhow::Result { 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, + _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, + 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, + source: &ZwpPrimarySelectionSourceV1, + ) { + self.primary_selection_source.take(); + source.destroy(); + } +} diff --git a/window/src/os/wayland/data_device.rs b/window/src/os/wayland/data_device.rs index 201f25d61..8726cb0ec 100644 --- a/window/src/os/wayland/data_device.rs +++ b/window/src/os/wayland/data_device.rs @@ -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); } } diff --git a/window/src/os/wayland/drag_and_drop.rs b/window/src/os/wayland/drag_and_drop.rs index beb7d7194..5de067656 100644 --- a/window/src/os/wayland/drag_and_drop.rs +++ b/window/src/os/wayland/drag_and_drop.rs @@ -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 { 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> { + pub(super) fn read_paths_from_pipe(read: ReadPipe) -> Option> { read_pipe_with_timeout(read) .map_err(|err| { log::error!("Error while reading pipe from drop result: {:#}", err); diff --git a/window/src/os/wayland/seat.rs b/window/src/os/wayland/seat.rs index 6f5443781..97d8af38a 100644 --- a/window/src/os/wayland/seat.rs +++ b/window/src/os/wayland/seat.rs @@ -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; } } diff --git a/window/src/os/wayland/state.rs b/window/src/os/wayland/state.rs index c9d7a3dbe..fdb269e01 100644 --- a/window/src/os/wayland/state.rs +++ b/window/src/os/wayland/state.rs @@ -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, pub(super) copy_paste_source: Option<(CopyPasteSource, String)>, pub(super) primary_selection_manager: Option, - pub(super) primary_select_device: Option, - pub(super) primary_selection_source: Option<(ZwpPrimarySelectionSourceV1, String)>, + pub(super) primary_selection_device: Option, + pub(super) primary_selection_source: Option<(PrimarySelectionSource, String)>, pub(super) shm: Shm, pub(super) mem_pool: RefCell, } @@ -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); diff --git a/window/src/os/wayland/window.rs b/window/src/os/wayland/window.rs index c9c4a8b67..adc3ee0de 100644 --- a/window/src/os/wayland/window.rs +++ b/window/src/os/wayland/window.rs @@ -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, } -pub(crate) fn read_pipe_with_timeout(mut file: FileDescriptor) -> anyhow::Result { +pub(crate) fn read_pipe_with_timeout(mut file: ReadPipe) -> anyhow::Result { 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,