diff --git a/Cargo.lock b/Cargo.lock index f781b18e5f3..292b6e6a14e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1274,6 +1274,12 @@ 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" @@ -3214,18 +3220,18 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.5.10" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" dependencies = [ "libc", ] [[package]] name = "memmap2" -version = "0.8.0" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", ] @@ -4291,15 +4297,6 @@ 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" @@ -5046,23 +5043,25 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "smithay-client-toolkit" -version = "0.17.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1476c3d89bb67079264b88aaf4f14358353318397e083b7c4e8c14517f55de7" +checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" dependencies = [ - "bitflags 1.3.2", - "dlib", - "lazy_static", + "bitflags 2.5.0", + "cursor-icon", + "libc", "log", - "memmap2 0.5.10", - "nix 0.26.4", + "memmap2 0.9.4", + "rustix 0.38.32", "thiserror", "wayland-backend", "wayland-client", + "wayland-csd-frame", "wayland-cursor", "wayland-protocols", "wayland-protocols-wlr", "wayland-scanner", + "xkeysym", ] [[package]] @@ -6122,14 +6121,13 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wayland-backend" -version = "0.1.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b48e27457e8da3b2260ac60d0a94512f5cba36448679f3747c0865b7893ed8" +checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" dependencies = [ "cc", "downcast-rs", - "io-lifetimes", - "nix 0.26.4", + "rustix 0.38.32", "scoped-tls", "smallvec", "wayland-sys", @@ -6137,32 +6135,43 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.30.2" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489c9654770f674fc7e266b3c579f4053d7551df0ceb392f153adb1f9ed06ac8" +checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" dependencies = [ - "bitflags 1.3.2", - "nix 0.26.4", + "bitflags 2.5.0", + "rustix 0.38.32", "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.5.0", + "cursor-icon", + "wayland-backend", +] + [[package]] name = "wayland-cursor" -version = "0.30.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0c3a0d5b4b688b07b0442362d3ed6bf04724fcc16cd69ab6285b90dbc487aa" +checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" dependencies = [ - "nix 0.26.4", + "rustix 0.38.32", "wayland-client", "xcursor", ] [[package]] name = "wayland-egl" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1187695fe81c3153c3163f9d2953149f638c5d7dbc6fe988914ca3f4961e28ed" +checksum = "355f652e5a24ae02d2ad536c8fc2d3dcc6c2bd635027cd6103a193e7d75eeda2" dependencies = [ "wayland-backend", "wayland-sys", @@ -6170,11 +6179,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.30.1" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b28101e5ca94f70461a6c2d610f76d85ad223d042dd76585ab23d3422dd9b4d" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -6182,11 +6191,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce991093320e4a6a525876e6b629ab24da25f9baef0c2e0080ad173ec89588a" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6195,20 +6204,20 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.30.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b873b257fbc32ec909c0eb80dea312076a67014e65e245f5eb69a6b8ab330e" +checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" dependencies = [ "proc-macro2", - "quick-xml 0.28.2", + "quick-xml 0.31.0", "quote", ] [[package]] name = "wayland-sys" -version = "0.30.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" dependencies = [ "dlib", "log", diff --git a/nix/flake.nix b/nix/flake.nix index 812b34f9cfe..b7a23921d93 100644 --- a/nix/flake.nix +++ b/nix/flake.nix @@ -162,8 +162,12 @@ buildInputs = buildInputs ++ (with pkgs.rust-bin; [ - stable.latest.minimal - stable.latest.clippy + (stable.latest.minimal.override { + extensions = [ + "clippy" + "rust-src" + ]; + }) nightly.latest.rustfmt nightly.latest.rust-analyzer diff --git a/window/Cargo.toml b/window/Cargo.toml index b4598297b90..2e55dbf0a47 100644 --- a/window/Cargo.toml +++ b/window/Cargo.toml @@ -81,10 +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.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} +smithay-client-toolkit = {version = "0.18", 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} [target.'cfg(target_os="macos")'.dependencies] cocoa = "0.25" diff --git a/window/build.rs b/window/build.rs index ed754edbafa..9da20d06419 100644 --- a/window/build.rs +++ b/window/build.rs @@ -8,7 +8,7 @@ fn main() { let dest = PathBuf::from(&env::var("OUT_DIR").unwrap()); let target = env::var("TARGET").unwrap(); - let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap(); + let mut file = File::create(dest.join("egl_bindings.rs")).unwrap(); let reg = Registry::new( Api::Egl, (1, 5), @@ -43,13 +43,13 @@ fn main() { } if target.contains("windows") { - let mut file = File::create(&dest.join("wgl_bindings.rs")).unwrap(); + let mut file = File::create(dest.join("wgl_bindings.rs")).unwrap(); let reg = Registry::new(Api::Wgl, (1, 0), Profile::Core, Fallbacks::All, []); reg.write_bindings(gl_generator::StructGenerator, &mut file) .unwrap(); - let mut file = File::create(&dest.join("wgl_extra_bindings.rs")).unwrap(); + let mut file = File::create(dest.join("wgl_extra_bindings.rs")).unwrap(); Registry::new( Api::Wgl, (1, 0), diff --git a/window/src/os/wayland/connection.rs b/window/src/os/wayland/connection.rs index f917bdfcadd..bd12b3668a5 100644 --- a/window/src/os/wayland/connection.rs +++ b/window/src/os/wayland/connection.rs @@ -58,7 +58,7 @@ impl WaylandConnection { let mut events = Events::with_capacity(8); let wl_fd = { - let read_guard = self.event_queue.borrow().prepare_read()?; + let read_guard = self.event_queue.borrow().prepare_read().unwrap(); read_guard.connection_fd().as_raw_fd() }; @@ -100,7 +100,7 @@ impl WaylandConnection { continue; } - if let Ok(guard) = event_q.prepare_read() { + if let Some(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 ad83e462b68..fce9b75e7ba 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, 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,37 +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(), 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(), pipe.write.as_raw_fd()); - Ok(pipe.read) + let pipe = offer.receive(TEXT_MIME_TYPE.to_string())?; + Ok(pipe) } } } @@ -78,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)); @@ -105,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); } } @@ -122,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, @@ -157,150 +154,45 @@ 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 { - 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, +impl PrimarySelectionDeviceHandler for WaylandState { + fn selection( + &mut self, _conn: &wayland_client::Connection, - _qhandle: &wayland_client::QueueHandle, + _qh: &wayland_client::QueueHandle, + _primary_selection_device: &ZwpPrimarySelectionDeviceV1, ) { - unreachable!("primary selection manager has no events"); + // TODO: do we need to do anything here? } } -impl Dispatch - for PrimarySelectionManagerState -{ - fn event( - state: &mut WaylandState, - source: &ZwpPrimarySelectionSourceV1, - event: ::Event, - _data: &PrimarySelectionManagerData, +impl PrimarySelectionSourceHandler for WaylandState { + fn send_request( + &mut self, _conn: &wayland_client::Connection, - _qhandle: &wayland_client::QueueHandle, + _qh: &wayland_client::QueueHandle, + source: &ZwpPrimarySelectionSourceV1, + mime: String, + write_pipe: toolkit::data_device_manager::WritePipe, ) { - 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); - } - } - PrimarySelectionSourceEvent::Cancelled => { - state.primary_selection_source.take(); - source.destroy(); - } - _ => unreachable!(), - } - } -} + if mime != TEXT_MIME_TYPE { + return; + }; -impl Dispatch - for PrimarySelectionManagerState -{ - fn event( - state: &mut WaylandState, - _proxy: &ZwpPrimarySelectionOfferV1, - event: ::Event, - _data: &PrimarySelectionManagerData, - _conn: &wayland_client::Connection, - _qhandle: &wayland_client::QueueHandle, - ) { - 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; - } + if let Some((ps_source, data)) = &self.primary_selection_source { + if ps_source.inner() != source { + return; } - _ => unreachable!(), + write_selection_to_pipe(write_pipe, data); } } -} -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, + fn cancelled( + &mut self, _conn: &wayland_client::Connection, - _qhandle: &wayland_client::QueueHandle, + _qh: &wayland_client::QueueHandle, + source: &ZwpPrimarySelectionSourceV1, ) { - 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!(), - } + 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 d14857df1b0..8726cb0ecad 100644 --- a/window/src/os/wayland/data_device.rs +++ b/window/src/os/wayland/data_device.rs @@ -1,12 +1,8 @@ -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_device::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 smithay_client_toolkit::reexports::client::protocol::wl_data_device::WlDataDevice; use wayland_client::protocol::wl_data_device_manager::DndAction; use wayland_client::Proxy; @@ -26,24 +22,29 @@ impl DataDeviceHandler for WaylandState { &mut self, _conn: &wayland_client::Connection, _qh: &wayland_client::QueueHandle, - data_device: DataDevice, + _data_device: &WlDataDevice, ) { - let mut drag_offer = data_device.drag_offer().unwrap(); - log::trace!( - "Data offer entered: {:?}, mime_types: {:?}", - drag_offer, - data_device.drag_mime_types() - ); - - 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())); - } + let offer = self + .data_device + .as_ref() + .unwrap() + .data() + .drag_offer() + .unwrap(); - drag_offer.set_actions(DndAction::None | DndAction::Copy, DndAction::None); + offer.with_mime_types(|mime_types| { + log::trace!( + "Data offer entered: {:?}, mime_types: {:?}", + offer, + mime_types + ); + + if let Some(mime) = mime_types.iter().find(|s| *s == URI_MIME_TYPE) { + offer.accept_mime_type(*self.last_serial.borrow(), Some(mime.clone())); + } + }); + + offer.set_actions(DndAction::None | DndAction::Copy, DndAction::None); let pointer = self.pointer.as_mut().unwrap(); let mut pstate = pointer @@ -54,8 +55,7 @@ impl DataDeviceHandler for WaylandState { .lock() .unwrap(); - let offer = drag_offer.inner().clone(); - let window_id = SurfaceUserData::from_wl(&drag_offer.surface).window_id; + let window_id = SurfaceUserData::from_wl(&offer.surface).window_id; pstate.drag_and_drop.offer = Some(SurfaceAndOffer { window_id, offer }); } @@ -64,7 +64,7 @@ impl DataDeviceHandler for WaylandState { &mut self, _conn: &wayland_client::Connection, _qh: &wayland_client::QueueHandle, - _data_device: DataDevice, + _data_device: &WlDataDevice, ) { let pointer = self.pointer.as_mut().unwrap(); let mut pstate = pointer @@ -83,7 +83,7 @@ impl DataDeviceHandler for WaylandState { &mut self, _conn: &wayland_client::Connection, _qh: &wayland_client::QueueHandle, - _data_device: DataDevice, + _data_device: &WlDataDevice, ) { } @@ -91,20 +91,22 @@ impl DataDeviceHandler for WaylandState { &mut self, _conn: &wayland_client::Connection, _qh: &wayland_client::QueueHandle, - data_device: DataDevice, + _data_device: &WlDataDevice, ) { - let mime_types = data_device.selection_mime_types(); - if !mime_types.iter().any(|s| s == TEXT_MIME_TYPE) { + let offer = self + .data_device + .as_ref() + .unwrap() + .data() + .selection_offer() + .unwrap(); + + if !offer.with_mime_types(|mime_types| mime_types.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.inner().clone()); - } + if let Some(copy_and_paste) = self.resolve_copy_and_paste() { + copy_and_paste.lock().unwrap().confirm_selection(offer); } } @@ -112,7 +114,7 @@ impl DataDeviceHandler for WaylandState { &mut self, _conn: &wayland_client::Connection, _qh: &wayland_client::QueueHandle, - _data_device: DataDevice, + _data_device: &WlDataDevice, ) { let pointer = self.pointer.as_mut().unwrap(); let mut pstate = pointer @@ -135,22 +137,6 @@ 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, @@ -198,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 59dbfbe400d..5de0676567e 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::unix::io::AsRawFd; 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,16 +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(), 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/pointer.rs b/window/src/os/wayland/pointer.rs index faefdce89f3..3436d17f40a 100644 --- a/window/src/os/wayland/pointer.rs +++ b/window/src/os/wayland/pointer.rs @@ -1,11 +1,12 @@ use std::cell::RefCell; use std::sync::{Arc, Mutex}; +use std::time::Duration; use smithay_client_toolkit::compositor::SurfaceData; +use smithay_client_toolkit::reexports::csd_frame::{DecorationsFrame, FrameClick}; 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; @@ -220,13 +221,23 @@ impl WaylandState { match evt.kind { PointerEventKind::Enter { .. } => { - inner.window_frame.click_point_moved(&evt.surface, x, y); + inner.window_frame.click_point_moved( + Duration::ZERO, + &evt.surface.id(), + x, + y, + ); } PointerEventKind::Leave { .. } => { inner.window_frame.click_point_left(); } PointerEventKind::Motion { .. } => { - inner.window_frame.click_point_moved(&evt.surface, x, y); + inner.window_frame.click_point_moved( + Duration::ZERO, + &evt.surface.id(), + x, + y, + ); } PointerEventKind::Press { button, serial, .. } | PointerEventKind::Release { button, serial, .. } => { @@ -240,7 +251,9 @@ impl WaylandState { 0x111 => FrameClick::Alternate, _ => continue, }; - if let Some(action) = inner.window_frame.on_click(click, pressed) { + if let Some(action) = + inner.window_frame.on_click(Duration::ZERO, 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 3798f4259e5..90b322b770a 100644 --- a/window/src/os/wayland/seat.rs +++ b/window/src/os/wayland/seat.rs @@ -3,9 +3,9 @@ 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; use super::state::WaylandState; @@ -23,40 +23,50 @@ impl SeatHandler for WaylandState { _conn: &Connection, qh: &QueueHandle, seat: WlSeat, - capability: smithay_client_toolkit::seat::Capability, + capability: Capability, ) { - if capability == Capability::Keyboard && self.keyboard.is_none() { - log::trace!("Setting keyboard capability"); - let keyboard = seat.get_keyboard(qh, KeyboardData {}); - self.keyboard = Some(keyboard.clone()); + match capability { + Capability::Keyboard if self.keyboard.is_none() => { + log::trace!("Setting keyboard capability"); + let keyboard = seat.get_keyboard(qh, KeyboardData {}); + self.keyboard = Some(keyboard.clone()); - if let Some(text_input) = &self.text_input { - text_input.advise_seat(&seat, &keyboard, qh); + if let Some(text_input) = &self.text_input { + text_input.advise_seat(&seat, &keyboard, qh); + } } + Capability::Pointer if self.pointer.is_none() => { + log::trace!("Setting pointer capability"); + let surface = self.compositor.create_surface(qh); + let pointer = self + .seat + .get_pointer_with_theme_and_data::( + qh, + &seat, + &self.shm.wl_shm(), + surface, + ThemeSpec::System, + PointerUserData::new(seat.clone()), + ) + .expect("Failed to create pointer"); + self.pointer = Some(pointer); + } + Capability::Touch /* if self.touch.is_none() */ => { + log::trace!("Setting touch capability"); + // TODO + } + _ => {} } - if capability == Capability::Pointer && self.pointer.is_none() { - log::trace!("Setting pointer capability"); - let pointer = self - .seat - .get_pointer_with_theme_and_data( - qh, - &seat, - ThemeSpec::System, - PointerUserData::new(seat.clone()), - ) - .expect("Failed to create pointer"); - self.pointer = Some(pointer); - + // TODO: is there a better place to put this? It only needs to be run once. (presumably per-seat) + if self.data_device.is_none() { let data_device_manager = &self.data_device_manager_state; - let data_device = data_device_manager.get_data_device(qh, &seat); - self.data_device.replace(data_device); + self.data_device = Some(data_device_manager.get_data_device(qh, &seat)); - 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; + self.primary_selection_device = self + .primary_selection_manager + .as_ref() + .map(|m| m.get_selection_device(qh, &seat)); } } @@ -65,9 +75,23 @@ impl SeatHandler for WaylandState { _conn: &Connection, _qh: &QueueHandle, _seat: WlSeat, - _capability: smithay_client_toolkit::seat::Capability, + capability: Capability, ) { - todo!() + match capability { + Capability::Keyboard => { + log::trace!("Lost keyboard capability"); + self.keyboard.take().unwrap().release(); + } + Capability::Pointer => { + log::trace!("Lost pointer capability"); + self.pointer.take(); // ThemedPointer's drop implementation calls wl_pointer.release() already. + } + Capability::Touch => { + log::trace!("Lost touch capability"); + // Nothing to do here. (yet) + } + _ => {} + } } fn remove_seat(&mut self, _conn: &Connection, _qh: &QueueHandle, _seat: WlSeat) { diff --git a/window/src/os/wayland/state.rs b/window/src/os/wayland/state.rs index c410e681766..fdb269e0104 100644 --- a/window/src/os/wayland/state.rs +++ b/window/src/os/wayland/state.rs @@ -3,12 +3,15 @@ use std::collections::HashMap; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use smithay_client_toolkit::compositor::CompositorState; +use smithay_client_toolkit::compositor::{CompositorState, SurfaceData}; 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; 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,25 +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_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 + 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::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}; @@ -71,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, } @@ -113,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), @@ -153,40 +149,29 @@ 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); +delegate_compositor!(WaylandState, surface: [SurfaceData, SurfaceUserData]); delegate_subcompositor!(WaylandState); delegate_seat!(WaylandState); -delegate_data_device_manager!(WaylandState); delegate_data_device!(WaylandState); -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_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 e6cd1d4f630..27cca621b3d 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, RefMut}; +use std::cell::RefCell; use std::cmp::max; use std::convert::TryInto; use std::io::Read; @@ -14,18 +14,22 @@ 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::shell::xdg::frame::fallback_frame::FallbackFrame; -use smithay_client_toolkit::shell::xdg::frame::{DecorationsFrame, FrameAction}; +use smithay_client_toolkit::data_device_manager::ReadPipe; +use smithay_client_toolkit::reexports::csd_frame::{ + DecorationsFrame, FrameAction, ResizeEdge, WindowState as SCTKWindowState, +}; +use smithay_client_toolkit::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge as XdgResizeEdge; +use smithay_client_toolkit::seat::pointer::CursorIcon; +use smithay_client_toolkit::shell::xdg::fallback_frame::FallbackFrame; use smithay_client_toolkit::shell::xdg::window::{ DecorationMode, Window as XdgWindow, WindowConfigure, WindowDecorations as Decorations, - WindowHandler, WindowState as SCTKWindowState, + WindowHandler, }; use smithay_client_toolkit::shell::xdg::XdgSurface; use smithay_client_toolkit::shell::WaylandSurface; @@ -193,11 +197,6 @@ 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: _, @@ -290,7 +289,6 @@ impl WaylandWindow { key_repeat: None, pending_event, pending_mouse, - pointer_surface, pending_first_configure: Some(pending_first_configure), frame_callback: None, @@ -465,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, @@ -505,7 +511,6 @@ pub struct WaylandWindowInner { dimensions: Dimensions, resize_increments: Option, window_state: WindowState, - pointer_surface: WlSurface, last_mouse_coords: Point, mouse_buttons: MouseButtons, hscroll_remainder: f64, @@ -895,27 +900,33 @@ impl WaylandWindowInner { } fn set_cursor(&mut self, cursor: Option) { - 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 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, - name, - shm.wl_shm(), - &self.pointer_surface, - 1, - ) { - log::error!("set_cursor: {}", err); + let pointer = match &state.pointer { + Some(pointer) => pointer, + None => return, + }; + + match cursor { + Some(cursor) => { + if let Err(err) = pointer.set_cursor( + &conn.connection, + match cursor { + MouseCursor::Arrow => CursorIcon::Default, + MouseCursor::Hand => CursorIcon::Pointer, + MouseCursor::SizeUpDown => CursorIcon::NsResize, + MouseCursor::SizeLeftRight => CursorIcon::EwResize, + MouseCursor::Text => CursorIcon::Text, + }, + ) { + log::error!("set_cursor: {}", err); + } + } + None => { + if let Err(err) = pointer.hide_cursor() { + log::error!("hide_cursor: {}", err) + } + } } } @@ -1146,8 +1157,23 @@ impl WaylandWindowInner { .unwrap() .show_window_menu(seat, serial, (x, y)) } - FrameAction::Resize(edge) => self.window.as_ref().unwrap().resize(seat, serial, edge), + FrameAction::Resize(edge) => { + let edge = match 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, + _ => return, // Realistically, there probably won't be any new edges added. + }; + self.window.as_ref().unwrap().resize(seat, serial, edge) + } FrameAction::Move => self.window.as_ref().unwrap().move_(seat, serial), + _ => log::warn!("unhandled FrameAction: {:?}", action), } } } @@ -1257,6 +1283,16 @@ impl CompositorHandler for WaylandState { Ok(()) }); } + + 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, + ) { + // TODO: do we need to do anything here? + } } impl WindowHandler for WaylandState {