diff --git a/Cargo.lock b/Cargo.lock index 222dd130d..305d4f3ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1274,12 +1274,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "cursor-icon" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" - [[package]] name = "d3d12" version = "0.7.0" @@ -3224,18 +3218,18 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.8.0" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] [[package]] name = "memmap2" -version = "0.9.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" dependencies = [ "libc", ] @@ -4300,6 +4294,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-xml" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +dependencies = [ + "memchr", +] + [[package]] name = "quick-xml" version = "0.30.0" @@ -5052,25 +5055,23 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "smithay-client-toolkit" -version = "0.18.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" +checksum = "e1476c3d89bb67079264b88aaf4f14358353318397e083b7c4e8c14517f55de7" dependencies = [ - "bitflags 2.4.2", - "cursor-icon", - "libc", + "bitflags 1.3.2", + "dlib", + "lazy_static", "log", - "memmap2 0.9.4", - "rustix 0.38.31", + "memmap2 0.5.10", + "nix 0.26.4", "thiserror", "wayland-backend", "wayland-client", - "wayland-csd-frame", "wayland-cursor", "wayland-protocols", "wayland-protocols-wlr", "wayland-scanner", - "xkeysym", ] [[package]] @@ -6124,13 +6125,14 @@ checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "wayland-backend" -version = "0.3.3" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" +checksum = "41b48e27457e8da3b2260ac60d0a94512f5cba36448679f3747c0865b7893ed8" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.31", + "io-lifetimes", + "nix 0.26.4", "scoped-tls", "smallvec", "wayland-sys", @@ -6138,43 +6140,32 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.2" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" +checksum = "489c9654770f674fc7e266b3c579f4053d7551df0ceb392f153adb1f9ed06ac8" dependencies = [ - "bitflags 2.4.2", - "rustix 0.38.31", + "bitflags 1.3.2", + "nix 0.26.4", "wayland-backend", "wayland-scanner", ] -[[package]] -name = "wayland-csd-frame" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" -dependencies = [ - "bitflags 2.4.2", - "cursor-icon", - "wayland-backend", -] - [[package]] name = "wayland-cursor" -version = "0.31.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" +checksum = "2d0c3a0d5b4b688b07b0442362d3ed6bf04724fcc16cd69ab6285b90dbc487aa" dependencies = [ - "rustix 0.38.31", + "nix 0.26.4", "wayland-client", "xcursor", ] [[package]] name = "wayland-egl" -version = "0.32.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355f652e5a24ae02d2ad536c8fc2d3dcc6c2bd635027cd6103a193e7d75eeda2" +checksum = "1187695fe81c3153c3163f9d2953149f638c5d7dbc6fe988914ca3f4961e28ed" dependencies = [ "wayland-backend", "wayland-sys", @@ -6182,11 +6173,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.31.2" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +checksum = "3b28101e5ca94f70461a6c2d610f76d85ad223d042dd76585ab23d3422dd9b4d" dependencies = [ - "bitflags 2.4.2", + "bitflags 1.3.2", "wayland-backend", "wayland-client", "wayland-scanner", @@ -6194,11 +6185,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +checksum = "fce991093320e4a6a525876e6b629ab24da25f9baef0c2e0080ad173ec89588a" dependencies = [ - "bitflags 2.4.2", + "bitflags 1.3.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6207,20 +6198,20 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.1" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" +checksum = "b9b873b257fbc32ec909c0eb80dea312076a67014e65e245f5eb69a6b8ab330e" dependencies = [ "proc-macro2", - "quick-xml 0.31.0", + "quick-xml 0.28.2", "quote", ] [[package]] name = "wayland-sys" -version = "0.31.1" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" dependencies = [ "dlib", "log", @@ -6890,7 +6881,6 @@ dependencies = [ "tiny-skia", "url", "wayland-client", - "wayland-csd-frame", "wayland-egl", "wayland-protocols", "wezterm-bidi", diff --git a/window/Cargo.toml b/window/Cargo.toml index 8915b6026..b4598297b 100644 --- a/window/Cargo.toml +++ b/window/Cargo.toml @@ -15,7 +15,7 @@ k9 = "0.11.0" gl_generator = "0.14" [features] -wayland = ["wayland-client", "smithay-client-toolkit", "wayland-egl", "wayland-protocols", "wayland-csd-frame"] +wayland = ["wayland-client", "smithay-client-toolkit", "wayland-egl", "wayland-protocols"] [dependencies] async-channel = "1.6" @@ -81,11 +81,10 @@ xcb-imdkit = { version="0.3", git="https://github.com/wez/xcb-imdkit-rs.git", re zbus = "3.14" zvariant = "3.15" -smithay-client-toolkit = {version = "0.18.1", default-features=false, optional=true} -wayland-protocols = {version="0.31", optional=true} -wayland-client = {version="0.31", optional=true} -wayland-egl = {version="0.32", optional=true} -wayland-csd-frame = { version = "0.3.0", optional = true} +smithay-client-toolkit = {version = "0.17.0", default-features=false, optional=true} +wayland-protocols = {version="0.30", optional=true} +wayland-client = {version="0.30", optional=true} +wayland-egl = {version="0.30", optional=true} [target.'cfg(target_os="macos")'.dependencies] cocoa = "0.25" diff --git a/window/src/os/wayland/connection.rs b/window/src/os/wayland/connection.rs index e133590eb..f917bdfca 100644 --- a/window/src/os/wayland/connection.rs +++ b/window/src/os/wayland/connection.rs @@ -58,11 +58,7 @@ impl WaylandConnection { let mut events = Events::with_capacity(8); let wl_fd = { - let read_guard = self - .event_queue - .borrow() - .prepare_read() - .ok_or_else(|| anyhow::anyhow!("Failed to preapre event queue"))?; + let read_guard = self.event_queue.borrow().prepare_read()?; read_guard.connection_fd().as_raw_fd() }; @@ -104,7 +100,7 @@ impl WaylandConnection { continue; } - if let Some(guard) = event_q.prepare_read() { + if let Ok(guard) = event_q.prepare_read() { if let Err(err) = guard.read() { log::trace!("Event Q error: {:?}", err); if let WaylandError::Protocol(perr) = err { diff --git a/window/src/os/wayland/copy_and_paste.rs b/window/src/os/wayland/copy_and_paste.rs index 63daa361d..ad83e462b 100644 --- a/window/src/os/wayland/copy_and_paste.rs +++ b/window/src/os/wayland/copy_and_paste.rs @@ -1,15 +1,17 @@ -use anyhow::{anyhow, bail}; +use anyhow::{anyhow, Error, bail}; +use filedescriptor::{FileDescriptor, Pipe}; 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; +use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd}; use std::sync::{Arc, Mutex}; -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_client::{Connection as WConnection, QueueHandle}; -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 toolkit::reexports::client::protocol::wl_data_offer::WlDataOffer; use crate::{Clipboard, ConnectionOps}; @@ -18,7 +20,7 @@ use super::state::WaylandState; #[derive(Default)] pub struct CopyAndPaste { - data_offer: Option, + data_offer: Option, } impl std::fmt::Debug for CopyAndPaste { @@ -34,31 +36,37 @@ 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_device.as_ref() + wayland_state.primary_selection_manager.as_ref() } else { None }; match primary_selection { Some(primary_selection) => { - let offer = primary_selection - .data() - .selection_offer() + let inner = primary_selection.inner.lock().unwrap(); + let offer = inner + .offer + .as_ref() .ok_or_else(|| anyhow!("no primary selection offer"))?; - let pipe = offer.receive(TEXT_MIME_TYPE.to_string())?; - Ok(pipe) + let pipe = Pipe::new().map_err(Error::msg)?; + offer.receive(TEXT_MIME_TYPE.to_string(), pipe.write.as_raw_fd()); + Ok(pipe.read) } None => { let offer = self .data_offer .as_ref() .ok_or_else(|| anyhow!("no data offer"))?; - let pipe = offer.receive(TEXT_MIME_TYPE.to_string())?; - Ok(pipe) + let pipe = Pipe::new().map_err(Error::msg)?; + offer.receive(TEXT_MIME_TYPE.to_string(), pipe.write.as_raw_fd()); + Ok(pipe.read) } } } @@ -70,16 +78,18 @@ impl CopyAndPaste { let last_serial = *wayland_state.last_serial.borrow(); let primary_selection = if let Clipboard::PrimarySelection = clipboard { - wayland_state.primary_selection_device.as_ref() + wayland_state.primary_selection_manager.as_ref() } else { None }; match primary_selection { - Some(selection_device) => { - let manager = wayland_state.primary_selection_manager.as_ref().unwrap(); - let source = manager.create_selection_source(&qh, [TEXT_MIME_TYPE]); - source.set_selection(&selection_device, last_serial); + 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); wayland_state .primary_selection_source .replace((source, data)); @@ -95,7 +105,7 @@ impl CopyAndPaste { } } - pub(super) fn confirm_selection(&mut self, offer: SelectionOffer) { + pub(super) fn confirm_selection(&mut self, offer: WlDataOffer) { self.data_offer.replace(offer); } } @@ -112,17 +122,14 @@ impl WaylandState { } } -pub(super) fn write_selection_to_pipe(pipe: WritePipe, text: &str) { - if let Err(e) = write_pipe_with_timeout(pipe, text.as_bytes()) { +pub(super) fn write_selection_to_pipe(fd: FileDescriptor, 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: WritePipe, data: &[u8]) -> anyhow::Result<()> { - // Set the file as non-blocking - unsafe { - libc::fcntl(file.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK); - } +fn write_pipe_with_timeout(mut file: FileDescriptor, data: &[u8]) -> anyhow::Result<()> { + file.set_non_blocking(true)?; let mut pfd = libc::pollfd { fd: file.as_raw_fd(), events: libc::POLLOUT, @@ -150,43 +157,150 @@ fn write_pipe_with_timeout(mut file: WritePipe, data: &[u8]) -> anyhow::Result<( Ok(()) } -impl PrimarySelectionSourceHandler for WaylandState { - fn send_request( - &mut self, - _conn: &WConnection, - _qh: &QueueHandle, - source: &ZwpPrimarySelectionSourceV1, - mime_type: String, - write_pipe: WritePipe, +// 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 { + pub(super) manager: ZwpPrimarySelectionDeviceManagerV1, + inner: Mutex, +} + +#[derive(Default, Debug)] +struct PrimaryInner { + pending_offer: Option, + offer: Option, + valid_mime: bool, +} + +#[derive(Default)] +pub(super) struct PrimarySelectionManagerData {} + +impl PrimarySelectionManagerState { + pub(super) fn bind( + globals: &GlobalList, + queue_handle: &wayland_client::QueueHandle, + ) -> Result { + let manager = globals.bind(queue_handle, 1..=1, GlobalData)?; + Ok(Self { + manager, + inner: Mutex::new(PrimaryInner::default()), + }) + } +} + +impl Dispatch + for PrimarySelectionManagerState +{ + fn event( + _state: &mut WaylandState, + _proxy: &ZwpPrimarySelectionDeviceManagerV1, + _event: ::Event, + _data: &GlobalData, + _conn: &wayland_client::Connection, + _qhandle: &wayland_client::QueueHandle, ) { - if mime_type != TEXT_MIME_TYPE { - return; - } - if let Some((ps_source, data)) = &self.primary_selection_source { - if ps_source.inner() != source { - return; + unreachable!("primary selection manager has no events"); + } +} + +impl Dispatch + for PrimarySelectionManagerState +{ + fn event( + state: &mut WaylandState, + source: &ZwpPrimarySelectionSourceV1, + event: ::Event, + _data: &PrimarySelectionManagerData, + _conn: &wayland_client::Connection, + _qhandle: &wayland_client::QueueHandle, + ) { + match event { + PrimarySelectionSourceEvent::Send { mime_type, fd } => { + if mime_type != TEXT_MIME_TYPE { + return; + }; + + if let Some((ps_source, data)) = &state.primary_selection_source { + if ps_source != source { + return; + } + let fd = unsafe { FileDescriptor::from_raw_fd(fd.into_raw_fd()) }; + write_selection_to_pipe(fd, data); + } } - write_selection_to_pipe(write_pipe, data); + PrimarySelectionSourceEvent::Cancelled => { + state.primary_selection_source.take(); + source.destroy(); + } + _ => unreachable!(), } } +} - fn cancelled( - &mut self, - _conn: &WConnection, - _qh: &QueueHandle, - source: &ZwpPrimarySelectionSourceV1, +impl Dispatch + for PrimarySelectionManagerState +{ + fn event( + state: &mut WaylandState, + _proxy: &ZwpPrimarySelectionOfferV1, + event: ::Event, + _data: &PrimarySelectionManagerData, + _conn: &wayland_client::Connection, + _qhandle: &wayland_client::QueueHandle, ) { - self.primary_selection_source.take(); - source.destroy(); + match event { + PrimarySelectionOfferEvent::Offer { mime_type } => { + if mime_type == TEXT_MIME_TYPE { + let mgr = state.primary_selection_manager.as_ref().unwrap(); + let mut inner = mgr.inner.lock().unwrap(); + inner.valid_mime = true; + } + } + _ => unreachable!(), + } } } -impl PrimarySelectionDeviceHandler for WaylandState { - fn selection( - &mut self, - _conn: &WConnection, - _qh: &QueueHandle, +impl Dispatch + for PrimarySelectionManagerState +{ + event_created_child!(WaylandState, ZwpPrimarySelectionDeviceV1, [ + zwp_primary_selection_device_v1::EVT_DATA_OFFER_OPCODE => (ZwpPrimarySelectionOfferV1, PrimarySelectionManagerData::default()) + ]); + + fn event( + state: &mut WaylandState, _primary_selection_device: &ZwpPrimarySelectionDeviceV1, + event: ::Event, + _data: &PrimarySelectionManagerData, + _conn: &wayland_client::Connection, + _qhandle: &wayland_client::QueueHandle, ) { + let psm = state.primary_selection_manager.as_ref().unwrap(); + let mut inner = psm.inner.lock().unwrap(); + match event { + PrimarySelectionDeviceEvent::DataOffer { offer } => { + inner.pending_offer = Some(offer); + } + PrimarySelectionDeviceEvent::Selection { id } => { + if !inner.valid_mime { + return; + } + + if let Some(offer) = inner.offer.take() { + offer.destroy(); + } + if id == inner.pending_offer { + inner.offer = inner.pending_offer.take(); + } else { + // Remove the pending offer, assign the new delivered one. + if let Some(offer) = inner.pending_offer.take() { + offer.destroy() + } + + inner.offer = id; + } + } + _ => unreachable!(), + } } } diff --git a/window/src/os/wayland/data_device.rs b/window/src/os/wayland/data_device.rs index 3ccb1fae1..d14857df1 100644 --- a/window/src/os/wayland/data_device.rs +++ b/window/src/os/wayland/data_device.rs @@ -1,8 +1,12 @@ -use smithay_client_toolkit::data_device_manager::data_device::DataDeviceHandler; +use std::os::fd::{FromRawFd, IntoRawFd}; + +use filedescriptor::FileDescriptor; +use smithay_client_toolkit::data_device_manager::data_device::{ + DataDevice, DataDeviceDataExt, DataDeviceHandler, +}; use smithay_client_toolkit::data_device_manager::data_offer::DataOfferHandler; use smithay_client_toolkit::data_device_manager::data_source::DataSourceHandler; use smithay_client_toolkit::data_device_manager::WritePipe; -use wayland_client::protocol::wl_data_device::WlDataDevice; use wayland_client::protocol::wl_data_device_manager::DndAction; use wayland_client::Proxy; @@ -22,25 +26,19 @@ impl DataDeviceHandler for WaylandState { &mut self, _conn: &wayland_client::Connection, _qh: &wayland_client::QueueHandle, - data_device: &WlDataDevice, + data_device: DataDevice, ) { - let data = match self.data_device { - Some(ref dv) if dv.inner() == data_device => dv.data(), - _ => { - log::warn!("No existing device manager for {:?}", data_device); - return; - } - }; - - let drag_offer = data.drag_offer().unwrap(); + let mut drag_offer = data_device.drag_offer().unwrap(); log::trace!( "Data offer entered: {:?}, mime_types: {:?}", drag_offer, - drag_offer.with_mime_types(|m| m.iter().cloned().collect::>()) + data_device.drag_mime_types() ); - if let Some(m) = - drag_offer.with_mime_types(|m| m.iter().find(|s| *s == URI_MIME_TYPE).cloned()) + if let Some(m) = data_device + .drag_mime_types() + .iter() + .find(|s| *s == URI_MIME_TYPE) { drag_offer.accept_mime_type(*self.last_serial.borrow(), Some(m.clone())); } @@ -56,8 +54,8 @@ impl DataDeviceHandler for WaylandState { .lock() .unwrap(); - let offer = drag_offer; - let window_id = SurfaceUserData::from_wl(&offer.surface).window_id; + let offer = drag_offer.inner().clone(); + let window_id = SurfaceUserData::from_wl(&drag_offer.surface).window_id; pstate.drag_and_drop.offer = Some(SurfaceAndOffer { window_id, offer }); } @@ -66,7 +64,7 @@ impl DataDeviceHandler for WaylandState { &mut self, _conn: &wayland_client::Connection, _qh: &wayland_client::QueueHandle, - _data_device: &WlDataDevice, + _data_device: DataDevice, ) { let pointer = self.pointer.as_mut().unwrap(); let mut pstate = pointer @@ -85,7 +83,7 @@ impl DataDeviceHandler for WaylandState { &mut self, _conn: &wayland_client::Connection, _qh: &wayland_client::QueueHandle, - _data_device: &WlDataDevice, + _data_device: DataDevice, ) { } @@ -93,21 +91,19 @@ impl DataDeviceHandler for WaylandState { &mut self, _conn: &wayland_client::Connection, _qh: &wayland_client::QueueHandle, - data_device: &WlDataDevice, + data_device: DataDevice, ) { - let selection = match self.data_device { - Some(ref dv) if dv.inner() == data_device => dv.data().selection_offer(), - _ => { - return; - } - }; + let mime_types = data_device.selection_mime_types(); + if !mime_types.iter().any(|s| s == TEXT_MIME_TYPE) { + return; + } - if let Some(offer) = selection { - if !offer.with_mime_types(|m| m.iter().any(|s| *s == TEXT_MIME_TYPE)) { - return; - } + if let Some(offer) = data_device.selection_offer() { if let Some(copy_and_paste) = self.resolve_copy_and_paste() { - copy_and_paste.lock().unwrap().confirm_selection(offer); + copy_and_paste + .lock() + .unwrap() + .confirm_selection(offer.inner().clone()); } } } @@ -116,7 +112,7 @@ impl DataDeviceHandler for WaylandState { &mut self, _conn: &wayland_client::Connection, _qh: &wayland_client::QueueHandle, - _data_device: &WlDataDevice, + _data_device: DataDevice, ) { let pointer = self.pointer.as_mut().unwrap(); let mut pstate = pointer @@ -139,6 +135,22 @@ impl DataDeviceHandler for WaylandState { } impl DataOfferHandler for WaylandState { + fn offer( + &mut self, + _conn: &wayland_client::Connection, + _qh: &wayland_client::QueueHandle, + offer: &mut smithay_client_toolkit::data_device_manager::data_offer::DataDeviceOffer, + mime_type: String, + ) { + log::trace!("Received offer with mime type: {mime_type}"); + if mime_type == TEXT_MIME_TYPE { + offer.accept_mime_type(*self.last_serial.borrow(), Some(mime_type)); + } else { + // Refuse other mime types + offer.accept_mime_type(*self.last_serial.borrow(), None); + } + } + // Ignore drag and drop events fn source_actions( &mut self, @@ -186,6 +198,7 @@ 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 5de067656..59dbfbe40 100644 --- a/window/src/os/wayland/drag_and_drop.rs +++ b/window/src/os/wayland/drag_and_drop.rs @@ -1,9 +1,10 @@ use crate::wayland::read_pipe_with_timeout; use crate::ConnectionOps; +use filedescriptor::{FileDescriptor, Pipe}; use smithay_client_toolkit as toolkit; +use std::os::unix::io::AsRawFd; use std::path::PathBuf; -use toolkit::data_device_manager::data_offer::DragOffer; -use toolkit::data_device_manager::ReadPipe; +use toolkit::reexports::client::protocol::wl_data_offer::WlDataOffer; use url::Url; use super::data_device::URI_MIME_TYPE; @@ -16,12 +17,12 @@ pub struct DragAndDrop { pub(super) struct SurfaceAndOffer { pub(super) window_id: usize, - pub(super) offer: DragOffer, + pub(super) offer: WlDataOffer, } pub(super) struct SurfaceAndPipe { pub(super) window_id: usize, - pub(super) read: ReadPipe, + pub(super) read: FileDescriptor, } impl DragAndDrop { @@ -29,15 +30,16 @@ 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 read = offer - .receive(URI_MIME_TYPE.to_string()) - .map_err(|err| log::error!("Unable to receive data: {:#}", err)) + let pipe = Pipe::new() + .map_err(|err| log::error!("Unable to create pipe: {:#}", err)) .ok()?; + offer.receive(URI_MIME_TYPE.to_string(), pipe.write.as_raw_fd()); + let read = pipe.read; offer.finish(); Some(SurfaceAndPipe { window_id, read }) } - pub(super) fn read_paths_from_pipe(read: ReadPipe) -> Option> { + pub(super) fn read_paths_from_pipe(read: FileDescriptor) -> 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/frame.rs b/window/src/os/wayland/frame.rs index 78bd1e0db..614f24d17 100644 --- a/window/src/os/wayland/frame.rs +++ b/window/src/os/wayland/frame.rs @@ -2,8 +2,6 @@ //! in smithay_client_toolkit 0.11 which is Copyright (c) 2018 Victor Berger //! and provided under the terms of the MIT license. -// TODO: update this for SCTK 0.18 and use this instead of the FallbackFrame - use crate::os::wayland::pointer::make_theme_manager; use config::{ConfigHandle, RgbaColor, WindowFrameConfig}; use smithay_client_toolkit::output::{add_output_listener, with_output_info, OutputListener}; diff --git a/window/src/os/wayland/pointer.rs b/window/src/os/wayland/pointer.rs index 80c7781be..faefdce89 100644 --- a/window/src/os/wayland/pointer.rs +++ b/window/src/os/wayland/pointer.rs @@ -1,16 +1,15 @@ use std::cell::RefCell; use std::sync::{Arc, Mutex}; -use std::time::Duration; use smithay_client_toolkit::compositor::SurfaceData; use smithay_client_toolkit::seat::pointer::{ PointerData, PointerDataExt, PointerEvent, PointerEventKind, PointerHandler, }; +use smithay_client_toolkit::shell::xdg::frame::{DecorationsFrame, FrameClick}; use wayland_client::backend::ObjectId; use wayland_client::protocol::wl_pointer::{ButtonState, WlPointer}; use wayland_client::protocol::wl_seat::WlSeat; use wayland_client::{Connection, Proxy, QueueHandle}; -use wayland_csd_frame::{DecorationsFrame, FrameClick}; use wezterm_input_types::MousePress; use crate::wayland::SurfaceUserData; @@ -219,52 +218,33 @@ impl WaylandState { let wid = SurfaceUserData::from_wl(parent_surface).window_id; let mut inner = windows.get(&wid).unwrap().borrow_mut(); - if let Some(frame) = inner.decorations.as_mut() { - match evt.kind { - PointerEventKind::Enter { .. } => { - frame.click_point_moved(Duration::ZERO, &evt.surface.id(), x, y); - } - PointerEventKind::Leave { .. } => { - frame.click_point_left(); - } - PointerEventKind::Motion { time, .. } => { - frame.click_point_moved( - Duration::from_millis(time as u64), - &evt.surface.id(), - x, - y, - ); - } - PointerEventKind::Press { - button, - serial, - time, - .. - } - | PointerEventKind::Release { - button, - serial, - time, - .. - } => { - let pressed = if matches!(evt.kind, PointerEventKind::Press { .. }) { - true - } else { - false - }; - let click = match button { - 0x110 => FrameClick::Normal, - 0x111 => FrameClick::Alternate, - _ => continue, - }; - if let Some(action) = - frame.on_click(Duration::from_millis(time as u64), click, pressed) - { - inner.frame_action(pointer, serial, action); - } - } - _ => {} + match evt.kind { + PointerEventKind::Enter { .. } => { + inner.window_frame.click_point_moved(&evt.surface, x, y); } + PointerEventKind::Leave { .. } => { + inner.window_frame.click_point_left(); + } + PointerEventKind::Motion { .. } => { + inner.window_frame.click_point_moved(&evt.surface, x, y); + } + PointerEventKind::Press { button, serial, .. } + | PointerEventKind::Release { button, serial, .. } => { + let pressed = if matches!(evt.kind, PointerEventKind::Press { .. }) { + true + } else { + false + }; + let click = match button { + 0x110 => FrameClick::Normal, + 0x111 => FrameClick::Alternate, + _ => continue, + }; + if let Some(action) = inner.window_frame.on_click(click, pressed) { + inner.frame_action(pointer, serial, action); + } + } + _ => {} } } } diff --git a/window/src/os/wayland/seat.rs b/window/src/os/wayland/seat.rs index d01865564..3798f4259 100644 --- a/window/src/os/wayland/seat.rs +++ b/window/src/os/wayland/seat.rs @@ -1,9 +1,9 @@ -use smithay_client_toolkit::compositor::SurfaceData; use smithay_client_toolkit::seat::pointer::ThemeSpec; 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; @@ -14,7 +14,9 @@ impl SeatHandler for WaylandState { &mut self.seat } - fn new_seat(&mut self, _conn: &Connection, _qh: &QueueHandle, _seat: WlSeat) {} + fn new_seat(&mut self, _conn: &Connection, _qh: &QueueHandle, _seat: WlSeat) { + todo!() + } fn new_capability( &mut self, @@ -24,10 +26,7 @@ impl SeatHandler for WaylandState { capability: smithay_client_toolkit::seat::Capability, ) { if capability == Capability::Keyboard && self.keyboard.is_none() { - log::trace!( - "Setting keyboard - capability" - ); + log::trace!("Setting keyboard capability"); let keyboard = seat.get_keyboard(qh, KeyboardData {}); self.keyboard = Some(keyboard.clone()); @@ -37,18 +36,12 @@ impl SeatHandler for WaylandState { } if capability == Capability::Pointer && self.pointer.is_none() { - log::trace!( - "Setting - pointer capability" - ); - let surface = self.compositor.create_surface(qh); + log::trace!("Setting pointer capability"); let pointer = self .seat - .get_pointer_with_theme_and_data::( + .get_pointer_with_theme_and_data( qh, &seat, - self.shm.wl_shm(), - surface, ThemeSpec::System, PointerUserData::new(seat.clone()), ) @@ -59,11 +52,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.get_selection_device(qh, &seat)); - self.primary_selection_device = primary_select_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; } } @@ -74,9 +67,10 @@ impl SeatHandler for WaylandState { _seat: WlSeat, _capability: smithay_client_toolkit::seat::Capability, ) { - // we need to clean up the keyboard and pointer resources we created earlier todo!() } - fn remove_seat(&mut self, _conn: &Connection, _qh: &QueueHandle, _seat: WlSeat) {} + fn remove_seat(&mut self, _conn: &Connection, _qh: &QueueHandle, _seat: WlSeat) { + todo!() + } } diff --git a/window/src/os/wayland/state.rs b/window/src/os/wayland/state.rs index b59cc4516..c410e6817 100644 --- a/window/src/os/wayland/state.rs +++ b/window/src/os/wayland/state.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use smithay_client_toolkit::compositor::{CompositorState, SurfaceData}; +use smithay_client_toolkit::compositor::CompositorState; use smithay_client_toolkit::data_device_manager::data_device::DataDevice; use smithay_client_toolkit::data_device_manager::data_source::CopyPasteSource; use smithay_client_toolkit::data_device_manager::DataDeviceManagerState; @@ -13,9 +13,6 @@ use smithay_client_toolkit::reexports::protocols_wlr::output_management::v1::cli 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; use smithay_client_toolkit::registry::{ProvidesRegistryState, RegistryState}; -use smithay_client_toolkit::primary_selection::PrimarySelectionManagerState; -use smithay_client_toolkit::primary_selection::selection::PrimarySelectionSource; -use smithay_client_toolkit::primary_selection::device::PrimarySelectionDevice; use smithay_client_toolkit::seat::pointer::ThemedPointer; use smithay_client_toolkit::seat::SeatState; use smithay_client_toolkit::shell::xdg::XdgShell; @@ -23,18 +20,25 @@ 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_pointer, delegate_data_device, delegate_primary_selection, delegate_output, delegate_registry, delegate_seat, delegate_shm, delegate_subcompositor, delegate_xdg_shell, delegate_xdg_window, registry_handlers + delegate_compositor, delegate_data_device, delegate_data_device_manager, delegate_data_offer, delegate_data_source, delegate_output, 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::protocol::wl_pointer::WlPointer; +use wayland_client::protocol::wl_surface::WlSurface; 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}; @@ -67,8 +71,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_selection_source: Option<(PrimarySelectionSource, String)>, - pub(super) primary_selection_device: Option, + pub(super) primary_select_device: Option, + pub(super) primary_selection_source: Option<(ZwpPrimarySelectionSourceV1, String)>, pub(super) shm: Shm, pub(super) mem_pool: RefCell, } @@ -109,8 +113,8 @@ impl WaylandState { data_device: None, copy_paste_source: None, primary_selection_manager: PrimarySelectionManagerState::bind(globals, qh).ok(), + primary_select_device: None, primary_selection_source: None, - primary_selection_device: None, shm, mem_pool: RefCell::new(mem_pool), }; @@ -149,20 +153,28 @@ impl OutputHandler for WaylandState { log::trace!("output destroyed: OutputHandler"); } } +// Undocumented in sctk 0.17: This is required to use have user data with a surface +// Will be just delegate_compositor!(WaylandState, surface: [SurfaceData, SurfaceUserData]) in 0.18 +delegate_dispatch!(WaylandState: [ WlSurface: SurfaceUserData] => CompositorState); delegate_registry!(WaylandState); delegate_shm!(WaylandState); delegate_output!(WaylandState); -delegate_compositor!(WaylandState, surface: [SurfaceData, SurfaceUserData]); +delegate_compositor!(WaylandState); delegate_subcompositor!(WaylandState); delegate_seat!(WaylandState); +delegate_data_device_manager!(WaylandState); delegate_data_device!(WaylandState); -delegate_primary_selection!(WaylandState); -delegate_pointer!(WaylandState, pointer: [PointerUserData]); +delegate_data_source!(WaylandState); +delegate_data_offer!(WaylandState); + +// Updating to 0.18 should have this be able to work +// delegate_pointer!(WaylandState, pointer: [PointerUserData]); +delegate_dispatch!(WaylandState: [WlPointer: PointerUserData] => SeatState); delegate_xdg_shell!(WaylandState); delegate_xdg_window!(WaylandState); @@ -173,3 +185,8 @@ delegate_dispatch!(WaylandState: [ZwpTextInputV3: TextInputData] => TextInputSta 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 db1f7eef2..e6cd1d4f6 100644 --- a/window/src/os/wayland/window.rs +++ b/window/src/os/wayland/window.rs @@ -1,5 +1,5 @@ use std::any::Any; -use std::cell::RefCell; +use std::cell::{RefCell, RefMut}; use std::cmp::max; use std::convert::TryInto; use std::io::Read; @@ -14,18 +14,18 @@ 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::seat::pointer::CursorIcon; -use smithay_client_toolkit::shell::xdg::fallback_frame::FallbackFrame; +use smithay_client_toolkit::shell::xdg::frame::fallback_frame::FallbackFrame; +use smithay_client_toolkit::shell::xdg::frame::{DecorationsFrame, FrameAction}; use smithay_client_toolkit::shell::xdg::window::{ DecorationMode, Window as XdgWindow, WindowConfigure, WindowDecorations as Decorations, - WindowHandler, + WindowHandler, WindowState as SCTKWindowState, }; use smithay_client_toolkit::shell::xdg::XdgSurface; use smithay_client_toolkit::shell::WaylandSurface; @@ -34,9 +34,7 @@ use wayland_client::protocol::wl_keyboard::{Event as WlKeyboardEvent, KeyState}; use wayland_client::protocol::wl_pointer::{ButtonState, WlPointer}; use wayland_client::protocol::wl_surface::WlSurface; use wayland_client::{Connection as WConnection, Proxy}; -use wayland_csd_frame::{DecorationsFrame, FrameAction, WindowState as CsdWindowState}; use wayland_egl::{is_available as egl_is_available, WlEglSurface}; -use wayland_protocols::xdg::shell::client::xdg_toplevel; use wezterm_font::FontConfiguration; use wezterm_input_types::{ KeyboardLedStatus, Modifiers, MouseButtons, MouseEvent, MouseEventKind, MousePress, @@ -195,6 +193,11 @@ impl WaylandWindow { compositor.create_surface_with_data(&qh, surface_data) }; + let pointer_surface = { + let compositor = &conn.wayland_state.borrow().compositor; + compositor.create_surface(&qh) + }; + let ResolvedGeometry { x: _, y: _, @@ -208,33 +211,54 @@ impl WaylandWindow { dpi: config.dpi.unwrap_or(crate::DEFAULT_DPI) as usize, }; - let decorations = config.window_decorations; - let decorations_mode = if decorations == WindowDecorations::NONE { - Decorations::None - } else if decorations == WindowDecorations::default() { - Decorations::ServerDefault - } else { - // SCTK/Wayland don't allow more nuance than "decorations are hidden", - // so if we have a mixture of things, then we need to force our - // client side decoration rendering. - Decorations::RequestClient - }; - let window = { let xdg_shell = &conn.wayland_state.borrow().xdg; - xdg_shell.create_window(surface.clone(), decorations_mode, &qh) + xdg_shell.create_window(surface.clone(), Decorations::RequestServer, &qh) }; window.set_app_id(class_name.to_string()); window.set_title(name.to_string()); + let decorations = config.window_decorations; + + let decor_mode = if decorations == WindowDecorations::NONE { + None + } else if decorations == WindowDecorations::default() { + Some(DecorationMode::Server) + } else { + Some(DecorationMode::Client) + }; + window.request_decoration_mode(decor_mode); + + let mut window_frame = { + let wayland_state = &conn.wayland_state.borrow(); + let shm = &wayland_state.shm; + let subcompositor = wayland_state.subcompositor.clone(); + FallbackFrame::new(&window, shm, subcompositor, qh.clone()) + .expect("failed to create csd frame") + }; + let hidden = match decor_mode { + Some(DecorationMode::Client) => false, + _ => true, + }; + window_frame.set_hidden(hidden); + if !hidden { + window_frame.resize( + NonZeroU32::new(dimensions.pixel_width as u32) + .ok_or_else(|| anyhow!("dimensions {dimensions:?} are invalid"))?, + NonZeroU32::new(dimensions.pixel_height as u32) + .ok_or_else(|| anyhow!("dimensions {dimensions:?} are invalid"))?, + ); + } window.set_min_size(Some((32, 32))); - window.set_window_geometry( - 0, - 0, + let (w, h) = window_frame.add_borders( dimensions.pixel_width as u32, dimensions.pixel_height as u32, ); + let (x, y) = window_frame.location(); + window + .xdg_surface() + .set_window_geometry(x, y, w as i32, h as i32); window.commit(); let copy_and_paste = CopyAndPaste::create(); @@ -251,7 +275,7 @@ impl WaylandWindow { copy_and_paste, invalidated: false, window: Some(window), - decorations: None, + window_frame, dimensions, resize_increments: None, window_state: WindowState::default(), @@ -266,6 +290,7 @@ impl WaylandWindow { key_repeat: None, pending_event, pending_mouse, + pointer_surface, pending_first_configure: Some(pending_first_configure), frame_callback: None, @@ -431,6 +456,7 @@ impl WindowOps for WaylandWindow { pub(crate) struct PendingEvent { pub(crate) close: bool, pub(crate) had_configure_event: bool, + refresh_decorations: bool, // XXX: configure and window_configure could probably be combined, but right now configure only // queues a new size, so it can be out of sync. Example would be maximizing and minimizing winodw pub(crate) configure: Option<(u32, u32)>, @@ -439,10 +465,10 @@ pub(crate) struct PendingEvent { pub(crate) window_state: Option, } -pub(crate) fn read_pipe_with_timeout(mut file: ReadPipe) -> anyhow::Result { +pub(crate) fn read_pipe_with_timeout(mut file: FileDescriptor) -> anyhow::Result { let mut result = Vec::new(); - unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK) }; + file.set_non_blocking(true)?; let mut pfd = libc::pollfd { fd: file.as_raw_fd(), events: libc::POLLIN, @@ -475,10 +501,11 @@ pub struct WaylandWindowInner { surface_factor: f64, copy_and_paste: Arc>, window: Option, - pub(super) decorations: Option>, + pub(super) window_frame: FallbackFrame, dimensions: Dimensions, resize_increments: Option, window_state: WindowState, + pointer_surface: WlSurface, last_mouse_coords: Point, mouse_buttons: MouseButtons, hscroll_remainder: f64, @@ -509,7 +536,6 @@ impl WaylandWindowInner { fn close(&mut self) { self.events.dispatch(WindowEvent::Destroyed); self.window.take(); - self.decorations.take(); } fn show(&mut self) { @@ -522,10 +548,11 @@ impl WaylandWindowInner { } fn refresh_frame(&mut self) { - if let Some(frame) = self.decorations.as_mut() { - if frame.is_dirty() && !frame.is_hidden() { - frame.draw(); + if let Some(window) = self.window.as_mut() { + if self.window_frame.is_dirty() && !self.window_frame.is_hidden() { + self.window_frame.draw(); } + window.wl_surface().commit(); } } @@ -708,7 +735,7 @@ impl WaylandWindowInner { } } - fn dispatch_pending_event(&mut self) { + pub(crate) fn dispatch_pending_event(&mut self) { let mut pending; { let mut pending_events = self.pending_event.lock().unwrap(); @@ -740,49 +767,11 @@ impl WaylandWindowInner { } } - let conn = Connection::get().unwrap().wayland(); - - if let Some(window_config) = pending.window_configure { - if self.window.is_some() - && window_config.decoration_mode == DecorationMode::Client - && self.config.window_decorations != WindowDecorations::NONE - { - log::trace!("Client side decoration"); - // the server requested client side decoration - // create a frame, if we don't have one already - let title = self.title.as_ref(); - let window = self.window.as_ref(); - let decorations = self.decorations.get_or_insert_with(|| { - let state = conn.wayland_state.borrow(); - let qh = WaylandConnection::get() - .unwrap() - .wayland() - .event_queue - .borrow() - .handle() - .clone(); - let mut frame = FallbackFrame::new( - window.unwrap(), - &state.shm, - state.subcompositor.clone(), - qh, - ) - .expect("failed to create csd frame."); - if let Some(title) = title { - frame.set_title(title.clone()); - } - frame.into() - }); - decorations.set_hidden(false); - decorations.update_state(window_config.state); - decorations.update_wm_capabilities(window_config.capabilities); - } else { - if let Some(frame) = self.decorations.as_mut() { - // If we have a frame already, hide it. - frame.set_hidden(true); - } - } - }; + if let Some(ref window_config) = pending.window_configure { + self.window_frame.update_state(window_config.state); + self.window_frame + .update_wm_capabilities(window_config.capabilities); + } if let Some((mut w, mut h)) = pending.configure.take() { log::trace!("Pending configure: w:{w}, h{h} -- {:?}", self.window); @@ -797,29 +786,11 @@ impl WaylandWindowInner { // Do this early because this affects surface_to_pixels/pixels_to_surface self.dimensions.dpi = dpi; - // we need to subtract the decorations before trying to resize - const MIN_PIXELS: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(1) }; - if let Some(ref dec) = self.decorations { - if !dec.is_hidden() { - let inner_size = dec.subtract_borders( - NonZeroU32::new(w).unwrap(), - NonZeroU32::new(h).unwrap(), - ); - // Clamp the size to at least one pixel. - let inner_width = inner_size.0.unwrap_or(MIN_PIXELS); - let inner_height = inner_size.1.unwrap_or(MIN_PIXELS); - w = inner_width.get(); - h = inner_height.get(); - } - } - let mut pixel_width = self.surface_to_pixels(w.try_into().unwrap()); let mut pixel_height = self.surface_to_pixels(h.try_into().unwrap()); if self.window_state.can_resize() { - if let Some(ref mut dec) = self.decorations { - dec.set_resizable(true); - } + self.window_frame.set_resizable(true); if let Some(incr) = self.resize_increments { let min_width = incr.base_width + incr.x; let min_height = incr.base_height + incr.y; @@ -830,33 +801,29 @@ impl WaylandWindowInner { max(pixel_height - extra_height, min_height as i32); w = self.pixels_to_surface(desired_pixel_width) as u32; h = self.pixels_to_surface(desired_pixel_height) as u32; + pixel_width = self.surface_to_pixels(w.try_into().unwrap()); + pixel_height = self.surface_to_pixels(h.try_into().unwrap()); } } - let window = self.window.as_ref().unwrap(); - - match self.decorations.as_mut() { - Some(frame) if !frame.is_hidden() => { - log::trace!("Resizing frame"); - frame.resize(w.try_into().unwrap(), h.try_into().unwrap()); - let outer_size = frame.add_borders(w, h); - let (x, y) = frame.location(); - window.set_window_geometry( - x.try_into().unwrap(), - y.try_into().unwrap(), - outer_size.0, - outer_size.1, - ); - } - _ => { - window.set_window_geometry(0, 0, w, h); - } + log::trace!("Resizing frame"); + let (width, height) = self.window_frame.subtract_borders( + NonZeroU32::new(pixel_width as u32).unwrap(), + NonZeroU32::new(pixel_height as u32).unwrap(), + ); + // Clamp the size to at least one pixel. + let width = width.unwrap_or(NonZeroU32::new(1).unwrap()); + let height = height.unwrap_or(NonZeroU32::new(1).unwrap()); + if !self.window_frame.is_hidden() { + self.window_frame.resize(width, height); } - // recompute the pixel dimensions because they may have changed - // due to resizing or decorations - pixel_width = self.surface_to_pixels(w.try_into().unwrap()); - pixel_height = self.surface_to_pixels(h.try_into().unwrap()); - + let (x, y) = self.window_frame.location(); + let outer_size = self.window_frame.add_borders(width.get(), height.get()); + self.window + .as_mut() + .unwrap() + .xdg_surface() + .set_window_geometry(x, y, outer_size.0 as i32, outer_size.1 as i32); // Compute the new pixel dimensions let new_dimensions = Dimensions { pixel_width: pixel_width.try_into().unwrap(), @@ -892,7 +859,8 @@ impl WaylandWindowInner { wegl_surface.resize(pixel_width, pixel_height, 0, 0); } if self.surface_factor != factor { - let wayland_state = conn.wayland_state.borrow(); + let wayland_conn = Connection::get().unwrap().wayland(); + let wayland_state = wayland_conn.wayland_state.borrow(); let mut pool = wayland_state.mem_pool.borrow_mut(); // Make a "fake" buffer with the right dimensions, as @@ -914,6 +882,9 @@ impl WaylandWindowInner { self.do_paint().unwrap(); } } + if pending.refresh_decorations && self.window.is_some() { + self.refresh_frame(); + } if pending.had_configure_event && self.window.is_some() { log::debug!("Had configured an event"); if let Some(notify) = self.pending_first_configure.take() { @@ -924,20 +895,27 @@ impl WaylandWindowInner { } fn set_cursor(&mut self, cursor: Option) { - let icon = cursor.map_or(CursorIcon::Default, |cursor| match cursor { - MouseCursor::Arrow => CursorIcon::Default, - MouseCursor::Hand => CursorIcon::Pointer, - MouseCursor::SizeUpDown => CursorIcon::NsResize, - MouseCursor::SizeLeftRight => CursorIcon::EwResize, - MouseCursor::Text => CursorIcon::Text, + let name = cursor.map_or("none", |cursor| match cursor { + MouseCursor::Arrow => "arrow", + MouseCursor::Hand => "hand", + MouseCursor::SizeUpDown => "ns-resize", + MouseCursor::SizeLeftRight => "ew-resize", + MouseCursor::Text => "xterm", }); let conn = Connection::get().unwrap().wayland(); - let mut state = conn.wayland_state.borrow_mut(); - let pointer = state.pointer.as_mut().unwrap(); + let state = conn.wayland_state.borrow_mut(); + let (shm, pointer) = + RefMut::map_split(state, |s| (&mut s.shm, s.pointer.as_mut().unwrap())); // Much different API in 0.18 - if let Err(err) = pointer.set_cursor(&conn.connection, icon) { - log::error!("set_cursor: (icon={}) {}", icon, err); + if let Err(err) = pointer.set_cursor( + &conn.connection, + name, + shm.wl_shm(), + &self.pointer_surface, + 1, + ) { + log::error!("set_cursor: {}", err); } } @@ -989,9 +967,6 @@ impl WaylandWindowInner { if let Some(window) = self.window.as_ref() { window.set_title(title.clone()); } - if let Some(frame) = self.decorations.as_mut() { - frame.set_title(title.clone()); - } self.refresh_frame(); self.title = Some(title); } @@ -1171,14 +1146,8 @@ impl WaylandWindowInner { .unwrap() .show_window_menu(seat, serial, (x, y)) } - FrameAction::Resize(edge) => { - self.window - .as_ref() - .unwrap() - .resize(seat, serial, frame_edge_to_window_edge(edge)) - } + FrameAction::Resize(edge) => self.window.as_ref().unwrap().resize(seat, serial, edge), FrameAction::Move => self.window.as_ref().unwrap().move_(seat, serial), - _ => {} // just ignore unrecognized frame actions } } } @@ -1188,7 +1157,7 @@ impl WaylandState { self.windows.borrow().get(&window_id).map(Rc::clone) } - fn handle_window_event(&mut self, window: &XdgWindow, event: WaylandWindowEvent) { + fn handle_window_event(&self, window: &XdgWindow, event: WaylandWindowEvent) { let surface_data = SurfaceUserData::from_wl(window.wl_surface()); let window_id = surface_data.window_id; @@ -1211,12 +1180,12 @@ impl WaylandState { } } WaylandWindowEvent::Request(configure) => { + pending_event.window_configure.replace(configure.clone()); // TODO: This should the new queue function // p.queue_configure(&configure) // let mut changed; pending_event.had_configure_event = true; - if let (Some(w), Some(h)) = configure.new_size { changed = pending_event.configure.is_none(); pending_event.configure.replace((w.get(), h.get())); @@ -1225,14 +1194,14 @@ impl WaylandState { } let mut state = WindowState::default(); - if configure.state.contains(CsdWindowState::FULLSCREEN) { + if configure.state.contains(SCTKWindowState::FULLSCREEN) { state |= WindowState::FULL_SCREEN; } - let fs_bits = CsdWindowState::MAXIMIZED - | CsdWindowState::TILED_LEFT - | CsdWindowState::TILED_RIGHT - | CsdWindowState::TILED_TOP - | CsdWindowState::TILED_BOTTOM; + let fs_bits = SCTKWindowState::MAXIMIZED + | SCTKWindowState::TILED_LEFT + | SCTKWindowState::TILED_RIGHT + | SCTKWindowState::TILED_TOP + | SCTKWindowState::TILED_BOTTOM; if !((configure.state & fs_bits).is_empty()) { state |= WindowState::MAXIMIZED; } @@ -1247,12 +1216,8 @@ impl WaylandState { if pending_event.window_state.is_none() && state != WindowState::default() { changed = true; } - if pending_event.window_configure.is_none() { - changed = true; - } pending_event.window_state.replace(state); - pending_event.window_configure.replace(configure); changed } }; @@ -1267,30 +1232,13 @@ impl WaylandState { impl CompositorHandler for WaylandState { fn scale_factor_changed( - &mut self, - _conn: &WConnection, - _qh: &wayland_client::QueueHandle, - surface: &wayland_client::protocol::wl_surface::WlSurface, - new_factor: i32, - ) { - let window_id = SurfaceUserData::from_wl(surface).window_id; - WaylandConnection::with_window_inner(window_id, move |inner| { - if let Some(frame) = inner.decorations.as_mut() { - frame.set_scaling_factor(new_factor as f64); - } - Ok(()) - }); - // We do nothing, we get the scale_factor from surface_data - } - - fn transform_changed( &mut self, _conn: &WConnection, _qh: &wayland_client::QueueHandle, _surface: &wayland_client::protocol::wl_surface::WlSurface, - _new_transform: wayland_client::protocol::wl_output::Transform, + _new_factor: i32, ) { - // Nothing to do here + // We do nothing, we get the scale_factor from surface_data } fn frame( @@ -1358,7 +1306,7 @@ unsafe impl HasRawWindowHandle for WaylandWindowInner { fn raw_window_handle(&self) -> RawWindowHandle { let mut handle = WaylandWindowHandle::empty(); let surface = self.surface(); - handle.surface = surface.id().interface().c_ptr.unwrap() as *const _ as *mut _; + handle.surface = surface.id().as_ptr() as *mut _; RawWindowHandle::Wayland(handle) } } @@ -1367,13 +1315,7 @@ unsafe impl HasRawDisplayHandle for WaylandWindow { fn raw_display_handle(&self) -> RawDisplayHandle { let mut handle = WaylandDisplayHandle::empty(); let conn = WaylandConnection::get().unwrap().wayland(); - handle.display = conn - .connection - .backend() - .display_id() - .interface() - .c_ptr - .unwrap() as *const _ as *mut _; + handle.display = conn.connection.backend().display_ptr() as *mut _; RawDisplayHandle::Wayland(handle) } } @@ -1390,22 +1332,3 @@ unsafe impl HasRawWindowHandle for WaylandWindow { inner.raw_window_handle() } } - -fn frame_edge_to_window_edge( - frame_edge: wayland_csd_frame::ResizeEdge, -) -> xdg_toplevel::ResizeEdge { - use wayland_csd_frame::ResizeEdge; - use xdg_toplevel::ResizeEdge as XdgResizeEdge; - match frame_edge { - ResizeEdge::None => XdgResizeEdge::None, - ResizeEdge::Top => XdgResizeEdge::Top, - ResizeEdge::Bottom => XdgResizeEdge::Bottom, - ResizeEdge::Left => XdgResizeEdge::Left, - ResizeEdge::TopLeft => XdgResizeEdge::TopLeft, - ResizeEdge::BottomLeft => XdgResizeEdge::BottomLeft, - ResizeEdge::Right => XdgResizeEdge::Right, - ResizeEdge::TopRight => XdgResizeEdge::TopRight, - ResizeEdge::BottomRight => XdgResizeEdge::BottomRight, - _ => XdgResizeEdge::None, - } -}