x11: Cursor style support (#11237)

Adds cursor style support to X11

![image](https://github.com/zed-industries/zed/assets/71973804/e5a2414f-4d80-4963-93d2-e4a15878a718)


Release Notes:

- N/A
This commit is contained in:
apricotbucket28 2024-05-06 17:05:00 -03:00 committed by GitHub
parent bb1817ff31
commit 091e7cb395
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 61 additions and 9 deletions

View file

@ -120,6 +120,7 @@ x11rb = { version = "0.13.0", features = [
"xkb",
"randr",
"xinput",
"cursor",
"resource_manager",
] }
xkbcommon = { version = "0.7", features = ["wayland", "x11"] }

View file

@ -3,19 +3,21 @@ use std::ops::Deref;
use std::rc::{Rc, Weak};
use std::time::{Duration, Instant};
use calloop::{EventLoop, LoopHandle};
use calloop::generic::{FdWrapper, Generic};
use calloop::{EventLoop, LoopHandle, RegistrationToken};
use collections::HashMap;
use copypasta::x11_clipboard::{Clipboard, Primary, X11ClipboardContext};
use copypasta::ClipboardProvider;
use util::ResultExt;
use x11rb::connection::{Connection, RequestConnection};
use x11rb::cursor;
use x11rb::errors::ConnectionError;
use x11rb::protocol::randr::ConnectionExt as _;
use x11rb::protocol::xinput::{ConnectionExt, ScrollClass};
use x11rb::protocol::xkb::ConnectionExt as _;
use x11rb::protocol::xproto::ConnectionExt as _;
use x11rb::protocol::{randr, xinput, xkb, xproto, Event};
use x11rb::protocol::xproto::{ChangeWindowAttributesAux, ConnectionExt as _};
use x11rb::protocol::{randr, render, xinput, xkb, xproto, Event};
use x11rb::resource_manager::Database;
use x11rb::xcb_ffi::XCBConnection;
use xkbc::x11::ffi::{XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION};
@ -36,10 +38,6 @@ use super::{
use super::{button_of_key, modifiers_from_state};
use crate::platform::linux::is_within_click_distance;
use crate::platform::linux::platform::DOUBLE_CLICK_INTERVAL;
use calloop::{
generic::{FdWrapper, Generic},
RegistrationToken,
};
pub(crate) struct WindowRef {
window: X11WindowStatePtr,
@ -72,6 +70,10 @@ pub struct X11ClientState {
pub(crate) focused_window: Option<xproto::Window>,
pub(crate) xkb: xkbc::State,
pub(crate) cursor_handle: cursor::Handle,
pub(crate) cursor_styles: HashMap<xproto::Window, CursorStyle>,
pub(crate) cursor_cache: HashMap<CursorStyle, xproto::Cursor>,
pub(crate) scroll_class_data: Vec<xinput::DeviceClassDataScroll>,
pub(crate) scroll_x: Option<f32>,
pub(crate) scroll_y: Option<f32>,
@ -93,6 +95,8 @@ impl X11ClientStatePtr {
state.loop_handle.remove(window_ref.refresh_event_token);
}
state.cursor_styles.remove(&x_window);
if state.windows.is_empty() {
state.common.signal.stop();
}
@ -123,6 +127,9 @@ impl X11Client {
xcb_connection
.prefetch_extension_information(randr::X11_EXTENSION_NAME)
.unwrap();
xcb_connection
.prefetch_extension_information(render::X11_EXTENSION_NAME)
.unwrap();
xcb_connection
.prefetch_extension_information(xinput::X11_EXTENSION_NAME)
.unwrap();
@ -210,6 +217,11 @@ impl X11Client {
.map(|dpi: f32| dpi / 96.0)
.unwrap_or(1.0);
let cursor_handle = cursor::Handle::new(&xcb_connection, x_root_index, &resource_database)
.unwrap()
.reply()
.unwrap();
let clipboard = X11ClipboardContext::<Clipboard>::new().unwrap();
let primary = X11ClipboardContext::<Primary>::new().unwrap();
@ -254,6 +266,10 @@ impl X11Client {
focused_window: None,
xkb: xkb_state,
cursor_handle,
cursor_styles: HashMap::default(),
cursor_cache: HashMap::default(),
scroll_class_data,
scroll_x: None,
scroll_y: None,
@ -672,8 +688,43 @@ impl LinuxClient for X11Client {
Box::new(window)
}
//todo(linux)
fn set_cursor_style(&self, _style: CursorStyle) {}
fn set_cursor_style(&self, style: CursorStyle) {
let mut state = self.0.borrow_mut();
let Some(focused_window) = state.focused_window else {
return;
};
let current_style = state
.cursor_styles
.get(&focused_window)
.unwrap_or(&CursorStyle::Arrow);
if *current_style == style {
return;
}
let cursor = match state.cursor_cache.get(&style) {
Some(cursor) => *cursor,
None => {
let cursor = state
.cursor_handle
.load_cursor(&state.xcb_connection, &style.to_icon_name())
.expect("failed to load cursor");
state.cursor_cache.insert(style, cursor);
cursor
}
};
state.cursor_styles.insert(focused_window, style);
state
.xcb_connection
.change_window_attributes(
focused_window,
&ChangeWindowAttributesAux {
cursor: Some(cursor),
..Default::default()
},
)
.expect("failed to change window cursor");
}
fn open_uri(&self, uri: &str) {
open_uri_internal(uri, None);