@@ -28,6 +28,7 @@ use futures::channel::oneshot;
use parking_lot::Mutex;
use time::UtcOffset;
use wayland_client::Connection;
+use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape;
use xkbcommon::xkb::{self, Keycode, Keysym, State};
use crate::platform::linux::wayland::WaylandClient;
@@ -501,6 +502,58 @@ pub(super) unsafe fn read_fd(mut fd: FileDescriptor) -> Result<String> {
Ok(result)
}
+impl CursorStyle {
+ pub(super) fn to_shape(&self) -> Shape {
+ match self {
+ CursorStyle::Arrow => Shape::Default,
+ CursorStyle::IBeam => Shape::Text,
+ CursorStyle::Crosshair => Shape::Crosshair,
+ CursorStyle::ClosedHand => Shape::Grabbing,
+ CursorStyle::OpenHand => Shape::Grab,
+ CursorStyle::PointingHand => Shape::Pointer,
+ CursorStyle::ResizeLeft => Shape::WResize,
+ CursorStyle::ResizeRight => Shape::EResize,
+ CursorStyle::ResizeLeftRight => Shape::EwResize,
+ CursorStyle::ResizeUp => Shape::NResize,
+ CursorStyle::ResizeDown => Shape::SResize,
+ CursorStyle::ResizeUpDown => Shape::NsResize,
+ CursorStyle::DisappearingItem => Shape::Grabbing, // todo(linux) - couldn't find equivalent icon in linux
+ CursorStyle::IBeamCursorForVerticalLayout => Shape::VerticalText,
+ CursorStyle::OperationNotAllowed => Shape::NotAllowed,
+ CursorStyle::DragLink => Shape::Alias,
+ CursorStyle::DragCopy => Shape::Copy,
+ CursorStyle::ContextualMenu => Shape::ContextMenu,
+ }
+ }
+
+ pub(super) fn to_icon_name(&self) -> String {
+ // Based on cursor names from https://gitlab.gnome.org/GNOME/adwaita-icon-theme (GNOME)
+ // and https://github.com/KDE/breeze (KDE). Both of them seem to be also derived from
+ // Web CSS cursor names: https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values
+ match self {
+ CursorStyle::Arrow => "arrow",
+ CursorStyle::IBeam => "text",
+ CursorStyle::Crosshair => "crosshair",
+ CursorStyle::ClosedHand => "grabbing",
+ CursorStyle::OpenHand => "grab",
+ CursorStyle::PointingHand => "pointer",
+ CursorStyle::ResizeLeft => "w-resize",
+ CursorStyle::ResizeRight => "e-resize",
+ CursorStyle::ResizeLeftRight => "ew-resize",
+ CursorStyle::ResizeUp => "n-resize",
+ CursorStyle::ResizeDown => "s-resize",
+ CursorStyle::ResizeUpDown => "ns-resize",
+ CursorStyle::DisappearingItem => "grabbing", // todo(linux) - couldn't find equivalent icon in linux
+ CursorStyle::IBeamCursorForVerticalLayout => "vertical-text",
+ CursorStyle::OperationNotAllowed => "not-allowed",
+ CursorStyle::DragLink => "alias",
+ CursorStyle::DragCopy => "copy",
+ CursorStyle::ContextualMenu => "context-menu",
+ }
+ .to_string()
+ }
+}
+
impl Keystroke {
pub(super) fn from_xkb(state: &State, modifiers: Modifiers, keycode: Keycode) -> Self {
let mut modifiers = modifiers;
@@ -34,6 +34,10 @@ use wayland_client::{
},
Connection, Dispatch, Proxy, QueueHandle,
};
+use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape;
+use wayland_protocols::wp::cursor_shape::v1::client::{
+ wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1,
+};
use wayland_protocols::wp::fractional_scale::v1::client::{
wp_fractional_scale_manager_v1, wp_fractional_scale_v1,
};
@@ -68,6 +72,7 @@ const MIN_KEYCODE: u32 = 8;
pub struct Globals {
pub qh: QueueHandle<WaylandClientStatePtr>,
pub compositor: wl_compositor::WlCompositor,
+ pub cursor_shape_manager: Option<wp_cursor_shape_manager_v1::WpCursorShapeManagerV1>,
pub data_device_manager: Option<wl_data_device_manager::WlDataDeviceManager>,
pub wm_base: xdg_wm_base::XdgWmBase,
pub shm: wl_shm::WlShm,
@@ -93,6 +98,7 @@ impl Globals {
(),
)
.unwrap(),
+ cursor_shape_manager: globals.bind(&qh, 1..=1, ()).ok(),
data_device_manager: globals
.bind(
&qh,
@@ -112,9 +118,11 @@ impl Globals {
}
pub(crate) struct WaylandClientState {
- serial: u32,
+ serial: u32, // todo(linux): storing a general serial is wrong
+ pointer_serial: u32,
globals: Globals,
wl_pointer: Option<wl_pointer::WlPointer>,
+ cursor_shape_device: Option<wp_cursor_shape_device_v1::WpCursorShapeDeviceV1>,
data_device: Option<wl_data_device::WlDataDevice>,
// Surface to Window mapping
windows: HashMap<ObjectId, WaylandWindowStatePtr>,
@@ -137,7 +145,7 @@ pub(crate) struct WaylandClientState {
mouse_focused_window: Option<WaylandWindowStatePtr>,
keyboard_focused_window: Option<WaylandWindowStatePtr>,
loop_handle: LoopHandle<'static, WaylandClientStatePtr>,
- cursor_icon_name: String,
+ cursor_style: Option<CursorStyle>,
cursor: Cursor,
clipboard: Option<Clipboard>,
primary: Option<Primary>,
@@ -197,6 +205,9 @@ impl WaylandClientStatePtr {
if let Some(wl_pointer) = &state.wl_pointer {
wl_pointer.release();
}
+ if let Some(cursor_shape_device) = &state.cursor_shape_device {
+ cursor_shape_device.destroy();
+ }
if let Some(data_device) = &state.data_device {
data_device.release();
}
@@ -289,8 +300,10 @@ impl WaylandClient {
let mut state = Rc::new(RefCell::new(WaylandClientState {
serial: 0,
+ pointer_serial: 0,
globals,
wl_pointer: None,
+ cursor_shape_device: None,
data_device,
output_scales: outputs,
windows: HashMap::default(),
@@ -330,8 +343,8 @@ impl WaylandClient {
mouse_focused_window: None,
keyboard_focused_window: None,
loop_handle: handle.clone(),
- cursor_icon_name: "arrow".to_string(),
enter_token: None,
+ cursor_style: None,
cursor,
clipboard: Some(clipboard),
primary: Some(primary),
@@ -375,39 +388,28 @@ impl LinuxClient for WaylandClient {
}
fn set_cursor_style(&self, style: CursorStyle) {
- // Based on cursor names from https://gitlab.gnome.org/GNOME/adwaita-icon-theme (GNOME)
- // and https://github.com/KDE/breeze (KDE). Both of them seem to be also derived from
- // Web CSS cursor names: https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values
- let cursor_icon_name = match style {
- CursorStyle::Arrow => "arrow",
- CursorStyle::IBeam => "text",
- CursorStyle::Crosshair => "crosshair",
- CursorStyle::ClosedHand => "grabbing",
- CursorStyle::OpenHand => "grab",
- CursorStyle::PointingHand => "pointer",
- CursorStyle::ResizeLeft => "w-resize",
- CursorStyle::ResizeRight => "e-resize",
- CursorStyle::ResizeLeftRight => "ew-resize",
- CursorStyle::ResizeUp => "n-resize",
- CursorStyle::ResizeDown => "s-resize",
- CursorStyle::ResizeUpDown => "ns-resize",
- CursorStyle::DisappearingItem => "grabbing", // todo(linux) - couldn't find equivalent icon in linux
- CursorStyle::IBeamCursorForVerticalLayout => "vertical-text",
- CursorStyle::OperationNotAllowed => "not-allowed",
- CursorStyle::DragLink => "alias",
- CursorStyle::DragCopy => "copy",
- CursorStyle::ContextualMenu => "context-menu",
- }
- .to_string();
-
let mut state = self.0.borrow_mut();
- state.cursor_icon_name = cursor_icon_name.clone();
- if state.mouse_focused_window.is_some() {
- let wl_pointer = state
- .wl_pointer
- .clone()
- .expect("window is focused by pointer");
- state.cursor.set_icon(&wl_pointer, &cursor_icon_name);
+
+ let need_update = state
+ .cursor_style
+ .map_or(true, |current_style| current_style != style);
+
+ if need_update {
+ let serial = state.pointer_serial;
+ state.cursor_style = Some(style);
+
+ if let Some(cursor_shape_device) = &state.cursor_shape_device {
+ cursor_shape_device.set_shape(serial, style.to_shape());
+ } else if state.mouse_focused_window.is_some() {
+ // cursor-shape-v1 isn't supported, set the cursor using a surface.
+ let wl_pointer = state
+ .wl_pointer
+ .clone()
+ .expect("window is focused by pointer");
+ state
+ .cursor
+ .set_icon(&wl_pointer, serial, &style.to_icon_name());
+ }
}
}
@@ -516,6 +518,8 @@ impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStat
}
delegate_noop!(WaylandClientStatePtr: ignore wl_compositor::WlCompositor);
+delegate_noop!(WaylandClientStatePtr: ignore wp_cursor_shape_device_v1::WpCursorShapeDeviceV1);
+delegate_noop!(WaylandClientStatePtr: ignore wp_cursor_shape_manager_v1::WpCursorShapeManagerV1);
delegate_noop!(WaylandClientStatePtr: ignore wl_data_device_manager::WlDataDeviceManager);
delegate_noop!(WaylandClientStatePtr: ignore wl_shm::WlShm);
delegate_noop!(WaylandClientStatePtr: ignore wl_shm_pool::WlShmPool);
@@ -684,7 +688,13 @@ impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientStatePtr {
if capabilities.contains(wl_seat::Capability::Pointer) {
let client = state.get_client();
let mut state = client.borrow_mut();
- state.wl_pointer = Some(seat.get_pointer(qh, ()));
+ let pointer = seat.get_pointer(qh, ());
+ state.cursor_shape_device = state
+ .globals
+ .cursor_shape_manager
+ .as_ref()
+ .map(|cursor_shape_manager| cursor_shape_manager.get_pointer(&pointer, qh, ()));
+ state.wl_pointer = Some(pointer);
}
}
}
@@ -889,7 +899,6 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
) {
let mut client = this.get_client();
let mut state = client.borrow_mut();
- let cursor_icon_name = state.cursor_icon_name.clone();
match event {
wl_pointer::Event::Enter {
@@ -899,17 +908,21 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
surface_y,
..
} => {
- state.serial = serial;
+ state.pointer_serial = serial;
state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
if let Some(window) = get_window(&mut state, &surface.id()) {
state.enter_token = Some(());
state.mouse_focused_window = Some(window.clone());
- state.cursor.mark_dirty();
- state.cursor.set_serial_id(serial);
- state
- .cursor
- .set_icon(&wl_pointer, cursor_icon_name.as_str());
+ if let Some(style) = state.cursor_style {
+ if let Some(cursor_shape_device) = &state.cursor_shape_device {
+ cursor_shape_device.set_shape(serial, style.to_shape());
+ } else {
+ state
+ .cursor
+ .set_icon(&wl_pointer, serial, &style.to_icon_name());
+ }
+ }
drop(state);
window.set_focused(true);
}
@@ -8,9 +8,7 @@ use wayland_cursor::{CursorImageBuffer, CursorTheme};
pub(crate) struct Cursor {
theme: Option<CursorTheme>,
- current_icon_name: Option<String>,
surface: WlSurface,
- serial_id: u32,
}
impl Drop for Cursor {
@@ -24,65 +22,39 @@ impl Cursor {
pub fn new(connection: &Connection, globals: &Globals, size: u32) -> Self {
Self {
theme: CursorTheme::load(&connection, globals.shm.clone(), size).log_err(),
- current_icon_name: None,
surface: globals.compositor.create_surface(&globals.qh, ()),
- serial_id: 0,
}
}
- pub fn mark_dirty(&mut self) {
- self.current_icon_name = None;
- }
-
- pub fn set_serial_id(&mut self, serial_id: u32) {
- self.serial_id = serial_id;
- }
-
- pub fn set_icon(&mut self, wl_pointer: &WlPointer, mut cursor_icon_name: &str) {
- let need_update = self
- .current_icon_name
- .as_ref()
- .map_or(true, |current_icon_name| {
- current_icon_name != cursor_icon_name
- });
-
- if need_update {
- if let Some(theme) = &mut self.theme {
- let mut buffer: Option<&CursorImageBuffer>;
-
- if let Some(cursor) = theme.get_cursor(&cursor_icon_name) {
- buffer = Some(&cursor[0]);
- } else if let Some(cursor) = theme.get_cursor("default") {
- buffer = Some(&cursor[0]);
- cursor_icon_name = "default";
- log::warn!(
- "Linux: Wayland: Unable to get cursor icon: {}. Using default cursor icon",
- cursor_icon_name
- );
- } else {
- buffer = None;
- log::warn!("Linux: Wayland: Unable to get default cursor too!");
- }
-
- if let Some(buffer) = &mut buffer {
- let (width, height) = buffer.dimensions();
- let (hot_x, hot_y) = buffer.hotspot();
+ pub fn set_icon(&mut self, wl_pointer: &WlPointer, serial_id: u32, mut cursor_icon_name: &str) {
+ if let Some(theme) = &mut self.theme {
+ let mut buffer: Option<&CursorImageBuffer>;
+
+ if let Some(cursor) = theme.get_cursor(&cursor_icon_name) {
+ buffer = Some(&cursor[0]);
+ } else if let Some(cursor) = theme.get_cursor("default") {
+ buffer = Some(&cursor[0]);
+ cursor_icon_name = "default";
+ log::warn!(
+ "Linux: Wayland: Unable to get cursor icon: {}. Using default cursor icon",
+ cursor_icon_name
+ );
+ } else {
+ buffer = None;
+ log::warn!("Linux: Wayland: Unable to get default cursor too!");
+ }
- wl_pointer.set_cursor(
- self.serial_id,
- Some(&self.surface),
- hot_x as i32,
- hot_y as i32,
- );
- self.surface.attach(Some(&buffer), 0, 0);
- self.surface.damage(0, 0, width as i32, height as i32);
- self.surface.commit();
+ if let Some(buffer) = &mut buffer {
+ let (width, height) = buffer.dimensions();
+ let (hot_x, hot_y) = buffer.hotspot();
- self.current_icon_name = Some(cursor_icon_name.to_string());
- }
- } else {
- log::warn!("Linux: Wayland: Unable to load cursor themes");
+ wl_pointer.set_cursor(serial_id, Some(&self.surface), hot_x as i32, hot_y as i32);
+ self.surface.attach(Some(&buffer), 0, 0);
+ self.surface.damage(0, 0, width as i32, height as i32);
+ self.surface.commit();
}
+ } else {
+ log::warn!("Linux: Wayland: Unable to load cursor themes");
}
}
}