1
0
Fork 0

x11: refactor cursor logic

This commit is contained in:
William Light 2020-09-14 02:47:58 +02:00
parent ad3dfce97d
commit 23a43c4cdd
3 changed files with 86 additions and 80 deletions

View file

@ -1,34 +1,65 @@
use std::os::raw::{c_ulong, c_char}; use std::os::raw::c_char;
use std::collections::HashMap;
use crate::MouseCursor; use crate::MouseCursor;
pub fn set_cursor( fn create_empty_cursor(display: *mut x11::xlib::Display) -> Option<u32> {
xcb_connection: &mut crate::x11::XcbConnection, let data = 0;
window_id: u32, let pixmap = unsafe {
cursor_cache: &mut HashMap<MouseCursor, c_ulong>, let screen = x11::xlib::XDefaultScreen(display);
mouse_cursor: MouseCursor, let window = x11::xlib::XRootWindow(display, screen);
) { x11::xlib::XCreateBitmapFromData(display, window, &data, 1, 1)
let display = xcb_connection.conn.get_raw_dpy(); };
let cursor = *cursor_cache if pixmap == 0 {
.entry(mouse_cursor) return None;
.or_insert_with(|| get_cursor(display, mouse_cursor)); }
unsafe { unsafe {
if cursor != 0 { // We don't care about this color, since it only fills bytes
x11::xlib::XDefineCursor(display, window_id as c_ulong, cursor); // in the pixmap which are not 0 in the mask.
} let mut color: x11::xlib::XColor = std::mem::zeroed();
x11::xlib::XFlush(display);
let cursor = x11::xlib::XCreatePixmapCursor(
display,
pixmap,
pixmap,
&mut color as *mut _,
&mut color as *mut _,
0,
0,
);
x11::xlib::XFreePixmap(display, pixmap);
Some(cursor as u32)
} }
} }
fn get_cursor(display: *mut x11::xlib::Display, cursor: MouseCursor) -> c_ulong { fn load_cursor(display: *mut x11::xlib::Display, name: &[u8]) -> Option<u32> {
let xcursor = unsafe {
x11::xcursor::XcursorLibraryLoadCursor(display, name.as_ptr() as *const c_char)
};
if xcursor == 0 {
None
} else {
Some(xcursor as u32)
}
}
fn load_first_existing_cursor(display: *mut x11::xlib::Display, names: &[&[u8]]) -> Option<u32> {
names.iter()
.map(|name| load_cursor(display, name))
.find(|xcursor| xcursor.is_some())
.unwrap_or(None)
}
pub(super) fn get_xcursor(display: *mut x11::xlib::Display, cursor: MouseCursor) -> u32 {
let load = |name: &[u8]| load_cursor(display, name); let load = |name: &[u8]| load_cursor(display, name);
let loadn = |names: &[&[u8]]| load_first_existing_cursor(display, names); let loadn = |names: &[&[u8]]| load_first_existing_cursor(display, names);
let mut cursor = match cursor { let cursor = match cursor {
MouseCursor::Default => load(b"left_ptr\0"), MouseCursor::Default => None, // catch this in the fallback case below
MouseCursor::Hand => loadn(&[b"hand2\0", b"hand1\0"]), MouseCursor::Hand => loadn(&[b"hand2\0", b"hand1\0"]),
MouseCursor::HandGrabbing => loadn(&[b"closedhand\0", b"grabbing\0"]), MouseCursor::HandGrabbing => loadn(&[b"closedhand\0", b"grabbing\0"]),
MouseCursor::Help => load(b"question_arrow\0"), MouseCursor::Help => load(b"question_arrow\0"),
@ -70,56 +101,7 @@ fn get_cursor(display: *mut x11::xlib::Display, cursor: MouseCursor) -> c_ulong
MouseCursor::RowResize => loadn(&[b"split_v\0", b"v_double_arrow\0"]), MouseCursor::RowResize => loadn(&[b"split_v\0", b"v_double_arrow\0"]),
}; };
if cursor == 0 {
cursor = load(b"left_ptr\0")
}
cursor cursor
} .or(load(b"left_ptr\0"))
.unwrap_or(0)
fn load_cursor(display: *mut x11::xlib::Display, name: &[u8]) -> c_ulong {
unsafe {
x11::xcursor::XcursorLibraryLoadCursor(display, name.as_ptr() as *const c_char)
}
}
fn load_first_existing_cursor(display: *mut x11::xlib::Display, names: &[&[u8]]) -> c_ulong {
for name in names.iter() {
let xcursor = load_cursor(display, name);
if xcursor != 0 {
return xcursor;
}
}
0
}
fn create_empty_cursor(display: *mut x11::xlib::Display,) -> c_ulong {
let data = 0;
let pixmap = unsafe {
let screen = x11::xlib::XDefaultScreen(display);
let window = x11::xlib::XRootWindow(display, screen);
x11::xlib::XCreateBitmapFromData(display, window, &data, 1, 1)
};
if pixmap == 0 {
panic!("failed to allocate pixmap for cursor");
}
unsafe {
// We don't care about this color, since it only fills bytes
// in the pixmap which are not 0 in the mask.
let mut dummy_color = maybe_uninit::MaybeUninit::uninit();
let cursor = x11::xlib::XCreatePixmapCursor(
display,
pixmap,
pixmap,
dummy_color.as_mut_ptr(),
dummy_color.as_mut_ptr(),
0,
0,
);
x11::xlib::XFreePixmap(display, pixmap);
cursor
}
} }

View file

@ -2,7 +2,6 @@ use std::os::raw::{c_ulong, c_void};
use std::sync::mpsc; use std::sync::mpsc;
use std::time::*; use std::time::*;
use std::thread; use std::thread;
use std::collections::HashMap;
use raw_window_handle::{ use raw_window_handle::{
unix::XlibHandle, unix::XlibHandle,
@ -21,7 +20,6 @@ pub struct Window {
window_id: u32, window_id: u32,
window_info: WindowInfo, window_info: WindowInfo,
mouse_cursor: MouseCursor, mouse_cursor: MouseCursor,
cursor_cache: HashMap<MouseCursor, c_ulong>,
frame_interval: Duration, frame_interval: Duration,
event_loop_running: bool, event_loop_running: bool,
@ -159,7 +157,6 @@ impl Window {
window_id, window_id,
window_info, window_info,
mouse_cursor: MouseCursor::default(), mouse_cursor: MouseCursor::default(),
cursor_cache: HashMap::new(),
frame_interval: Duration::from_millis(15), frame_interval: Duration::from_millis(15),
event_loop_running: false, event_loop_running: false,
@ -180,16 +177,23 @@ impl Window {
} }
pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) { pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) {
if self.mouse_cursor != mouse_cursor { if self.mouse_cursor == mouse_cursor {
crate::x11::cursor::set_cursor( return
&mut self.xcb_connection, }
let xid = self.xcb_connection.get_cursor_xid(mouse_cursor);
if xid != 0 {
xcb::change_window_attributes(
&self.xcb_connection.conn,
self.window_id, self.window_id,
&mut self.cursor_cache, &[(xcb::CW_CURSOR, xid)]
mouse_cursor
); );
self.mouse_cursor = mouse_cursor; self.xcb_connection.conn.flush();
} }
self.mouse_cursor = mouse_cursor;
} }
#[inline] #[inline]

View file

@ -1,7 +1,14 @@
/// A very light abstraction around the XCB connection. /// A very light abstraction around the XCB connection.
/// ///
/// Keeps track of the xcb connection itself and the xlib display ID that was used to connect. /// Keeps track of the xcb connection itself and the xlib display ID that was used to connect.
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::collections::HashMap;
use crate::MouseCursor;
use super::cursor;
pub(crate) struct Atoms { pub(crate) struct Atoms {
pub wm_protocols: Option<u32>, pub wm_protocols: Option<u32>,
@ -13,6 +20,8 @@ pub struct XcbConnection {
pub xlib_display: i32, pub xlib_display: i32,
pub(crate) atoms: Atoms, pub(crate) atoms: Atoms,
pub(super) cursor_cache: HashMap<MouseCursor, u32>,
} }
macro_rules! intern_atoms { macro_rules! intern_atoms {
@ -46,6 +55,8 @@ impl XcbConnection {
wm_protocols, wm_protocols,
wm_delete_window, wm_delete_window,
}, },
cursor_cache: HashMap::new()
}) })
} }
@ -132,4 +143,13 @@ impl XcbConnection {
self.get_scaling_xft() self.get_scaling_xft()
.or(self.get_scaling_screen_dimensions()) .or(self.get_scaling_screen_dimensions())
} }
#[inline]
pub fn get_cursor_xid(&mut self, cursor: MouseCursor) -> u32 {
let dpy = self.conn.get_raw_dpy();
*self.cursor_cache
.entry(cursor)
.or_insert_with(|| cursor::get_xcursor(dpy, cursor))
}
} }