srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/cursor.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-05-29 14:40:00 +0200
committersrdusr <[email protected]>2024-05-29 14:40:00 +0200
commit3d3057ae384ef7389284af8988410889e99c6bb9 (patch)
tree57b05e9660afd8ed51b26e5db1ab541a811ccc16 /crates/wayland/src/cursor.rs
parentdd31bf5ac2a692617d478478d36c733586b93cf8 (diff)
downloadsrdwm-3d3057ae384ef7389284af8988410889e99c6bb9.tar.gz
srdwm-3d3057ae384ef7389284af8988410889e99c6bb9.zip
Config at ~/.config/srd; cursor shapes, key repeat, pin, mouse defaults
Config path drops a level: ~/.config/srd, not ~/.config/srdwm/srd, which said the same thing twice. No other user-facing path had the same problem -- srdwm reads the config dir and writes nothing else. Cursor shapes. A client's own cursor surface is now rendered with the hotspot it declared, so an I-beam over text or a hand over a link shows the app's image instead of srdwm's arrow. The built-in arrow stays as the fallback when no client has set one, over decorations and the desktop. Named shapes still fall back to the arrow; most toolkits set a surface. Decorations and cursors now share one OverlayElement type, since render_output takes a single custom-element slice. Key repeat (srd.bind_repeat, Hyprland's binde). Held volume, brightness and switcher keys repeat at the seat's own rate rather than firing once. Driven from the poll loop, not a timer source: the winit backend has no calloop loop of its own, and poll_events already runs continuously in both backends. Repeat stops when *that* key is released, not when any key is. Always-on-top / pin, for the picture-in-picture and HUD rules that used it. Window::always_on_top was another declared-but-never-read field. Enforced in WindowManager's stacking order rather than at render time, so every consumer of stacking_order gets it and none can forget to honour it. Mouse-only window management, checked end to end: drag the titlebar to move, drag any edge or corner to resize, titlebar buttons to close/maximise/ minimise, click to focus, drag to a screen edge to snap, and now double-click the titlebar to maximise. The resize grab band went from 6px to 10px - a hairline is genuinely hard to hit with a mouse, which is why Hyprland ships extend_border_grab_area. Also removed the emoji status markers from docs/IMPLEMENTATION_STATUS.md.
Diffstat (limited to 'crates/wayland/src/cursor.rs')
-rw-r--r--crates/wayland/src/cursor.rs59
1 files changed, 46 insertions, 13 deletions
diff --git a/crates/wayland/src/cursor.rs b/crates/wayland/src/cursor.rs
index 121e8e1..b489c03 100644
--- a/crates/wayland/src/cursor.rs
+++ b/crates/wayland/src/cursor.rs
@@ -7,13 +7,19 @@
//! can move it, click with it, and drag windows with it, but you cannot see
//! where it is. That makes the udev backend unusable as a real session.
//!
-//! **Scope, stated plainly:** this draws one built-in arrow, always. It
-//! honours `CursorImageStatus::Hidden` (so a client that hides the pointer
-//! still gets its way), but it does *not* yet render a client's own cursor
-//! surface or a named shape - an app asking for an I-beam or a resize arrow
-//! still sees this arrow. That is a real limitation, and a visible one over
-//! text fields; it is also strictly better than the previous behaviour of
-//! drawing nothing at all.
+//! Two sources, in priority order:
+//!
+//! 1. **The client's own cursor surface** (`CursorImageStatus::Surface`) --
+//! a terminal's I-beam, a browser's hand, an app's resize arrows. Drawn
+//! from its surface tree, offset by the hotspot the client declared.
+//! 2. **A built-in arrow**, for when no client has set an image (over
+//! srdwm's own decorations and the desktop) or asked for a named shape
+//! we have no art for.
+//!
+//! `CursorImageStatus::Hidden` is honoured, so a client that hides the
+//! pointer still gets its way. Named shapes (`CursorIcon::Text` etc.) fall
+//! back to the arrow rather than being drawn as the requested shape - most
+//! toolkits set a surface rather than a name, so this is rarely visible.
//!
//! The built-in arrow is deliberate rather than loading an XCursor theme:
//! theme loading pulls in a dependency, needs a theme to actually be
@@ -21,8 +27,11 @@
//! is always present beats a prettier one that sometimes isn't there - the
//! same reasoning as `decoration.rs`'s font fallback.
+use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;
use smithay::utils::{Logical, Point};
+use crate::elements::OverlayElement;
+
/// Side length of the built-in cursor bitmap, in pixels.
pub(crate) const CURSOR_SIZE: i32 = 24;
@@ -85,6 +94,10 @@ pub(crate) fn arrow_bitmap() -> Vec<u8> {
}
+/// One cursor render element, whatever the source.
+///
+/// Client cursor surfaces and the built-in bitmap are different element
+/// types, so they're unified here rather than forcing both through one.
/// Render elements for the pointer, to be drawn above everything else.
///
/// `pos` is in the global space and `origin` is the output's origin, since
@@ -99,14 +112,17 @@ pub(crate) fn render_elements<R>(
pos: Point<f64, Logical>,
origin: Point<i32, Logical>,
size: (i32, i32),
-) -> Vec<smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement<R>>
+) -> Vec<OverlayElement<R>>
where
- R: smithay::backend::renderer::Renderer + smithay::backend::renderer::ImportMem,
+ R: smithay::backend::renderer::Renderer
+ + smithay::backend::renderer::ImportAll
+ + smithay::backend::renderer::ImportMem,
R::TextureId: Clone + Send + 'static,
{
- use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;
+ use smithay::backend::renderer::element::surface::render_elements_from_surface_tree;
use smithay::backend::renderer::element::Kind;
- use smithay::input::pointer::CursorImageStatus;
+ use smithay::input::pointer::{CursorImageStatus, CursorImageSurfaceData};
+ use smithay::wayland::compositor::with_states;
if matches!(status, CursorImageStatus::Hidden) {
return Vec::new();
@@ -117,10 +133,26 @@ where
return Vec::new();
}
- // Built-in arrow: hotspot is the tip, so no offset.
+ if let CursorImageStatus::Surface(surface) = status {
+ // The client picked an image. Its hotspot is the point *inside* that
+ // image which tracks the pointer, so the surface is drawn offset by
+ // it - without this the image sits down-right of where clicks land.
+ let hotspot = with_states(surface, |states| {
+ states
+ .data_map
+ .get::<CursorImageSurfaceData>()
+ .map(|d| d.lock().unwrap().hotspot)
+ .unwrap_or_default()
+ });
+ let at = (local.0 - hotspot.x, local.1 - hotspot.y);
+ return render_elements_from_surface_tree(renderer, surface, at, 1.0, 1.0, Kind::Cursor);
+ }
+
+ // No client image (or a named shape we don't have art for): the
+ // built-in arrow, whose hotspot is its tip, so no offset.
let at = (local.0 as f64, local.1 as f64);
match MemoryRenderBufferRenderElement::from_buffer(renderer, at, buffer, None, None, None, Kind::Cursor) {
- Ok(e) => vec![e],
+ Ok(e) => vec![OverlayElement::Memory(e)],
Err(e) => {
log::warn!("cursor: failed to import bitmap: {e}");
Vec::new()
@@ -128,6 +160,7 @@ where
}
}
+
/// The built-in arrow as an uploadable buffer. Built once at startup rather
/// than per frame - the bitmap never changes.
pub(crate) fn make_buffer() -> smithay::backend::renderer::element::memory::MemoryRenderBuffer {