From 02f5d63770527e3d766768f56a02c43473be90ad Mon Sep 17 00:00:00 2001 From: Maan2003 Date: Sat, 1 May 2021 16:07:23 +0530 Subject: [PATCH 1/2] X11 cursor --- CHANGELOG.md | 2 ++ druid-shell/Cargo.toml | 2 +- druid-shell/src/platform/x11/application.rs | 28 ++++++++++++++++- druid-shell/src/platform/x11/window.rs | 35 ++++++++++++++++++--- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 007416c983..0247accfb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,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 @@ -701,6 +702,7 @@ Last release without a changelog :( [#1737]: https://github.com/linebender/druid/pull/1737 [#1743]: https://github.com/linebender/druid/pull/1743 [#1751]: https://github.com/linebender/druid/pull/1751 +[#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..0ddffb2585 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: xproto::Cursor, + pub text: xproto::Cursor, + pub pointer: xproto::Cursor, + pub crosshair: xproto::Cursor, + pub not_allowed: xproto::Cursor, + pub row_resize: xproto::Cursor, + pub col_resize: xproto::Cursor, +} + 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,19 @@ impl Application { } }; + let handle = x11rb::cursor::Handle::new(connection.as_ref(), screen_num, &rdb)?.reply()?; + let load_cursor = |name| handle.load_cursor(connection.as_ref(), name); + + 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 +158,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..d2aedfb972 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,31 @@ 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"); + cursors.default + } + Cursor::NotAllowed => cursors.not_allowed, + Cursor::ResizeLeftRight => cursors.col_resize, + Cursor::ResizeUpDown => cursors.row_resize, + // TODO: (x11/custom cursor) + Cursor::Custom(_) => cursors.default, + }; + let conn = self.app.connection(); + let changes = ChangeWindowAttributesAux::new().cursor(cursor); + if let Err(e) = conn.change_window_attributes(self.id, &changes) { + error!("Changing window attributes failed {}", e); + }; + } + fn set_menu(&self, _menu: Menu) { // TODO(x11/menus): implement Window::set_menu (currently a no-op) } @@ -1592,8 +1617,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 { From 6d41e50552d75445a5215586c56d0c3490f61046 Mon Sep 17 00:00:00 2001 From: Maan2003 Date: Mon, 3 May 2021 21:22:51 +0530 Subject: [PATCH 2/2] Apply suggestions from code review --- druid-shell/src/platform/x11/application.rs | 35 ++++++++++++--------- druid-shell/src/platform/x11/window.rs | 10 ++++-- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/druid-shell/src/platform/x11/application.rs b/druid-shell/src/platform/x11/application.rs index 0ddffb2585..214c50c7d8 100644 --- a/druid-shell/src/platform/x11/application.rs +++ b/druid-shell/src/platform/x11/application.rs @@ -95,13 +95,13 @@ struct State { #[derive(Clone, Debug)] pub(crate) struct Cursors { - pub default: xproto::Cursor, - pub text: xproto::Cursor, - pub pointer: xproto::Cursor, - pub crosshair: xproto::Cursor, - pub not_allowed: xproto::Cursor, - pub row_resize: xproto::Cursor, - pub col_resize: xproto::Cursor, + 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 { @@ -139,16 +139,21 @@ impl Application { }; let handle = x11rb::cursor::Handle::new(connection.as_ref(), screen_num, &rdb)?.reply()?; - let load_cursor = |name| handle.load_cursor(connection.as_ref(), name); + 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")?, + 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 { diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index d2aedfb972..01e4716abb 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -894,18 +894,22 @@ impl Window { Cursor::Crosshair => cursors.crosshair, Cursor::OpenHand => { warn!("Cursor::OpenHand not supported for x11 backend. using arrow cursor"); - cursors.default + None } Cursor::NotAllowed => cursors.not_allowed, Cursor::ResizeLeftRight => cursors.col_resize, Cursor::ResizeUpDown => cursors.row_resize, // TODO: (x11/custom cursor) - Cursor::Custom(_) => cursors.default, + 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 window attributes failed {}", e); + error!("Changing cursor window attribute failed {}", e); }; }