@@ -32,12 +32,13 @@ use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS};
use crate::platform::linux::client::Client;
+use crate::platform::linux::wayland::cursor::Cursor;
use crate::platform::linux::wayland::window::{WaylandDecorationState, WaylandWindow};
use crate::platform::{LinuxPlatformInner, PlatformWindow};
use crate::{
- platform::linux::wayland::window::WaylandWindowState, AnyWindowHandle, DisplayId, KeyDownEvent,
- KeyUpEvent, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
- NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta,
+ platform::linux::wayland::window::WaylandWindowState, AnyWindowHandle, CursorStyle, DisplayId,
+ KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent,
+ MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta,
ScrollWheelEvent, TouchPhase, WindowOptions,
};
@@ -47,6 +48,7 @@ const MIN_KEYCODE: u32 = 8;
pub(crate) struct WaylandClientStateInner {
compositor: wl_compositor::WlCompositor,
wm_base: xdg_wm_base::XdgWmBase,
+ shm: wl_shm::WlShm,
viewporter: Option<wp_viewporter::WpViewporter>,
fractional_scale_manager: Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
@@ -63,8 +65,16 @@ pub(crate) struct WaylandClientStateInner {
loop_handle: Rc<LoopHandle<'static, ()>>,
}
+pub(crate) struct CursorState {
+ cursor_icon_name: String,
+ cursor: Option<Cursor>,
+}
+
#[derive(Clone)]
-pub(crate) struct WaylandClientState(Rc<RefCell<WaylandClientStateInner>>);
+pub(crate) struct WaylandClientState {
+ client_state_inner: Rc<RefCell<WaylandClientStateInner>>,
+ cursor_state: Rc<RefCell<CursorState>>,
+}
pub(crate) struct KeyRepeat {
characters_per_second: u32,
@@ -104,6 +114,7 @@ impl WaylandClient {
let mut state_inner = Rc::new(RefCell::new(WaylandClientStateInner {
compositor: globals.bind(&qh, 1..=1, ()).unwrap(),
wm_base: globals.bind(&qh, 1..=1, ()).unwrap(),
+ shm: globals.bind(&qh, 1..=1, ()).unwrap(),
viewporter: globals.bind(&qh, 1..=1, ()).ok(),
fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
@@ -131,9 +142,17 @@ impl WaylandClient {
loop_handle: Rc::clone(&linux_platform_inner.loop_handle),
}));
+ let mut cursor_state = Rc::new(RefCell::new(CursorState {
+ cursor_icon_name: "arrow".to_string(),
+ cursor: None,
+ }));
+
let source = WaylandSource::new(conn, event_queue);
- let mut state = WaylandClientState(Rc::clone(&state_inner));
+ let mut state = WaylandClientState {
+ client_state_inner: Rc::clone(&state_inner),
+ cursor_state: Rc::clone(&cursor_state),
+ };
linux_platform_inner
.loop_handle
.insert_source(source, move |_, queue, _| {
@@ -143,7 +162,10 @@ impl WaylandClient {
Self {
platform_inner: linux_platform_inner,
- state: WaylandClientState(state_inner),
+ state: WaylandClientState {
+ client_state_inner: state_inner,
+ cursor_state,
+ },
qh: Arc::new(qh),
}
}
@@ -163,7 +185,7 @@ impl Client for WaylandClient {
handle: AnyWindowHandle,
options: WindowOptions,
) -> Box<dyn PlatformWindow> {
- let mut state = self.state.0.borrow_mut();
+ let mut state = self.state.client_state_inner.borrow_mut();
let wl_surface = state.compositor.create_surface(&self.qh, ());
let xdg_surface = state.wm_base.get_xdg_surface(&wl_surface, &self.qh, ());
@@ -217,6 +239,32 @@ impl Client for WaylandClient {
state.windows.push((xdg_surface, Rc::clone(&window_state)));
Box::new(WaylandWindow(window_state))
}
+
+ fn set_cursor_style(&self, style: CursorStyle) {
+ let cursor_icon_name = match style {
+ CursorStyle::Arrow => "arrow".to_string(),
+ CursorStyle::IBeam => "text".to_string(),
+ CursorStyle::Crosshair => "crosshair".to_string(),
+ CursorStyle::ClosedHand => "grabbing".to_string(),
+ CursorStyle::OpenHand => "openhand".to_string(),
+ CursorStyle::PointingHand => "hand".to_string(),
+ CursorStyle::ResizeLeft => "w-resize".to_string(),
+ CursorStyle::ResizeRight => "e-resize".to_string(),
+ CursorStyle::ResizeLeftRight => "ew-resize".to_string(),
+ CursorStyle::ResizeUp => "n-resize".to_string(),
+ CursorStyle::ResizeDown => "s-resize".to_string(),
+ CursorStyle::ResizeUpDown => "ns-resize".to_string(),
+ CursorStyle::DisappearingItem => "grabbing".to_string(), // todo!(linux) - couldn't find equivalent icon in linux
+ CursorStyle::IBeamCursorForVerticalLayout => "vertical-text".to_string(),
+ CursorStyle::OperationNotAllowed => "not-allowed".to_string(),
+ CursorStyle::DragLink => "dnd-link".to_string(),
+ CursorStyle::DragCopy => "dnd-copy".to_string(),
+ CursorStyle::ContextualMenu => "context-menu".to_string(),
+ };
+
+ let mut cursor_state = self.state.cursor_state.borrow_mut();
+ cursor_state.cursor_icon_name = cursor_icon_name;
+ }
}
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientState {
@@ -263,7 +311,7 @@ impl Dispatch<WlCallback, Arc<WlSurface>> for WaylandClientState {
_: &Connection,
qh: &QueueHandle<Self>,
) {
- let mut state = state.0.borrow_mut();
+ let mut state = state.client_state_inner.borrow_mut();
if let wl_callback::Event::Done { .. } = event {
for window in &state.windows {
if window.1.surface.id() == surf.id() {
@@ -285,7 +333,7 @@ impl Dispatch<xdg_surface::XdgSurface, ()> for WaylandClientState {
_: &Connection,
_: &QueueHandle<Self>,
) {
- let mut state = state.0.borrow_mut();
+ let mut state = state.client_state_inner.borrow_mut();
if let xdg_surface::Event::Configure { serial, .. } = event {
xdg_surface.ack_configure(serial);
for window in &state.windows {
@@ -308,7 +356,7 @@ impl Dispatch<xdg_toplevel::XdgToplevel, ()> for WaylandClientState {
_: &Connection,
_: &QueueHandle<Self>,
) {
- let mut state = state.0.borrow_mut();
+ let mut state = state.client_state_inner.borrow_mut();
if let xdg_toplevel::Event::Configure {
width,
height,
@@ -386,7 +434,7 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientState {
conn: &Connection,
qh: &QueueHandle<Self>,
) {
- let mut state = this.0.borrow_mut();
+ let mut state = this.client_state_inner.borrow_mut();
match event {
wl_keyboard::Event::RepeatInfo { rate, delay } => {
state.repeat.characters_per_second = rate as u32;
@@ -497,7 +545,7 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientState {
let this = this.clone();
let timer = Timer::from_duration(delay);
- let state_ = Rc::clone(&this.0);
+ let state_ = Rc::clone(&this.client_state_inner);
state
.loop_handle
.insert_source(timer, move |event, _metadata, shared_data| {
@@ -567,9 +615,18 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientState {
conn: &Connection,
qh: &QueueHandle<Self>,
) {
- let mut state = state.0.borrow_mut();
+ let mut cursor_state = state.cursor_state.borrow_mut();
+ let mut state = state.client_state_inner.borrow_mut();
+
+ if cursor_state.cursor.is_none() {
+ cursor_state.cursor = Some(Cursor::new(&conn, &state.compositor, &qh, &state.shm, 24));
+ }
+ let cursor_icon_name = cursor_state.cursor_icon_name.clone();
+ let mut cursor: &mut Cursor = cursor_state.cursor.as_mut().unwrap();
+
match event {
wl_pointer::Event::Enter {
+ serial,
surface,
surface_x,
surface_y,
@@ -578,11 +635,14 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientState {
let mut mouse_focused_window = None;
for window in &state.windows {
if window.1.surface.id() == surface.id() {
+ window.1.set_focused(true);
mouse_focused_window = Some(Rc::clone(&window.1));
}
}
if mouse_focused_window.is_some() {
state.mouse_focused_window = mouse_focused_window;
+ cursor.set_serial_id(serial);
+ cursor.set_icon(&wl_pointer, cursor_icon_name);
}
state.mouse_location = Some(Point {
@@ -610,6 +670,7 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientState {
modifiers: state.modifiers,
}),
);
+ cursor.set_icon(&wl_pointer, cursor_icon_name);
}
wl_pointer::Event::Button {
button,
@@ -693,6 +754,7 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientState {
pressed_button: None,
modifiers: Modifiers::default(),
}));
+ focused_window.set_focused(false);
}
state.mouse_focused_window = None;
state.mouse_location = None;
@@ -711,7 +773,7 @@ impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for Wayland
_: &Connection,
_: &QueueHandle<Self>,
) {
- let mut state = state.0.borrow_mut();
+ let mut state = state.client_state_inner.borrow_mut();
if let wp_fractional_scale_v1::Event::PreferredScale { scale, .. } = event {
for window in &state.windows {
if window.0.id() == *id {
@@ -734,7 +796,7 @@ impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId>
_: &Connection,
_: &QueueHandle<Self>,
) {
- let mut state = state.0.borrow_mut();
+ let mut state = state.client_state_inner.borrow_mut();
if let zxdg_toplevel_decoration_v1::Event::Configure { mode, .. } = event {
for window in &state.windows {
if window.0.id() == *surface_id {
@@ -0,0 +1,67 @@
+use crate::platform::linux::wayland::WaylandClientState;
+use wayland_backend::client::InvalidId;
+use wayland_client::protocol::wl_compositor::WlCompositor;
+use wayland_client::protocol::wl_pointer::WlPointer;
+use wayland_client::protocol::wl_shm::WlShm;
+use wayland_client::protocol::wl_surface::WlSurface;
+use wayland_client::{Connection, QueueHandle};
+use wayland_cursor::{CursorImageBuffer, CursorTheme};
+
+pub(crate) struct Cursor {
+ theme: Result<CursorTheme, InvalidId>,
+ current_icon_name: String,
+ surface: WlSurface,
+ serial_id: u32,
+}
+
+impl Cursor {
+ pub fn new(
+ connection: &Connection,
+ compositor: &WlCompositor,
+ qh: &QueueHandle<WaylandClientState>,
+ shm: &WlShm,
+ size: u32,
+ ) -> Self {
+ Self {
+ theme: CursorTheme::load(&connection, shm.clone(), size),
+ current_icon_name: "".to_string(),
+ surface: compositor.create_surface(qh, ()),
+ serial_id: 0,
+ }
+ }
+
+ pub fn set_serial_id(&mut self, serial_id: u32) {
+ self.serial_id = serial_id;
+ }
+
+ pub fn set_icon(&mut self, wl_pointer: &WlPointer, cursor_icon_name: String) {
+ if self.current_icon_name != cursor_icon_name {
+ if self.theme.is_ok() {
+ if let Some(cursor) = self.theme.as_mut().unwrap().get_cursor(&cursor_icon_name) {
+ let buffer: &CursorImageBuffer = &cursor[0];
+ let (width, height) = buffer.dimensions();
+ let (hot_x, hot_y) = buffer.hotspot();
+
+ 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();
+
+ self.current_icon_name = cursor_icon_name;
+ } else {
+ log::warn!(
+ "Linux: Wayland: Unable to get cursor icon: {}",
+ cursor_icon_name
+ );
+ }
+ } else {
+ log::warn!("Linux: Wayland: Unable to load cursor themes");
+ }
+ }
+ }
+}