Commit bb1e0d9edf31
tarball4 files changed+54-1
@@ -27,6 +27,31 @@ struct Prepared {
2727
hotspot: Point<i32, Physical>,2828
}2929
30
+const PRELOADED: [CursorIcon; 18] = [31
+ CursorIcon::Default,32
+ CursorIcon::Text,33
+ CursorIcon::Pointer,34
+ CursorIcon::Grab,35
+ CursorIcon::Grabbing,36
+ CursorIcon::NotAllowed,37
+ CursorIcon::Wait,38
+ CursorIcon::Progress,39
+ CursorIcon::EwResize,40
+ CursorIcon::NsResize,41
+ CursorIcon::NResize,42
+ CursorIcon::SResize,43
+ CursorIcon::EResize,44
+ CursorIcon::WResize,45
+ CursorIcon::NwResize,46
+ CursorIcon::NeResize,47
+ CursorIcon::SwResize,48
+ CursorIcon::SeResize,49
+];50
+51
+fn scale_step(scale: f64) -> u32 {52
+ (scale * 120.0).round() as u3253
+}54
+3055
pub struct Cursor {3156
theme: xcursor::CursorTheme,3257
size: u32,@@ -63,6 +88,18 @@ impl Cursor {
6388
.find_map(|n| load_file(&self.theme, n))6489
}6590
91
+ /// Reads the shapes clients ask for most at each of `scales`, so none is92
+ /// read from the theme inside a frame, and forgets other scales.93
+ pub fn preload(&mut self, renderer: &mut VulkanRenderer, scales: &[f64]) {94
+ let steps: Vec<u32> = scales.iter().map(|s| scale_step(*s)).collect();95
+ self.sized.retain(|(_, step), _| steps.contains(step));96
+ for &scale in scales {97
+ for icon in PRELOADED {98
+ self.sized(renderer, icon, scale);99
+ }100
+ }101
+ }102
+66103
/// The theme's image nearest the cursor size times `scale`, stretched to67104
/// exactly that size so it covers whole pixels. Only that image is kept,68105
/// and an upload that fails is retried on the next call.@@ -72,7 +109,7 @@ impl Cursor {
72109
icon: CursorIcon,73110
scale: f64,74111
) -> Option<&Prepared> {75
- let key = (icon, (scale * 120.0).round() as u32);112
+ let key = (icon, scale_step(scale));76113
if !self.sized.contains_key(&key) && !self.absent.contains(&icon) {77114
let Some(images) = self.read(icon) else {78115
self.absent.insert(icon);@@ -222,6 +222,7 @@ pub(super) fn connect(state: &mut State, connector: connector::Info, crtc: crtc:
222222
refresh_ns: 1_000_000_000_000 / u64::from(wl_mode.refresh.max(1) as u32),223223
},224224
);225
+ state.preload_cursors();225226
}226227
227228
pub(super) fn connector_name(connector: &connector::Info) -> String {@@ -428,6 +429,7 @@ pub fn reconfigure_outputs(state: &mut State) {
428429
);429430
}430431
if changed {432
+ state.preload_cursors();431433
state.reserve_layers();432434
state.reconfigure_lock();433435
state.apply_layout();@@ -181,6 +181,7 @@ pub fn init(
181181
next_feedback: 0,182182
awaiting_frame: Vec::new(),183183
}));184
+ state.preload_cursors();184185
queue.roundtrip(state)?;185186
queue.roundtrip(state)?;186187
WaylandSource::new(conn, queue).insert(event_loop.handle())?;@@ -217,6 +218,7 @@ pub fn reconfigure(state: &mut State) {
217218
);218219
state.wm.layout.set_output_area(o, area);219220
tracing::info!(scale, "output reconfigured");221
+ state.preload_cursors();220222
state.dirty.redraw = true;221223
state.reserve_layers();222224
state.reconfigure_lock();@@ -116,6 +116,18 @@ impl State {
116116
}117117
}118118
119
+ /// Readies the cursor shapes for every output's scale, for when an output120
+ /// appears or changes scale.121
+ pub fn preload_cursors(&mut self) {122
+ let scales: Vec<f64> = self123
+ .wm124
+ .space125
+ .outputs()126
+ .map(|o| o.current_scale().fractional_scale())127
+ .collect();128
+ self.input.cursor.preload(&mut self.renderer, &scales);129
+ }130
+119131
/// Retells the cursor and drag icon when the pointer crosses to an output.120132
pub fn follow_pointer_scale(&mut self) {121133
let under = self.pointer_output_ref();