diff --git a/intern/ghost/intern/GHOST_SystemWayland.cc b/intern/ghost/intern/GHOST_SystemWayland.cc index 814b1710792..7af2549b9f2 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cc +++ b/intern/ghost/intern/GHOST_SystemWayland.cc @@ -8648,7 +8648,48 @@ GHOST_TSuccess GHOST_SystemWayland::cursor_shape_custom_set(const uint8_t *bitma cursor->visible = true; cursor->is_custom = true; - cursor->custom_scale = 1; /* TODO: support Hi-DPI custom cursors. */ + + /* Calculate the cursor size to use based on the theme setting. */ + { + + /* WARNING: Weak logic, if we can't use vector cursors - ideally the custom cursor + * function would receive multiple sizes which WAYLAND could then switch between + * as it does with themes. The following logic is fairly weak but works perfectly + * when all outputs have the same scale. + * + * There is nothing preventing multiple sized cursors from being passed in, + * it's just a matter of refactoring and adding support to WAYLAND. */ + + /* Get the lowest scale so in the case of mixed-scale-outputs, + * the cursor will be too big on some of the outputs instead of too small. + * + * Note that getting the min/max scale for all outputs be made into an function + * however it's bad practice because it means the cursor size will be wrong + * when there are multiple outputs with different scale. + * So this is not something to encouraged. */ + int output_scale = -1; + for (const GWL_Output *output : display_->outputs) { + output_scale = (output_scale == -1) ? output->scale : std::min(output_scale, output->scale); + } + if (output_scale == -1) { + output_scale = 1; + } + + const int custom_size = std::max(sizex, sizey); + const int target_size = seat->cursor.theme_size * output_scale; + + cursor->custom_scale = std::max(1, (output_scale * custom_size) / target_size); + /* It would make more sense to adjust the buffer size instead of the scale. + * In practice with custom cursors of 16x16, 24x24 & 32x32 its only likely to cause + * problems with odd-scaling (HI-DPI scale of 300% or 500% for e.g.). + * In these cases the custom cursor will be a little too large. */ + while ((cursor->custom_scale > 1) && + !((sizex % cursor->custom_scale) == 0 && (sizey % cursor->custom_scale) == 0)) + { + cursor->custom_scale -= 1; + } + } + cursor->wl.buffer = buffer; cursor->wl.image.width = uint32_t(sizex); cursor->wl.image.height = uint32_t(sizey); diff --git a/source/blender/windowmanager/intern/wm_cursors.cc b/source/blender/windowmanager/intern/wm_cursors.cc index 39590823d59..4910752146f 100644 --- a/source/blender/windowmanager/intern/wm_cursors.cc +++ b/source/blender/windowmanager/intern/wm_cursors.cc @@ -129,10 +129,26 @@ static GHOST_TStandardCursor convert_to_ghost_standard_cursor(WMCursorType curs) } } +static CursorSize window_size_calc() +{ + /* Use `U.dpi` without the `U.ui_scale` because the UI scale does not impact the + * windowing-systems cursor size (only the size which is used for drawing the UI). + * The DPI however is used for scaling defined by the windowing-system. + * Ideally this would also be able to check the cursor size via GHOST. */ + const int dpi_system = int(U.dpi / U.ui_scale); + + if (dpi_system <= 72) { + return CURSOR_SIZE_16; + } + if (dpi_system <= int(72 * 1.2)) { + return CURSOR_SIZE_24; + } + return CURSOR_SIZE_32; +} + static void window_set_custom_cursor(wmWindow *win, BCursor *cursor) { - /* TODO: set sizes based on the environment. */ - const int size = CURSOR_SIZE_16; + const CursorSize size = window_size_calc(); GHOST_SetCustomCursorShape(static_cast(win->ghostwin), (uint8_t *)cursor->bitmap[size],