From 0d58d5f133170b7005620cb8b33ed75528df0de1 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 22 May 2021 14:49:43 +0200 Subject: [PATCH 1/6] x11: Add support for custom cursors --- CHANGELOG.md | 2 + druid-shell/Cargo.toml | 2 +- druid-shell/src/platform/x11/application.rs | 42 ++++++++++- druid-shell/src/platform/x11/window.rs | 79 +++++++++++++++++++-- 4 files changed, 118 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f58efa6770..edb0a56b71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ You can find its changes [documented below](#070---2021-01-01). - `chrono` feature with `Data` support for [chrono](https://docs.rs/chrono/) types ([#1743] by [@r-ml]) - Text input handles Delete key ([#1746] by [@bjorn]) - `lens` macro can access nested fields ([#1764] by [@Maan2003]) +- X11 backend now supports custom cursors ([#1800] by [@psychon]) ### Changed @@ -724,6 +725,7 @@ Last release without a changelog :( [#1764]: https://github.com/linebender/druid/pull/1764 [#1772]: https://github.com/linebender/druid/pull/1772 [#1787]: https://github.com/linebender/druid/pull/1787 +[#1800]: https://github.com/linebender/druid/pull/1800 [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 5815f9d1ba..d5deff9242 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", "cursor"], optional = true } +x11rb = { version = "0.8.0", features = ["allow-unsafe-code", "present", "render", "randr", "xfixes", "resource_manager", "cursor", "image"], 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 39725d0fc0..53d132b2c8 100644 --- a/druid-shell/src/platform/x11/application.rs +++ b/druid-shell/src/platform/x11/application.rs @@ -22,8 +22,9 @@ use std::rc::Rc; use std::time::{Duration, Instant}; use anyhow::{anyhow, Context, Error}; -use x11rb::connection::Connection; +use x11rb::connection::{Connection, RequestConnection}; use x11rb::protocol::present::ConnectionExt as _; +use x11rb::protocol::render::{self, ConnectionExt as _, Pictformat}; use x11rb::protocol::xfixes::ConnectionExt as _; use x11rb::protocol::xproto::{self, ConnectionExt, CreateWindowAux, EventMask, WindowClass}; use x11rb::protocol::Event; @@ -83,6 +84,8 @@ pub(crate) struct Application { idle_write: RawFd, /// The major opcode of the Present extension, if it is supported. present_opcode: Option, + /// Support for the render extension in at least version 0.5? + render_argb32_pictformat_cursor: Option, } /// The mutable `Application` state. @@ -138,6 +141,36 @@ impl Application { } }; + let pictformats = connection.render_query_pict_formats()?; + let render_create_cursor_supported = match connection.extension_information(render::X11_EXTENSION_NAME)? + .and_then(|_| connection.render_query_version(0, 5).ok()) + .map(|cookie| cookie.reply()) + .transpose()? { + Some(version) if version.major_version >= 1 || version.minor_version >= 5 => true, + _ => false, + }; + let render_argb32_pictformat_cursor = if render_create_cursor_supported { + pictformats.reply()? + .formats + .iter() + .find(|format| { + format.type_ == render::PictType::DIRECT + && format.depth == 32 + && format.direct.red_shift == 16 + && format.direct.red_mask == 0xff + && format.direct.green_shift == 8 + && format.direct.green_mask == 0xff + && format.direct.blue_shift == 0 + && format.direct.blue_mask == 0xff + && format.direct.alpha_shift == 0 + && format.direct.alpha_mask == 0xff + }) + .map(|format| format.id) + } else { + drop(pictformats); + None + }; + let handle = x11rb::cursor::Handle::new(connection.as_ref(), screen_num, &rdb)?.reply()?; let load_cursor = |cursor| { handle @@ -167,6 +200,7 @@ impl Application { idle_write, present_opcode, marker: std::marker::PhantomData, + render_argb32_pictformat_cursor, }) } @@ -217,6 +251,12 @@ impl Application { self.present_opcode } + /// Return the ARGB32 pictformat of the server, but only if RENDER's CreateCursor is supported + #[inline] + pub(crate) fn render_argb32_pictformat_cursor(&self) -> Option { + self.render_argb32_pictformat_cursor + } + fn create_event_window(conn: &Rc, screen_num: i32) -> Result { let id = conn.generate_id()?; let setup = conn.setup(); diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index 30d6e8f7be..959ffbba1c 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -14,6 +14,7 @@ //! X11 window creation and window management. +use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::collections::BinaryHeap; use std::convert::{TryFrom, TryInto}; @@ -29,7 +30,10 @@ use cairo::{XCBConnection as CairoXCBConnection, XCBDrawable, XCBSurface, XCBVis use tracing::{error, info, warn}; use x11rb::atom_manager; use x11rb::connection::Connection; +use x11rb::errors::ReplyOrIdError; +use x11rb::image::{BitsPerPixel, Image, ImageOrder, ScanlinePad}; use x11rb::protocol::present::{CompleteNotifyEvent, ConnectionExt as _, IdleNotifyEvent}; +use x11rb::protocol::render::{ConnectionExt as _, Pictformat}; use x11rb::protocol::xfixes::{ConnectionExt as _, Region as XRegion}; use x11rb::protocol::xproto::{ self, AtomEnum, ChangeWindowAttributesAux, ConfigureNotifyEvent, ConnectionExt, CreateGCAux, @@ -883,8 +887,7 @@ impl Window { Cursor::NotAllowed => cursors.not_allowed, Cursor::ResizeLeftRight => cursors.col_resize, Cursor::ResizeUpDown => cursors.row_resize, - // TODO: (x11/custom cursor) - Cursor::Custom(_) => None, + Cursor::Custom(custom) => Some(custom.0), }; if cursor.is_none() { warn!("Unable to load cursor {:?}", cursor); @@ -1611,9 +1614,31 @@ impl WindowHandle { } } - pub fn make_cursor(&self, _cursor_desc: &CursorDesc) -> Option { - warn!("Custom cursors are not yet supported in the X11 backend"); - None + pub fn make_cursor(&self, desc: &CursorDesc) -> Option { + if let Some(w) = self.window.upgrade() { + match w.app.render_argb32_pictformat_cursor() { + None => { + warn!("Custom cursors are not supported by the X11 server"); + None + } + Some(format) => { + // BEGIN: Lots of code just to get the image into a RENDER Picture + + let conn = w.app.connection(); + let screen = &conn.setup().roots[w.app.screen_num() as usize]; + match make_cursor(&**conn, screen.root, format, desc) { + // TODO: We 'leak' the cursor - nothing ever calls render_free_cursor + Ok(cursor) => Some(cursor), + Err(err) => { + error!("Failed to create custom cursor: {:?}", err); + None + }, + } + } + } + } else { + None + } } pub fn open_file(&mut self, _options: FileDialogOptions) -> Option { @@ -1669,3 +1694,47 @@ unsafe impl HasRawWindowHandle for WindowHandle { RawWindowHandle::Xcb(handle) } } + +fn make_cursor(conn: &XCBConnection, root_window: u32, argb32_format: Pictformat, desc: &CursorDesc) -> Result { + // BEGIN: Lots of code just to get the image into a RENDER Picture + + // No idea how to sanely get the pixel values, so I'll go with 'insane': + // Iterate over all pixels and build an array + let pixels = desc.image + .pixel_colors() + .flat_map(|row| row.flat_map(|color| { + // TODO: RENDER (likely) expects unpremultiplied alpha. We should convert. + let (r, g, b, a) = color.as_rgba8(); + // TODO Ownership and flat_map don't go well together :-( + vec![r, g, b, a] + })) + .collect::>(); + let image = Image::new( + desc.image.width().try_into().expect("Invalid cursor width"), + desc.image.height().try_into().expect("Invalid cursor height"), + ScanlinePad::Pad8, + 32, + BitsPerPixel::B32, + ImageOrder::MSBFirst, + Cow::Owned(pixels), + ).expect("We got the number of bytes for this image wrong?!"); + + let pixmap = conn.generate_id()?; + let gc = conn.generate_id()?; + let picture = conn.generate_id()?; + conn.create_pixmap(32, pixmap, root_window, image.width(), image.height())?; + conn.create_gc(gc, pixmap, &Default::default())?; + + image.put(conn, pixmap, gc, 0, 0)?; + conn.render_create_picture(picture, pixmap, argb32_format, &Default::default())?; + + conn.free_gc(gc)?; + conn.free_pixmap(pixmap)?; + // End: Lots of code just to get the image into a RENDER Picture + + let cursor = conn.generate_id()?; + conn.render_create_cursor(cursor, picture, desc.hot.x as u16, desc.hot.y as u16)?; + conn.render_free_picture(picture)?; + + Ok(Cursor::Custom(CustomCursor(cursor))) +} From 707e1404fa69f738431af753be71e704537a5020 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 22 May 2021 15:02:19 +0200 Subject: [PATCH 2/6] Rustfmt and fixed link --- CHANGELOG.md | 4 +-- druid-shell/src/platform/x11/application.rs | 13 +++++--- druid-shell/src/platform/x11/window.rs | 34 ++++++++++++++------- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edb0a56b71..cad7aab1c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ You can find its changes [documented below](#070---2021-01-01). - `chrono` feature with `Data` support for [chrono](https://docs.rs/chrono/) types ([#1743] by [@r-ml]) - Text input handles Delete key ([#1746] by [@bjorn]) - `lens` macro can access nested fields ([#1764] by [@Maan2003]) -- X11 backend now supports custom cursors ([#1800] by [@psychon]) +- X11 backend now supports custom cursors ([#1801] by [@psychon]) ### Changed @@ -725,7 +725,7 @@ Last release without a changelog :( [#1764]: https://github.com/linebender/druid/pull/1764 [#1772]: https://github.com/linebender/druid/pull/1772 [#1787]: https://github.com/linebender/druid/pull/1787 -[#1800]: https://github.com/linebender/druid/pull/1800 +[#1801]: https://github.com/linebender/druid/pull/1800 [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/src/platform/x11/application.rs b/druid-shell/src/platform/x11/application.rs index 53d132b2c8..2949b7e473 100644 --- a/druid-shell/src/platform/x11/application.rs +++ b/druid-shell/src/platform/x11/application.rs @@ -142,15 +142,18 @@ impl Application { }; let pictformats = connection.render_query_pict_formats()?; - let render_create_cursor_supported = match connection.extension_information(render::X11_EXTENSION_NAME)? - .and_then(|_| connection.render_query_version(0, 5).ok()) - .map(|cookie| cookie.reply()) - .transpose()? { + let render_create_cursor_supported = match connection + .extension_information(render::X11_EXTENSION_NAME)? + .and_then(|_| connection.render_query_version(0, 5).ok()) + .map(|cookie| cookie.reply()) + .transpose()? + { Some(version) if version.major_version >= 1 || version.minor_version >= 5 => true, _ => false, }; let render_argb32_pictformat_cursor = if render_create_cursor_supported { - pictformats.reply()? + pictformats + .reply()? .formats .iter() .find(|format| { diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index 959ffbba1c..be1f5b8f65 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -1632,7 +1632,7 @@ impl WindowHandle { Err(err) => { error!("Failed to create custom cursor: {:?}", err); None - }, + } } } } @@ -1695,29 +1695,41 @@ unsafe impl HasRawWindowHandle for WindowHandle { } } -fn make_cursor(conn: &XCBConnection, root_window: u32, argb32_format: Pictformat, desc: &CursorDesc) -> Result { +fn make_cursor( + conn: &XCBConnection, + root_window: u32, + argb32_format: Pictformat, + desc: &CursorDesc, +) -> Result { // BEGIN: Lots of code just to get the image into a RENDER Picture // No idea how to sanely get the pixel values, so I'll go with 'insane': // Iterate over all pixels and build an array - let pixels = desc.image + let pixels = desc + .image .pixel_colors() - .flat_map(|row| row.flat_map(|color| { - // TODO: RENDER (likely) expects unpremultiplied alpha. We should convert. - let (r, g, b, a) = color.as_rgba8(); - // TODO Ownership and flat_map don't go well together :-( - vec![r, g, b, a] - })) + .flat_map(|row| { + row.flat_map(|color| { + // TODO: RENDER (likely) expects unpremultiplied alpha. We should convert. + let (r, g, b, a) = color.as_rgba8(); + // TODO Ownership and flat_map don't go well together :-( + vec![r, g, b, a] + }) + }) .collect::>(); let image = Image::new( desc.image.width().try_into().expect("Invalid cursor width"), - desc.image.height().try_into().expect("Invalid cursor height"), + desc.image + .height() + .try_into() + .expect("Invalid cursor height"), ScanlinePad::Pad8, 32, BitsPerPixel::B32, ImageOrder::MSBFirst, Cow::Owned(pixels), - ).expect("We got the number of bytes for this image wrong?!"); + ) + .expect("We got the number of bytes for this image wrong?!"); let pixmap = conn.generate_id()?; let gc = conn.generate_id()?; From 16533d6e8c5a38446a4bb0b7cd61c33894a62b7d Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 22 May 2021 15:31:00 +0200 Subject: [PATCH 3/6] Apply suggestion from clippy Signed-off-by: Uli Schlachter --- druid-shell/src/platform/x11/application.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/druid-shell/src/platform/x11/application.rs b/druid-shell/src/platform/x11/application.rs index 2949b7e473..af4f01b6b9 100644 --- a/druid-shell/src/platform/x11/application.rs +++ b/druid-shell/src/platform/x11/application.rs @@ -142,15 +142,12 @@ impl Application { }; let pictformats = connection.render_query_pict_formats()?; - let render_create_cursor_supported = match connection + let render_create_cursor_supported = matches!(connection .extension_information(render::X11_EXTENSION_NAME)? .and_then(|_| connection.render_query_version(0, 5).ok()) .map(|cookie| cookie.reply()) - .transpose()? - { - Some(version) if version.major_version >= 1 || version.minor_version >= 5 => true, - _ => false, - }; + .transpose()?, + Some(version) if version.major_version >= 1 || version.minor_version >= 5); let render_argb32_pictformat_cursor = if render_create_cursor_supported { pictformats .reply()? From 2d4e2cd3cdd78af1623fee704412f4f12f0b104c Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 22 May 2021 17:27:20 +0200 Subject: [PATCH 4/6] Fix pict format check Signed-off-by: Uli Schlachter --- druid-shell/src/platform/x11/application.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/druid-shell/src/platform/x11/application.rs b/druid-shell/src/platform/x11/application.rs index af4f01b6b9..b8fbbac8d6 100644 --- a/druid-shell/src/platform/x11/application.rs +++ b/druid-shell/src/platform/x11/application.rs @@ -162,7 +162,7 @@ impl Application { && format.direct.green_mask == 0xff && format.direct.blue_shift == 0 && format.direct.blue_mask == 0xff - && format.direct.alpha_shift == 0 + && format.direct.alpha_shift == 24 && format.direct.alpha_mask == 0xff }) .map(|format| format.id) From 912991d4da21dbbbbbd8e6cd8e0e6fe45ce3b82f Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sun, 30 May 2021 14:28:11 +0200 Subject: [PATCH 5/6] Fix cursor formats While piet provides pixels in rgba order, the X11 server wants an u32 in its native endianness with order argb. I even called a variable argb32_format, but I still did not handle this difference. This commit premultiplies the color component with the alpha, handles the different component order and also converts to the server's endianness. Signed-off-by: Uli Schlachter --- druid-shell/src/platform/x11/window.rs | 31 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index be1f5b8f65..ce7eeb0598 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -37,7 +37,8 @@ use x11rb::protocol::render::{ConnectionExt as _, Pictformat}; use x11rb::protocol::xfixes::{ConnectionExt as _, Region as XRegion}; use x11rb::protocol::xproto::{ self, AtomEnum, ChangeWindowAttributesAux, ConfigureNotifyEvent, ConnectionExt, CreateGCAux, - EventMask, Gcontext, Pixmap, PropMode, Rectangle, Visualtype, WindowClass, + EventMask, Gcontext, ImageOrder as X11ImageOrder, Pixmap, PropMode, Rectangle, Visualtype, + WindowClass, }; use x11rb::wrapper::ConnectionExt as _; use x11rb::xcb_ffi::XCBConnection; @@ -1622,11 +1623,10 @@ impl WindowHandle { None } Some(format) => { - // BEGIN: Lots of code just to get the image into a RENDER Picture - let conn = w.app.connection(); - let screen = &conn.setup().roots[w.app.screen_num() as usize]; - match make_cursor(&**conn, screen.root, format, desc) { + let setup = &conn.setup(); + let screen = &setup.roots[w.app.screen_num() as usize]; + match make_cursor(&**conn, setup.image_byte_order, screen.root, format, desc) { // TODO: We 'leak' the cursor - nothing ever calls render_free_cursor Ok(cursor) => Some(cursor), Err(err) => { @@ -1697,12 +1697,19 @@ unsafe impl HasRawWindowHandle for WindowHandle { fn make_cursor( conn: &XCBConnection, + byte_order: X11ImageOrder, root_window: u32, argb32_format: Pictformat, desc: &CursorDesc, ) -> Result { // BEGIN: Lots of code just to get the image into a RENDER Picture + fn multiply_alpha(color: u8, alpha: u8) -> u8 { + let (color, alpha) = (u16::from(color), u16::from(alpha)); + let temp = color * alpha + 0x80u16; + ((temp + (temp >> 8)) >> 8) as u8 + } + // No idea how to sanely get the pixel values, so I'll go with 'insane': // Iterate over all pixels and build an array let pixels = desc @@ -1710,10 +1717,20 @@ fn make_cursor( .pixel_colors() .flat_map(|row| { row.flat_map(|color| { - // TODO: RENDER (likely) expects unpremultiplied alpha. We should convert. let (r, g, b, a) = color.as_rgba8(); + // RENDER wants premultiplied alpha + let (r, g, b) = ( + multiply_alpha(r, a), + multiply_alpha(g, a), + multiply_alpha(b, a), + ); + // piet gives us rgba in this order, the server expects an u32 with argb. + let (b0, b1, b2, b3) = match byte_order { + X11ImageOrder::LSB_FIRST => (b, g, r, a), + _ => (a, r, g, b), + }; // TODO Ownership and flat_map don't go well together :-( - vec![r, g, b, a] + vec![b0, b1, b2, b3] }) }) .collect::>(); From 75aba5c7ef5a32c119f99831423fdc4c015f60b6 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sun, 30 May 2021 14:42:37 +0200 Subject: [PATCH 6/6] Remove depdendency x11rb's image feature The code only used x11rb's image feature for splitting a large PutImage request into smaller pieces. Since X11 servers usually have a maximum request size of 16 MiB these days, it is unlikely that people create cursors large enough to go above this limit. You'd need a 2k x 2k cursor for that! Signed-off-by: Uli Schlachter --- druid-shell/Cargo.toml | 2 +- druid-shell/src/platform/x11/window.rs | 40 ++++++++++++++------------ 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/druid-shell/Cargo.toml b/druid-shell/Cargo.toml index d5deff9242..8e2e33cf93 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", "render", "randr", "xfixes", "resource_manager", "cursor", "image"], optional = true } +x11rb = { version = "0.8.0", features = ["allow-unsafe-code", "present", "render", "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/window.rs b/druid-shell/src/platform/x11/window.rs index ce7eeb0598..1d22a305a6 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -14,7 +14,6 @@ //! X11 window creation and window management. -use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::collections::BinaryHeap; use std::convert::{TryFrom, TryInto}; @@ -31,14 +30,13 @@ use tracing::{error, info, warn}; use x11rb::atom_manager; use x11rb::connection::Connection; use x11rb::errors::ReplyOrIdError; -use x11rb::image::{BitsPerPixel, Image, ImageOrder, ScanlinePad}; use x11rb::protocol::present::{CompleteNotifyEvent, ConnectionExt as _, IdleNotifyEvent}; use x11rb::protocol::render::{ConnectionExt as _, Pictformat}; use x11rb::protocol::xfixes::{ConnectionExt as _, Region as XRegion}; use x11rb::protocol::xproto::{ self, AtomEnum, ChangeWindowAttributesAux, ConfigureNotifyEvent, ConnectionExt, CreateGCAux, - EventMask, Gcontext, ImageOrder as X11ImageOrder, Pixmap, PropMode, Rectangle, Visualtype, - WindowClass, + EventMask, Gcontext, ImageFormat, ImageOrder as X11ImageOrder, Pixmap, PropMode, Rectangle, + Visualtype, WindowClass, }; use x11rb::wrapper::ConnectionExt as _; use x11rb::xcb_ffi::XCBConnection; @@ -1734,27 +1732,31 @@ fn make_cursor( }) }) .collect::>(); - let image = Image::new( - desc.image.width().try_into().expect("Invalid cursor width"), - desc.image - .height() - .try_into() - .expect("Invalid cursor height"), - ScanlinePad::Pad8, - 32, - BitsPerPixel::B32, - ImageOrder::MSBFirst, - Cow::Owned(pixels), - ) - .expect("We got the number of bytes for this image wrong?!"); + let width = desc.image.width().try_into().expect("Invalid cursor width"); + let height = desc + .image + .height() + .try_into() + .expect("Invalid cursor height"); let pixmap = conn.generate_id()?; let gc = conn.generate_id()?; let picture = conn.generate_id()?; - conn.create_pixmap(32, pixmap, root_window, image.width(), image.height())?; + conn.create_pixmap(32, pixmap, root_window, width, height)?; conn.create_gc(gc, pixmap, &Default::default())?; - image.put(conn, pixmap, gc, 0, 0)?; + conn.put_image( + ImageFormat::Z_PIXMAP, + pixmap, + gc, + width, + height, + 0, + 0, + 0, + 32, + &pixels, + )?; conn.render_create_picture(picture, pixmap, argb32_format, &Default::default())?; conn.free_gc(gc)?;