Skip to content

Commit 26fe07a

Browse files
committed
seat/pointer: simplify ThemedPointer handling
This should simplify the handling of custom cursors in the user applications by incapsulating all the necessary state in the ThemedPointer. The new cursor-icon crate for better compatibility across the ecosystem, so the icon could be passed as is. In the future we can automatically handle the `wp_cursor_shape` under the hood.
1 parent c0eabf6 commit 26fe07a

File tree

7 files changed

+236
-112
lines changed

7 files changed

+236
-112
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
## Unreleased
44

5+
#### Breaking Changes
6+
7+
- `ThemedPointer::set_cursor` now takes only `Connection` and `&str`.
8+
- `SeatState:get_pointer_with_them*` now takes `Shm` and `WlSurface` for the themed cursor.
9+
- `ThemedPointer` now automatically releases the associated `WlPointer`.
10+
- `CursorIcon` from `cursor-icon` crate is now used for `set_cursor` and `Frame`.
11+
512
## 0.17.0 - 2023-03-28
613

714
#### Breaking Changes

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,18 @@ rustdoc-args = ["--cfg", "docsrs"]
1717

1818
[dependencies]
1919
bitflags = "1.0"
20-
nix = { version = "0.26.1", default-features = false, features = ["fs", "mman"] }
20+
cursor-icon = "1.0.0"
2121
dlib = "0.5"
2222
lazy_static = "1.0"
23-
memmap2 = "0.5.0"
2423
log = "0.4"
24+
memmap2 = "0.5.0"
25+
nix = { version = "0.26.1", default-features = false, features = ["fs", "mman"] }
2526
thiserror = "1.0.30"
2627
wayland-backend = "0.1.0"
2728
wayland-client = "0.30.1"
29+
wayland-cursor = "0.30.0"
2830
wayland-protocols = { version = "0.30.0", features = ["client", "unstable"] }
2931
wayland-protocols-wlr = { version = "0.1.0", features = ["client"] }
30-
wayland-cursor = "0.30.0"
3132
wayland-scanner = "0.30.0"
3233

3334
xkbcommon = { version = "0.5", optional = true, features = ["wayland"] }

examples/themed_window.rs

Lines changed: 78 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::sync::Arc;
22
use std::{convert::TryInto, num::NonZeroU32};
33

4+
use smithay_client_toolkit::seat::keyboard::keysyms;
45
use smithay_client_toolkit::{
56
compositor::{CompositorHandler, CompositorState},
67
delegate_compositor, delegate_keyboard, delegate_output, delegate_pointer, delegate_registry,
@@ -11,7 +12,8 @@ use smithay_client_toolkit::{
1112
seat::{
1213
keyboard::{KeyEvent, KeyboardHandler, Modifiers},
1314
pointer::{
14-
PointerData, PointerEvent, PointerEventKind, PointerHandler, ThemeSpec, ThemedPointer,
15+
CursorIcon, PointerData, PointerEvent, PointerEventKind, PointerHandler, ThemeSpec,
16+
ThemedPointer,
1517
},
1618
Capability, SeatHandler, SeatState,
1719
},
@@ -36,6 +38,44 @@ use wayland_client::{
3638
Connection, Proxy, QueueHandle,
3739
};
3840

41+
// Cursor shapes.
42+
const CURSORS: &[CursorIcon] = &[
43+
CursorIcon::Default,
44+
CursorIcon::Crosshair,
45+
CursorIcon::Pointer,
46+
CursorIcon::Move,
47+
CursorIcon::Text,
48+
CursorIcon::Wait,
49+
CursorIcon::Help,
50+
CursorIcon::Progress,
51+
CursorIcon::NotAllowed,
52+
CursorIcon::ContextMenu,
53+
CursorIcon::Cell,
54+
CursorIcon::VerticalText,
55+
CursorIcon::Alias,
56+
CursorIcon::Copy,
57+
CursorIcon::NoDrop,
58+
CursorIcon::Grab,
59+
CursorIcon::Grabbing,
60+
CursorIcon::AllScroll,
61+
CursorIcon::ZoomIn,
62+
CursorIcon::ZoomOut,
63+
CursorIcon::EResize,
64+
CursorIcon::NResize,
65+
CursorIcon::NeResize,
66+
CursorIcon::NwResize,
67+
CursorIcon::SResize,
68+
CursorIcon::SeResize,
69+
CursorIcon::SwResize,
70+
CursorIcon::WResize,
71+
CursorIcon::EwResize,
72+
CursorIcon::NsResize,
73+
CursorIcon::NeswResize,
74+
CursorIcon::NwseResize,
75+
CursorIcon::ColResize,
76+
CursorIcon::RowResize,
77+
];
78+
3979
fn main() {
4080
env_logger::init();
4181

@@ -60,7 +100,6 @@ fn main() {
60100
.expect("Failed to create pool");
61101

62102
let window_surface = compositor_state.create_surface(&qh);
63-
let pointer_surface = compositor_state.create_surface(&qh);
64103

65104
let window =
66105
xdg_shell_state.create_window(window_surface, WindowDecorations::ServerDefault, &qh);
@@ -76,11 +115,13 @@ fn main() {
76115
// the correct options.
77116
window.commit();
78117

118+
println!("Press `n` to cycle through cursor icons.");
119+
79120
let mut simple_window = SimpleWindow {
80121
registry_state,
81122
seat_state,
82123
output_state,
83-
_compositor_state: compositor_state,
124+
compositor_state,
84125
subcompositor_state: Arc::new(subcompositor_state),
85126
shm_state,
86127
_xdg_shell_state: xdg_shell_state,
@@ -94,21 +135,20 @@ fn main() {
94135
buffer: None,
95136
window,
96137
window_frame: None,
97-
pointer_surface,
98138
keyboard: None,
99139
keyboard_focus: false,
100140
themed_pointer: None,
101141
set_cursor: false,
102-
cursor_icon: String::from("diamond_cross"),
142+
window_cursor_icon_idx: 0,
143+
decorations_cursor: None,
103144
};
104145

105146
// We don't draw immediately, the configure will notify us when to first draw.
106-
107147
loop {
108148
event_queue.blocking_dispatch(&mut simple_window).unwrap();
109149

110150
if simple_window.exit {
111-
println!("exiting example");
151+
println!("Exiting example.");
112152
break;
113153
}
114154
}
@@ -118,7 +158,7 @@ struct SimpleWindow {
118158
registry_state: RegistryState,
119159
seat_state: SeatState,
120160
output_state: OutputState,
121-
_compositor_state: CompositorState,
161+
compositor_state: CompositorState,
122162
subcompositor_state: Arc<SubcompositorState>,
123163
shm_state: Shm,
124164
_xdg_shell_state: XdgShell,
@@ -132,12 +172,12 @@ struct SimpleWindow {
132172
buffer: Option<Buffer>,
133173
window: Window,
134174
window_frame: Option<FallbackFrame<Self>>,
135-
pointer_surface: wl_surface::WlSurface,
136175
keyboard: Option<wl_keyboard::WlKeyboard>,
137176
keyboard_focus: bool,
138177
themed_pointer: Option<ThemedPointer>,
139178
set_cursor: bool,
140-
cursor_icon: String,
179+
window_cursor_icon_idx: usize,
180+
decorations_cursor: Option<CursorIcon>,
141181
}
142182

143183
impl CompositorHandler for SimpleWindow {
@@ -247,7 +287,7 @@ impl WindowHandler for SimpleWindow {
247287
let width = width.unwrap_or(NonZeroU32::new(1).unwrap());
248288
let height = height.unwrap_or(NonZeroU32::new(1).unwrap());
249289

250-
println!("{width}, {height}");
290+
println!("New dimentions: {width}, {height}");
251291
window_frame.resize(width, height);
252292

253293
let (x, y) = window_frame.location();
@@ -311,10 +351,16 @@ impl SeatHandler for SimpleWindow {
311351

312352
if capability == Capability::Pointer && self.themed_pointer.is_none() {
313353
println!("Set pointer capability");
314-
println!("Creating pointer theme");
354+
let surface = self.compositor_state.create_surface(qh);
315355
let themed_pointer = self
316356
.seat_state
317-
.get_pointer_with_theme(qh, &seat, ThemeSpec::default())
357+
.get_pointer_with_theme(
358+
qh,
359+
&seat,
360+
self.shm_state.wl_shm(),
361+
surface,
362+
ThemeSpec::default(),
363+
)
318364
.expect("Failed to create pointer");
319365
self.themed_pointer.replace(themed_pointer);
320366
}
@@ -367,7 +413,6 @@ impl KeyboardHandler for SimpleWindow {
367413
_: u32,
368414
) {
369415
if self.window.wl_surface() == surface {
370-
println!("Release keyboard focus on window");
371416
self.keyboard_focus = false;
372417
}
373418
}
@@ -380,7 +425,12 @@ impl KeyboardHandler for SimpleWindow {
380425
_: u32,
381426
event: KeyEvent,
382427
) {
383-
println!("Key press: {event:?}");
428+
if event.keysym == keysyms::XKB_KEY_n {
429+
// Cycle through cursor icons.
430+
self.window_cursor_icon_idx = (self.window_cursor_icon_idx + 1) % CURSORS.len();
431+
println!("Setting cursor icon to: {}", CURSORS[self.window_cursor_icon_idx].name());
432+
self.set_cursor = true;
433+
}
384434
}
385435

386436
fn release_key(
@@ -389,9 +439,8 @@ impl KeyboardHandler for SimpleWindow {
389439
_: &QueueHandle<Self>,
390440
_: &wl_keyboard::WlKeyboard,
391441
_: u32,
392-
event: KeyEvent,
442+
_: KeyEvent,
393443
) {
394-
println!("Key release: {event:?}");
395444
}
396445

397446
fn update_modifiers(
@@ -400,9 +449,8 @@ impl KeyboardHandler for SimpleWindow {
400449
_: &QueueHandle<Self>,
401450
_: &wl_keyboard::WlKeyboard,
402451
_serial: u32,
403-
modifiers: Modifiers,
452+
_: Modifiers,
404453
) {
405-
println!("Update modifiers: {modifiers:?}");
406454
}
407455
}
408456

@@ -420,24 +468,17 @@ impl PointerHandler for SimpleWindow {
420468
match event.kind {
421469
Enter { .. } => {
422470
self.set_cursor = true;
423-
self.cursor_icon = self
471+
self.decorations_cursor = self
424472
.window_frame
425473
.as_mut()
426-
.and_then(|frame| frame.click_point_moved(&event.surface, x, y))
427-
.unwrap_or("diamond_cross")
428-
.to_owned();
429-
430-
if &event.surface == self.window.wl_surface() {
431-
println!("Pointer entered @{:?}", event.position);
432-
}
474+
.and_then(|frame| frame.click_point_moved(&event.surface, x, y));
433475
}
434476
Leave { .. } => {
435477
if &event.surface != self.window.wl_surface() {
436478
if let Some(window_frame) = self.window_frame.as_mut() {
437479
window_frame.click_point_left();
438480
}
439481
}
440-
println!("Pointer left");
441482
}
442483
Motion { .. } => {
443484
if let Some(new_cursor) = self
@@ -446,11 +487,11 @@ impl PointerHandler for SimpleWindow {
446487
.and_then(|frame| frame.click_point_moved(&event.surface, x, y))
447488
{
448489
self.set_cursor = true;
449-
self.cursor_icon = new_cursor.to_owned();
490+
self.decorations_cursor = Some(new_cursor);
450491
}
451492
}
452493
Press { button, serial, .. } | Release { button, serial, .. } => {
453-
let pressed = if matches!(event.kind, Press { .. }) { true } else { false };
494+
let pressed = matches!(event.kind, Press { .. });
454495
if &event.surface != self.window.wl_surface() {
455496
let click = match button {
456497
0x110 => FrameClick::Normal,
@@ -466,19 +507,15 @@ impl PointerHandler for SimpleWindow {
466507
self.frame_action(pointer, serial, action);
467508
}
468509
} else if pressed {
469-
println!("Press {:x} @ {:?}", button, event.position);
470510
self.shift = self.shift.xor(Some(0));
471511
}
472512
}
473-
Axis { horizontal, vertical, .. } => {
474-
if &event.surface == self.window.wl_surface() {
475-
println!("Scroll H:{horizontal:?}, V:{vertical:?}");
476-
}
477-
}
513+
Axis { .. } => {}
478514
}
479515
}
480516
}
481517
}
518+
482519
impl SimpleWindow {
483520
fn frame_action(&mut self, pointer: &wl_pointer::WlPointer, serial: u32, action: FrameAction) {
484521
let pointer_data = pointer.data::<PointerData>().unwrap();
@@ -504,13 +541,9 @@ impl ShmHandler for SimpleWindow {
504541
impl SimpleWindow {
505542
pub fn draw(&mut self, conn: &Connection, qh: &QueueHandle<Self>) {
506543
if self.set_cursor {
507-
let _ = self.themed_pointer.as_mut().unwrap().set_cursor(
508-
conn,
509-
&self.cursor_icon,
510-
self.shm_state.wl_shm(),
511-
&self.pointer_surface,
512-
1,
513-
);
544+
let cursor_icon =
545+
self.decorations_cursor.unwrap_or(CURSORS[self.window_cursor_icon_idx]);
546+
let _ = self.themed_pointer.as_mut().unwrap().set_cursor(conn, cursor_icon);
514547
self.set_cursor = false;
515548
}
516549

@@ -562,11 +595,11 @@ impl SimpleWindow {
562595
}
563596

564597
// Draw the decorations frame.
565-
self.window_frame.as_mut().map(|frame| {
598+
if let Some(frame) = self.window_frame.as_mut() {
566599
if frame.is_dirty() && !frame.is_hidden() {
567600
frame.draw();
568601
}
569-
});
602+
}
570603

571604
// Damage the entire window
572605
self.window.wl_surface().damage_buffer(0, 0, width as i32, height as i32);

0 commit comments

Comments
 (0)