linux: Fix mouse cursor size and blur on Wayland (#21373)
Closes #15788, #13258 This is a long-standing issue with a few previous attempts to fix it, such as [this one](https://github.com/zed-industries/zed/pull/17496). However, that fix was later reverted because it resolved the blur issue but caused a size issue. Currently, both blur and size issues persist when you set a custom cursor size from GNOME Settings and use fractional scaling. This PR addresses both issues. --- ### Context A new Wayland protocol, [cursor-shape-v1](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/194), allows the compositor to handle rendering the cursor at the correct size and shape. This protocol is implemented by KDE, wlroots (Sway-like environments), etc. Zed supports this protocol, so there are no issues on these desktop environments. However, GNOME has not yet [adopted](https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6212) this protocol. As a result, apps must fall back to manually rendering the cursor by specifying the theme, size, scale, etc., themselves. Zed also implements this fallback but does not correctly account for the display scale. --- ### Scale Fix For example, if your cursor size is `64px` and you’re using fractional scaling (e.g., `150%`), the display scale reported by the window query will be an integer value, `2` in this case. Why `2` if the scale is `150%`? That’s what the new protocol aims to improve. However, since GNOME Wayland uses this integer scale everywhere, it’s sufficient for our use case. To fix the issue, we set the `buffer_scale` to this value. But that alone doesn’t solve the problem. We also need to generate a matching theme cursor size for this scaled version. This can be calculated as `64px` * `2`, resulting in `128px` as the theme cursor size. --- ### Size Fix The XDG Desktop Portal’s `cursor-size` event fails to read the cursor size because it expects an `i32` but encounters a type error with `u32`. Due to this, the cursor size was interpreted as the default `24px` instead of the actual size set via user. --- ### Tested This fix has been tested with all possible combinations of the following: - [x] GNOME Normal Scale (100%, 200%, etc.) - [x] GNOME Fractional Scaling (125%, 150%, etc.) - [x] GNOME Cursor Sizes (**Settings > Accessibility > Seeing**, e.g., `24px`, `64px`, etc.) - [x] GNOME Experimental Feature `scale-monitor-framebuffer` (both enabled and disabled) - [x] KDE (`cursor-shape-v1` protocol) --- **Result:** 64px custom cursor size + 150% Fractional Scale: https://github.com/user-attachments/assets/cf3b1a0f-9a25-45d0-ab03-75059d3305e7 --- Release Notes: - Fixed mouse cursor size and blur issues on Wayland
This commit is contained in:
parent
fd71801346
commit
d609931e1c
4 changed files with 79 additions and 43 deletions
|
@ -194,6 +194,23 @@ impl WaylandWindowState {
|
|||
self.decorations == WindowDecorations::Client
|
||||
|| self.background_appearance != WindowBackgroundAppearance::Opaque
|
||||
}
|
||||
|
||||
pub fn primary_output_scale(&mut self) -> i32 {
|
||||
let mut scale = 1;
|
||||
let mut current_output = self.display.take();
|
||||
for (id, output) in self.outputs.iter() {
|
||||
if let Some((_, output_data)) = ¤t_output {
|
||||
if output.scale > output_data.scale {
|
||||
current_output = Some((id.clone(), output.clone()));
|
||||
}
|
||||
} else {
|
||||
current_output = Some((id.clone(), output.clone()));
|
||||
}
|
||||
scale = scale.max(output.scale);
|
||||
}
|
||||
self.display = current_output;
|
||||
scale
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WaylandWindow(pub WaylandWindowStatePtr);
|
||||
|
@ -560,7 +577,7 @@ impl WaylandWindowStatePtr {
|
|||
|
||||
state.outputs.insert(id, output.clone());
|
||||
|
||||
let scale = primary_output_scale(&mut state);
|
||||
let scale = state.primary_output_scale();
|
||||
|
||||
// We use `PreferredBufferScale` instead to set the scale if it's available
|
||||
if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
|
||||
|
@ -572,7 +589,7 @@ impl WaylandWindowStatePtr {
|
|||
wl_surface::Event::Leave { output } => {
|
||||
state.outputs.remove(&output.id());
|
||||
|
||||
let scale = primary_output_scale(&mut state);
|
||||
let scale = state.primary_output_scale();
|
||||
|
||||
// We use `PreferredBufferScale` instead to set the scale if it's available
|
||||
if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
|
||||
|
@ -719,6 +736,10 @@ impl WaylandWindowStatePtr {
|
|||
(fun)()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn primary_output_scale(&self) -> i32 {
|
||||
self.state.borrow_mut().primary_output_scale()
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_states<'a, S: TryFrom<u32> + 'a>(states: &'a [u8]) -> impl Iterator<Item = S> + 'a
|
||||
|
@ -732,23 +753,6 @@ where
|
|||
.flat_map(S::try_from)
|
||||
}
|
||||
|
||||
fn primary_output_scale(state: &mut RefMut<WaylandWindowState>) -> i32 {
|
||||
let mut scale = 1;
|
||||
let mut current_output = state.display.take();
|
||||
for (id, output) in state.outputs.iter() {
|
||||
if let Some((_, output_data)) = ¤t_output {
|
||||
if output.scale > output_data.scale {
|
||||
current_output = Some((id.clone(), output.clone()));
|
||||
}
|
||||
} else {
|
||||
current_output = Some((id.clone(), output.clone()));
|
||||
}
|
||||
scale = scale.max(output.scale);
|
||||
}
|
||||
state.display = current_output;
|
||||
scale
|
||||
}
|
||||
|
||||
impl rwh::HasWindowHandle for WaylandWindow {
|
||||
fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
|
||||
unimplemented!()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue