Skip to content

Change all *LoadImage(..., size) APIs to always return a cursor with the requested size.

Jin Liu requested to merge jinliu/libxcursor:work/jinliu/scaling into master

This MR changes various *LoadImage(s) APIs that takes a size parameter, from returning the closest match, to returning the closest match scaled to the requested nominal size.

*LoadAllImages() are not changed. They still only return the sizes present in the theme.

Rationale

Callers of these APIs (especially on Wayland) currently uses different strategies to scale the returned cursor to the size set by the user, if the returned size does not match the requested size. This results in inconsistent cursor sizes (due to some toolkits do the scaling while others don't) and looks (due to different scaling algorithms and using different sizes as the source) across different apps and toolkits. Having the cursor scaled in libXcursor will skip app's own scaling algorithm and guarantee a consistent look, which is what the user expects.

The situation is especially bad on Wayland. On X11, the user sets XCURSOR_SIZE to a value which, in most cases, is available in the cursor theme, so apps load cursors in this size and display them without further processing. On Wayland, however, different screens can have different scaling factor, and apps load cursors in size user_set_cursor_size * scaling_factor. Also, not all Wayland apps support fractional scale, so if the scaling factor is 2.5x, some apps would request cursors in size user_set_cursor_size * 2.5, while others user_set_cursor_size * 3 (the compositor then scales the latter back to 2.5). This makes it more likely that the cursor theme doesn't provide cursors in app-requested size (themes like Adwaita only has 5 sizes, while there can be more than 10 fractional scaling factors). And then apps do (or not do) scaling by themselves, resulting in inconsistent cursors.

The situation can be improved by:

  1. Wayland apps can use the "cursor shape" Wayland protocol to ask the compositor to draw the cursor for them. But this protocol hasn't been universally adopted. And X11 apps running under Wayland would still need to draw cursors in a size that might not be available in the theme, in order to have consistent cursors with Wayland apps.
  2. Convincing all apps / toolkits to use the same scaling algorithm. But such a "standard" algorithm must be defined first. Which leads to:
  3. Do the scaling in libXcursor, as proposed in this MR. The scaling code in apps / toolkits would become dead code, as they will always get the cursor in the requested size. They can then remove these dead code, but it doesn't matter.

Impact

This looks like a pretty disruptive change. But:

  1. In X11 sessions, the cursor size is very likely to be set to a value available in the theme. So no change in this case.
  2. If the cursor size is not set, so it's automatically determined by the DPI; or it's set to a value unavailable in the theme, then it changes the behavior, but I'd argue that it's for good, as the cursor is more accurate in size as intended.
  3. In Wayland sessions, the requested cursor size is more likely to be unavailable in the theme. Scaling in libXcursor would guarantee cursors in the same size across X11 and Wayland apps.

This change needs to be synchronized to libraries (libxcb-cursor, wayland), toolkits (GTK), window managers / Wayland compositors (i3, wlroots) who have a (modified) copy of libXcursor source code, in order to have a fully consistent cursor size across all apps. But this MR can be a first step, as I suppose their copies are all originated from this project.

Demo

Here's the same Adwaita cursors, size 48, scaling factor 2.5x, in KDE Plasma 6.2 Wayland session: (left to right: KWrite (wayland), xterm, xterm (with this MR))

cursors

Merge request reports

Loading