diff --git a/gfx/common/wayland_common.c b/gfx/common/wayland_common.c index 3e8f8355bdc8..e59b45f0d301 100644 --- a/gfx/common/wayland_common.c +++ b/gfx/common/wayland_common.c @@ -805,8 +805,10 @@ bool gfx_ctx_wl_init_common( wl->input.keyboard_focus = true; wl->input.mouse.focus = true; + wl->cursor.theme_name = NULL; + wl->cursor.size = 24; wl->cursor.surface = wl_compositor_create_surface(wl->compositor); - wl->cursor.theme = wl_cursor_theme_load(NULL, 16, wl->shm); + wl->cursor.theme = wl_cursor_theme_load(wl->cursor.theme_name, wl->cursor.size, wl->shm); wl->cursor.default_cursor = wl_cursor_theme_get_cursor(wl->cursor.theme, "left_ptr"); wl->num_active_touches = 0; diff --git a/input/common/wayland_common.c b/input/common/wayland_common.c index 1a36c61b6022..ca5c6fdd7285 100644 --- a/input/common/wayland_common.c +++ b/input/common/wayland_common.c @@ -50,6 +50,183 @@ #define SPLASH_SHM_NAME "retroarch-wayland-splash" + +/* + * Copyright © 2019 Christian Rauch + * Copyright © 2024 Colin Kinloch + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "config.h" + +static bool +get_cursor_settings_from_env(char **theme, int *size) +{ + char *env_xtheme; + char *env_xsize; + + env_xtheme = getenv("XCURSOR_THEME"); + if (env_xtheme != NULL) + *theme = strdup(env_xtheme); + + env_xsize = getenv("XCURSOR_SIZE"); + if (env_xsize != NULL) + *size = atoi(env_xsize); + + return env_xtheme != NULL && env_xsize != NULL; +} + +#ifdef HAVE_DBUS +#include + +static DBusMessage * +get_setting_sync(DBusConnection *const connection, + const char *key, + const char *value) +{ + DBusError error; + dbus_bool_t success; + DBusMessage *message; + DBusMessage *reply; + + message = dbus_message_new_method_call( + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Settings", + "Read"); + + success = dbus_message_append_args(message, + DBUS_TYPE_STRING, &key, + DBUS_TYPE_STRING, &value, + DBUS_TYPE_INVALID); + + if (!success) + return NULL; + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block( + connection, + message, + DBUS_TIMEOUT_USE_DEFAULT, + &error); + + dbus_message_unref(message); + + if (dbus_error_is_set(&error)) { + dbus_error_free(&error); + return NULL; + } + + dbus_error_free(&error); + return reply; +} + +static bool +parse_type(DBusMessage *const reply, + const int type, + void *value) +{ + DBusMessageIter iter[3]; + + dbus_message_iter_init(reply, &iter[0]); + if (dbus_message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT) + return false; + + dbus_message_iter_recurse(&iter[0], &iter[1]); + if (dbus_message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT) + return false; + + dbus_message_iter_recurse(&iter[1], &iter[2]); + if (dbus_message_iter_get_arg_type(&iter[2]) != type) + return false; + + dbus_message_iter_get_basic(&iter[2], value); + + return true; +} + +bool +libdecor_get_cursor_settings(char **theme, int *size) +{ + static const char name[] = "org.gnome.desktop.interface"; + static const char key_theme[] = "cursor-theme"; + static const char key_size[] = "cursor-size"; + + DBusError error; + DBusConnection *connection; + DBusMessage *reply; + const char *value_theme = NULL; + + dbus_error_init(&error); + + connection = dbus_bus_get(DBUS_BUS_SESSION, &error); + + if (dbus_error_is_set(&error)) + goto fallback; + + reply = get_setting_sync(connection, name, key_theme); + if (!reply) + goto fallback; + + if (!parse_type(reply, DBUS_TYPE_STRING, &value_theme)) { + dbus_message_unref(reply); + goto fallback; + } + + *theme = strdup(value_theme); + + dbus_message_unref(reply); + + reply = get_setting_sync(connection, name, key_size); + if (!reply) + goto fallback; + + if (!parse_type(reply, DBUS_TYPE_INT32, size)) { + dbus_message_unref(reply); + goto fallback; + } + + dbus_message_unref(reply); + + return true; + +fallback: + return get_cursor_settings_from_env(theme, size); +} +#else +bool +libdecor_get_cursor_settings(char **theme, int *size) +{ + return get_cursor_settings_from_env(theme, size); +} +#endif + + static void wl_keyboard_handle_keymap(void* data, struct wl_keyboard* keyboard, uint32_t format, @@ -164,12 +341,33 @@ void gfx_ctx_wl_show_mouse(void *data, bool state) if (state) { + if (!libdecor_get_cursor_settings(&wl->cursor.theme_name, &wl->cursor.size)) { + wl->cursor.theme_name = NULL; + wl->cursor.size = 24; + } + int scale = ceil(FRACTIONAL_SCALE_MULT_DOUBLE(1.0, wl->fractional_scale_num)); + struct wl_cursor_theme *theme = wl_cursor_theme_load( + wl->cursor.theme_name, + wl->cursor.size * scale, + wl->shm); + if (theme) { + wl_cursor_theme_destroy(wl->cursor.theme); + wl->cursor.theme = theme; + } + + wl->cursor.default_cursor = wl_cursor_theme_get_cursor(wl->cursor.theme, "left_ptr"); struct wl_cursor_image *image = wl->cursor.default_cursor->images[0]; wl_pointer_set_cursor(wl->wl_pointer, wl->cursor.serial, wl->cursor.surface, - image->hotspot_x, image->hotspot_y); + image->hotspot_x / scale, image->hotspot_y / scale); wl_surface_attach(wl->cursor.surface, wl_cursor_image_get_buffer(image), 0, 0); + + if (wl_surface_get_version (wl->cursor.surface) + >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) + { + wl_surface_set_buffer_scale (wl->cursor.surface, scale); + } wl_surface_damage(wl->cursor.surface, 0, 0, image->width, image->height); wl_surface_commit(wl->cursor.surface); } diff --git a/input/common/wayland_common.h b/input/common/wayland_common.h index 8ed1f99f33e3..d325dfc222bf 100644 --- a/input/common/wayland_common.h +++ b/input/common/wayland_common.h @@ -45,6 +45,8 @@ #define FRACTIONAL_SCALE_V1_DEN 120 #define FRACTIONAL_SCALE_MULT(v, scale_num) \ (((v) * (scale_num) + FRACTIONAL_SCALE_V1_DEN / 2) / FRACTIONAL_SCALE_V1_DEN) +#define FRACTIONAL_SCALE_MULT_DOUBLE(v, scale_num) \ + (((double)(v) * (double)(scale_num) + (double)FRACTIONAL_SCALE_V1_DEN / 2.0) / (double)FRACTIONAL_SCALE_V1_DEN) #define UDEV_KEY_MAX 0x2ff #define UDEV_MAX_KEYS (UDEV_KEY_MAX + 7) / 8 @@ -195,6 +197,8 @@ typedef struct gfx_ctx_wayland_data struct wl_cursor_theme *theme; struct wl_surface *surface; uint32_t serial; + char *theme_name; + int size; bool visible; } cursor; @@ -238,6 +242,8 @@ void gfx_ctx_wl_show_mouse(void *data, bool state); void flush_wayland_fd(void *data); +bool libdecor_get_cursor_settings(char **theme, int *size); + extern const struct wl_keyboard_listener keyboard_listener; extern const struct wl_pointer_listener pointer_listener;