diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cf2193b6f..b09a70c109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ You can find its changes [documented below](#070---2021-01-01). - Fixed layout of scrollbar with very small viewports ([#1715] by [@andrewhickman]) - Fixed `WindowLevel::Tooltip` on Windows platform ([#1737] by [@djeedai]) - X11 backend now supports scaling([#1751] by [@Maan2003]) +- X11 backend now supports changing cursors ([#1755] by [@Maan2003]) ### Visual @@ -706,6 +707,7 @@ Last release without a changelog :( [#1746]: https://github.com/linebender/druid/pull/1746 [#1751]: https://github.com/linebender/druid/pull/1751 [#1754]: https://github.com/linebender/druid/pull/1754 +[#1755]: https://github.com/linebender/druid/pull/1755 [Unreleased]: https://github.com/linebender/druid/compare/v0.7.0...master [0.7.0]: https://github.com/linebender/druid/compare/v0.6.0...v0.7.0 diff --git a/druid-shell/Cargo.toml b/druid-shell/Cargo.toml index 4127ebcfae..2055654ef2 100755 --- a/druid-shell/Cargo.toml +++ b/druid-shell/Cargo.toml @@ -88,7 +88,7 @@ glib = { version = "0.10.1", optional = true } glib-sys = { version = "0.10.0", optional = true } gtk-sys = { version = "0.10.0", optional = true } nix = { version = "0.18.0", optional = true } -x11rb = { version = "0.8.0", features = ["allow-unsafe-code", "present", "randr", "xfixes", "resource_manager"], optional = true } +x11rb = { version = "0.8.0", features = ["allow-unsafe-code", "present", "randr", "xfixes", "resource_manager", "cursor"], optional = true } [target.'cfg(target_arch="wasm32")'.dependencies] wasm-bindgen = "0.2.67" diff --git a/druid-shell/src/platform/x11/application.rs b/druid-shell/src/platform/x11/application.rs index a2b19d6617..214c50c7d8 100644 --- a/druid-shell/src/platform/x11/application.rs +++ b/druid-shell/src/platform/x11/application.rs @@ -25,7 +25,7 @@ use anyhow::{anyhow, Context, Error}; use x11rb::connection::Connection; use x11rb::protocol::present::ConnectionExt as _; use x11rb::protocol::xfixes::ConnectionExt as _; -use x11rb::protocol::xproto::{ConnectionExt, CreateWindowAux, EventMask, WindowClass}; +use x11rb::protocol::xproto::{self, ConnectionExt, CreateWindowAux, EventMask, WindowClass}; use x11rb::protocol::Event; use x11rb::resource_manager::Database as ResourceDb; use x11rb::xcb_ffi::XCBConnection; @@ -53,6 +53,7 @@ pub(crate) struct Application { /// The X11 resource database used to query dpi. pub(crate) rdb: ResourceDb, + pub(crate) cursors: Cursors, /// The default screen of the connected display. /// /// The connected display may also have additional screens. @@ -92,6 +93,17 @@ struct State { windows: HashMap>, } +#[derive(Clone, Debug)] +pub(crate) struct Cursors { + pub default: Option, + pub text: Option, + pub pointer: Option, + pub crosshair: Option, + pub not_allowed: Option, + pub row_resize: Option, + pub col_resize: Option, +} + impl Application { pub fn new() -> Result { // If we want to support OpenGL, we will need to open a connection with Xlib support (see @@ -126,6 +138,24 @@ impl Application { } }; + let handle = x11rb::cursor::Handle::new(connection.as_ref(), screen_num, &rdb)?.reply()?; + let load_cursor = |cursor| { + handle + .load_cursor(connection.as_ref(), cursor) + .map_err(|e| tracing::warn!("Unable to load cursor {}, error: {}", cursor, e)) + .ok() + }; + + let cursors = Cursors { + default: load_cursor("default"), + text: load_cursor("text"), + pointer: load_cursor("pointer"), + crosshair: load_cursor("crosshair"), + not_allowed: load_cursor("not-allowed"), + row_resize: load_cursor("row-resize"), + col_resize: load_cursor("col-resize"), + }; + Ok(Application { connection, rdb, @@ -133,6 +163,7 @@ impl Application { window_id, state, idle_read, + cursors, idle_write, present_opcode, marker: std::marker::PhantomData, diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index d057124c3a..01e4716abb 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -33,8 +33,8 @@ use x11rb::connection::Connection; use x11rb::protocol::present::{CompleteNotifyEvent, ConnectionExt as _, IdleNotifyEvent}; use x11rb::protocol::xfixes::{ConnectionExt as _, Region as XRegion}; use x11rb::protocol::xproto::{ - self, AtomEnum, ConfigureNotifyEvent, ConnectionExt, CreateGCAux, EventMask, Gcontext, Pixmap, - PropMode, Rectangle, Visualtype, WindowClass, + self, AtomEnum, ChangeWindowAttributesAux, ConfigureNotifyEvent, ConnectionExt, CreateGCAux, + EventMask, Gcontext, Pixmap, PropMode, Rectangle, Visualtype, WindowClass, }; use x11rb::wrapper::ConnectionExt as _; use x11rb::xcb_ffi::XCBConnection; @@ -884,6 +884,35 @@ impl Window { )); } + fn set_cursor(&self, cursor: &Cursor) { + let cursors = &self.app.cursors; + #[allow(deprecated)] + let cursor = match cursor { + Cursor::Arrow => cursors.default, + Cursor::IBeam => cursors.text, + Cursor::Pointer => cursors.pointer, + Cursor::Crosshair => cursors.crosshair, + Cursor::OpenHand => { + warn!("Cursor::OpenHand not supported for x11 backend. using arrow cursor"); + None + } + Cursor::NotAllowed => cursors.not_allowed, + Cursor::ResizeLeftRight => cursors.col_resize, + Cursor::ResizeUpDown => cursors.row_resize, + // TODO: (x11/custom cursor) + Cursor::Custom(_) => None, + }; + if cursor.is_none() { + warn!("Unable to load cursor {:?}", cursor); + return; + } + let conn = self.app.connection(); + let changes = ChangeWindowAttributesAux::new().cursor(cursor); + if let Err(e) = conn.change_window_attributes(self.id, &changes) { + error!("Changing cursor window attribute failed {}", e); + }; + } + fn set_menu(&self, _menu: Menu) { // TODO(x11/menus): implement Window::set_menu (currently a no-op) } @@ -1592,8 +1621,10 @@ impl WindowHandle { } } - pub fn set_cursor(&mut self, _cursor: &Cursor) { - // TODO(x11/cursors): implement WindowHandle::set_cursor + pub fn set_cursor(&mut self, cursor: &Cursor) { + if let Some(w) = self.window.upgrade() { + w.set_cursor(cursor); + } } pub fn make_cursor(&self, _cursor_desc: &CursorDesc) -> Option {