diff --git a/.gitignore b/.gitignore
index 49eb8cbffb..996bbf2437 100644
--- a/.gitignore
+++ b/.gitignore
@@ -213,6 +213,10 @@ retroarch_switch.nso
 *_irx.c
 
 # Wayland
+gfx/common/wayland/fractional-scale-v1.c
+gfx/common/wayland/fractional-scale-v1.h
+gfx/common/wayland/viewporter.c
+gfx/common/wayland/viewporter.h
 gfx/common/wayland/idle-inhibit-unstable-v1.c
 gfx/common/wayland/idle-inhibit-unstable-v1.h
 gfx/common/wayland/xdg-shell-unstable-v6.c
diff --git a/Makefile.common b/Makefile.common
index ef418f679b..ecab263bbd 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -1240,6 +1240,7 @@ ifeq ($(HAVE_WAYLAND), 1)
         input/common/wayland_common.o \
         input/drivers/wayland_input.o \
         gfx/common/wayland_common.o \
+        gfx/common/wayland/fractional-scale-v1.o \
         gfx/common/wayland/viewporter.o \
         gfx/common/wayland/xdg-shell.o \
         gfx/common/wayland/idle-inhibit-unstable-v1.o \
diff --git a/deps/wayland-protocols/staging/fractional-scale/README b/deps/wayland-protocols/staging/fractional-scale/README
new file mode 100644
index 0000000000..6cb0475f27
--- /dev/null
+++ b/deps/wayland-protocols/staging/fractional-scale/README
@@ -0,0 +1,4 @@
+wp fractional scale protocol
+
+Maintainers:
+Kenny Levinsen <kl@kl.wtf>
diff --git a/deps/wayland-protocols/staging/fractional-scale/fractional-scale-v1.xml b/deps/wayland-protocols/staging/fractional-scale/fractional-scale-v1.xml
new file mode 100644
index 0000000000..350bfc01ea
--- /dev/null
+++ b/deps/wayland-protocols/staging/fractional-scale/fractional-scale-v1.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="fractional_scale_v1">
+  <copyright>
+    Copyright © 2022 Kenny Levinsen
+
+    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.
+  </copyright>
+
+  <description summary="Protocol for requesting fractional surface scales">
+    This protocol allows a compositor to suggest for surfaces to render at
+    fractional scales.
+
+    A client can submit scaled content by utilizing wp_viewport. This is done by
+    creating a wp_viewport object for the surface and setting the destination
+    rectangle to the surface size before the scale factor is applied.
+
+    The buffer size is calculated by multiplying the surface size by the
+    intended scale.
+
+    The wl_surface buffer scale should remain set to 1.
+
+    If a surface has a surface-local size of 100 px by 50 px and wishes to
+    submit buffers with a scale of 1.5, then a buffer of 150px by 75 px should
+    be used and the wp_viewport destination rectangle should be 100 px by 50 px.
+
+    For toplevel surfaces, the size is rounded halfway away from zero. The
+    rounding algorithm for subsurface position and size is not defined.
+  </description>
+
+  <interface name="wp_fractional_scale_manager_v1" version="1">
+    <description summary="fractional surface scale information">
+      A global interface for requesting surfaces to use fractional scales.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="unbind the fractional surface scale interface">
+        Informs the server that the client will not be using this protocol
+        object anymore. This does not affect any other objects,
+        wp_fractional_scale_v1 objects included.
+      </description>
+    </request>
+
+    <enum name="error">
+      <entry name="fractional_scale_exists" value="0"
+        summary="the surface already has a fractional_scale object associated"/>
+    </enum>
+
+    <request name="get_fractional_scale">
+      <description summary="extend surface interface for scale information">
+        Create an add-on object for the the wl_surface to let the compositor
+        request fractional scales. If the given wl_surface already has a
+        wp_fractional_scale_v1 object associated, the fractional_scale_exists
+        protocol error is raised.
+      </description>
+      <arg name="id" type="new_id" interface="wp_fractional_scale_v1"
+           summary="the new surface scale info interface id"/>
+      <arg name="surface" type="object" interface="wl_surface"
+           summary="the surface"/>
+    </request>
+  </interface>
+
+  <interface name="wp_fractional_scale_v1" version="1">
+    <description summary="fractional scale interface to a wl_surface">
+      An additional interface to a wl_surface object which allows the compositor
+      to inform the client of the preferred scale.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="remove surface scale information for surface">
+        Destroy the fractional scale object. When this object is destroyed,
+        preferred_scale events will no longer be sent.
+      </description>
+    </request>
+
+    <event name="preferred_scale">
+      <description summary="notify of new preferred scale">
+        Notification of a new preferred scale for this surface that the
+        compositor suggests that the client should use.
+
+        The sent scale is the numerator of a fraction with a denominator of 120.
+      </description>
+      <arg name="scale" type="uint" summary="the new preferred scale"/>
+    </event>
+  </interface>
+</protocol>
diff --git a/gfx/common/wayland/generate_wayland_protos.sh b/gfx/common/wayland/generate_wayland_protos.sh
index 1670b26eb4..5c9347428b 100755
--- a/gfx/common/wayland/generate_wayland_protos.sh
+++ b/gfx/common/wayland/generate_wayland_protos.sh
@@ -67,4 +67,4 @@ generate_source 'unstable/xdg-decoration' 'xdg-decoration-unstable-v1'
 generate_source 'unstable/idle-inhibit' 'idle-inhibit-unstable-v1'
 generate_source 'unstable/pointer-constraints' 'pointer-constraints-unstable-v1'
 generate_source 'unstable/relative-pointer' 'relative-pointer-unstable-v1'
-
+generate_source 'staging/fractional-scale' 'fractional-scale-v1'
diff --git a/gfx/common/wayland_common.c b/gfx/common/wayland_common.c
index a0a2158d1a..817f9e3cc7 100644
--- a/gfx/common/wayland_common.c
+++ b/gfx/common/wayland_common.c
@@ -128,11 +128,13 @@ void xdg_toplevel_handle_configure_common(gfx_ctx_wayland_data_t *wl,
    if (     (width  > 0)
          && (height > 0))
    {
-      wl->width           = width;
-      wl->height          = height;
-      wl->buffer_width    = wl->width * wl->buffer_scale;
-      wl->buffer_height   = wl->height * wl->buffer_scale;
-      wl->resize          = true;
+      wl->width         = width;
+      wl->height        = height;
+      wl->buffer_width  = wl->fractional_scale ?
+         FRACTIONAL_SCALE_MULT(wl->width,  wl->fractional_scale_num) : wl->width  * wl->buffer_scale;
+      wl->buffer_height = wl->fractional_scale ?
+         FRACTIONAL_SCALE_MULT(wl->height, wl->fractional_scale_num) : wl->height * wl->buffer_scale;
+      wl->resize        = true;
       if (wl->viewport) /* Update viewport */
          wp_viewport_set_destination(wl->viewport, wl->width, wl->height);
    }
@@ -193,8 +195,10 @@ void libdecor_frame_handle_configure_common(struct libdecor_frame *frame,
    {
       wl->width         = width;
       wl->height        = height;
-      wl->buffer_width  = width * wl->buffer_scale;
-      wl->buffer_height = height * wl->buffer_scale;
+      wl->buffer_width  = wl->fractional_scale ?
+         FRACTIONAL_SCALE_MULT(width,  wl->fractional_scale_num) : width  * wl->buffer_scale;
+      wl->buffer_height = wl->fractional_scale ?
+         FRACTIONAL_SCALE_MULT(height, wl->fractional_scale_num) : height * wl->buffer_scale;
       wl->resize        = true;
       if (wl->viewport) /* Update viewport */
          wp_viewport_set_destination(wl->viewport, wl->width, wl->height);
@@ -243,8 +247,10 @@ void gfx_ctx_wl_get_video_size_common(void *data,
    }
    else
    {
-      *width  = wl->width  * wl->pending_buffer_scale;
-      *height = wl->height * wl->pending_buffer_scale;
+     *width  = wl->fractional_scale ?
+        FRACTIONAL_SCALE_MULT(wl->width,  wl->pending_fractional_scale_num) : wl->width  * wl->pending_buffer_scale;
+     *height = wl->fractional_scale ?
+        FRACTIONAL_SCALE_MULT(wl->height, wl->pending_fractional_scale_num) : wl->height * wl->pending_buffer_scale;
    }
 }
 
@@ -271,6 +277,8 @@ void gfx_ctx_wl_destroy_resources_common(gfx_ctx_wayland_data_t *wl)
 
    if (wl->viewport)
       wp_viewport_destroy(wl->viewport);
+   if (wl->fractional_scale)
+      wp_fractional_scale_v1_destroy(wl->fractional_scale);
    if (wl->idle_inhibitor)
       zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor);
    if (wl->deco)
@@ -322,6 +330,8 @@ void gfx_ctx_wl_destroy_resources_common(gfx_ctx_wayland_data_t *wl)
       wl_shm_destroy (wl->shm);
    if (wl->viewporter)
       wp_viewporter_destroy(wl->viewporter);
+   if (wl->fractional_scale_manager)
+      wp_fractional_scale_manager_v1_destroy(wl->fractional_scale_manager);
    if (wl->compositor)
       wl_compositor_destroy(wl->compositor);
    if (wl->registry)
@@ -578,8 +588,8 @@ static void shm_buffer_paint_checkerboard(
 static bool wl_draw_splash_screen(gfx_ctx_wayland_data_t *wl)
 {
    shm_buffer_t *buffer = create_shm_buffer(wl,
-      wl->width * wl->buffer_scale,
-      wl->height * wl->buffer_scale,
+      wl->buffer_width,
+      wl->buffer_height,
       WL_SHM_FORMAT_XRGB8888);
 
    if (!buffer)
@@ -630,12 +640,15 @@ bool gfx_ctx_wl_init_common(
 
    frontend_driver_destroy_signal_handler_state();
 
-   wl->input.dpy            = wl_display_connect(NULL);
-   wl->last_buffer_scale    = 1;
-   wl->buffer_scale         = 1;
-   wl->pending_buffer_scale = 1;
-   wl->floating_width       = SPLASH_WINDOW_WIDTH;
-   wl->floating_height      = SPLASH_WINDOW_HEIGHT;
+   wl->input.dpy                    = wl_display_connect(NULL);
+   wl->last_buffer_scale            = 1;
+   wl->buffer_scale                 = 1;
+   wl->pending_buffer_scale         = 1;
+   wl->last_fractional_scale_num    = FRACTIONAL_SCALE_V1_DEN;
+   wl->fractional_scale_num         = FRACTIONAL_SCALE_V1_DEN;
+   wl->pending_fractional_scale_num = FRACTIONAL_SCALE_V1_DEN;
+   wl->floating_width               = SPLASH_WINDOW_WIDTH;
+   wl->floating_height              = SPLASH_WINDOW_HEIGHT;
 
    if (!wl->input.dpy)
    {
@@ -683,6 +696,13 @@ bool gfx_ctx_wl_init_common(
    wl->surface = wl_compositor_create_surface(wl->compositor);
    if (wl->viewporter)
       wl->viewport = wp_viewporter_get_viewport(wl->viewporter, wl->surface);
+   if (wl->fractional_scale_manager)
+   {
+      wl->fractional_scale = wp_fractional_scale_manager_v1_get_fractional_scale(
+           wl->fractional_scale_manager, wl->surface);
+      wp_fractional_scale_v1_add_listener(wl->fractional_scale, &wp_fractional_scale_v1_listener, wl);
+      RARCH_LOG("[Wayland]: fractional_scale_v1 enabled\n");
+   }
 
    wl_surface_add_listener(wl->surface, &wl_surface_listener, wl);
 
@@ -809,9 +829,12 @@ bool gfx_ctx_wl_set_video_mode_common_size(gfx_ctx_wayland_data_t *wl,
 
    if (!fullscreen)
    {
-      wl->buffer_scale   = wl->pending_buffer_scale;
-      wl->buffer_width  *= wl->buffer_scale;
-      wl->buffer_height *= wl->buffer_scale;
+      wl->buffer_scale         = wl->pending_buffer_scale;
+      wl->fractional_scale_num = wl->pending_fractional_scale_num;
+      wl->buffer_width         = wl->fractional_scale ?
+         FRACTIONAL_SCALE_MULT(wl->buffer_width,  wl->fractional_scale_num) : wl->buffer_width  * wl->buffer_scale;
+      wl->buffer_height        = wl->fractional_scale ?
+         FRACTIONAL_SCALE_MULT(wl->buffer_height, wl->fractional_scale_num) : wl->buffer_height * wl->buffer_scale;
    }
    if (wl->viewport) /* Update viewport */
       wp_viewport_set_destination(wl->viewport, wl->width, wl->height);
@@ -944,13 +967,15 @@ void gfx_ctx_wl_check_window_common(gfx_ctx_wayland_data_t *wl,
    get_video_size(wl, &new_width, &new_height);
 
    if (     wl->pending_buffer_scale != wl->buffer_scale
+         || wl->pending_fractional_scale_num != wl->fractional_scale_num
          || new_width  != *width
          || new_height != *height)
    {
-      wl->buffer_scale = wl->pending_buffer_scale;
-      *width           = new_width;
-      *height          = new_height;
-      *resize          = true;
+      wl->buffer_scale         = wl->pending_buffer_scale;
+      wl->fractional_scale_num = wl->pending_fractional_scale_num;
+      *width                   = new_width;
+      *height                  = new_height;
+      *resize                  = true;
    }
 
    *quit = (bool)frontend_driver_get_signal_handler_state();
diff --git a/gfx/drivers_context/wayland_ctx.c b/gfx/drivers_context/wayland_ctx.c
index 4c883f093f..d1832c2182 100644
--- a/gfx/drivers_context/wayland_ctx.c
+++ b/gfx/drivers_context/wayland_ctx.c
@@ -107,10 +107,11 @@ static void gfx_ctx_wl_check_window(void *data, bool *quit,
 
 static bool gfx_ctx_wl_set_resize(void *data, unsigned width, unsigned height)
 {
-   gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
-
-   wl->last_buffer_scale      = wl->buffer_scale;
-   wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale);
+   gfx_ctx_wayland_data_t *wl    = (gfx_ctx_wayland_data_t*)data;
+   wl->last_buffer_scale         = wl->buffer_scale;
+   wl->last_fractional_scale_num = wl->fractional_scale_num;
+   if (!wl->fractional_scale)
+      wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale);
 
 #ifdef HAVE_EGL
    wl_egl_window_resize(wl->win, width, height, 0, 0);
diff --git a/gfx/drivers_context/wayland_vk_ctx.c b/gfx/drivers_context/wayland_vk_ctx.c
index 5161b6ef85..2b726a8fa8 100644
--- a/gfx/drivers_context/wayland_vk_ctx.c
+++ b/gfx/drivers_context/wayland_vk_ctx.c
@@ -86,7 +86,9 @@ static bool gfx_ctx_wl_set_resize(void *data, unsigned width, unsigned height)
    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
 
    wl->last_buffer_scale = wl->buffer_scale;
-   wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale);
+   wl->last_fractional_scale_num = wl->fractional_scale_num;
+   if (!wl->fractional_scale)
+      wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale);
 
    if (vulkan_create_swapchain(&wl->vk, width, height, wl->swap_interval))
    {
diff --git a/input/common/wayland_common.c b/input/common/wayland_common.c
index de428f8c36..8084699bea 100644
--- a/input/common/wayland_common.c
+++ b/input/common/wayland_common.c
@@ -189,10 +189,12 @@ static void wl_pointer_handle_enter(void *data,
    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
 
    wl->input.mouse.surface    = surface;
-   wl->input.mouse.last_x     = wl_fixed_to_int(
-         sx * (wl_fixed_t)wl->buffer_scale);
-   wl->input.mouse.last_y     = wl_fixed_to_int(
-         sy * (wl_fixed_t)wl->buffer_scale);
+   wl->input.mouse.last_x     = wl->fractional_scale ?
+         (int) FRACTIONAL_SCALE_MULT(wl_fixed_to_int(sx), wl->fractional_scale_num) :
+         wl_fixed_to_int(sx * (wl_fixed_t)wl->buffer_scale);
+   wl->input.mouse.last_y     = wl->fractional_scale ?
+         (int) FRACTIONAL_SCALE_MULT(wl_fixed_to_int(sy), wl->fractional_scale_num) :
+         wl_fixed_to_int(sy * (wl_fixed_t)wl->buffer_scale);
    wl->input.mouse.x          = wl->input.mouse.last_x;
    wl->input.mouse.y          = wl->input.mouse.last_y;
    wl->input.mouse.focus      = true;
@@ -223,10 +225,12 @@ static void wl_pointer_handle_motion(void *data,
       wl_fixed_t sy)
 {
    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
-   wl->input.mouse.x          = wl_fixed_to_int(
-         (wl_fixed_t)wl->buffer_scale * sx);
-   wl->input.mouse.y          = wl_fixed_to_int(
-         (wl_fixed_t)wl->buffer_scale * sy);
+   wl->input.mouse.x          = wl->fractional_scale ?
+         (int) FRACTIONAL_SCALE_MULT(wl_fixed_to_int(sx), wl->fractional_scale_num) :
+         wl_fixed_to_int((wl_fixed_t)wl->buffer_scale * sx);
+   wl->input.mouse.y          = wl->fractional_scale ?
+         (int) FRACTIONAL_SCALE_MULT(wl_fixed_to_int(sy), wl->fractional_scale_num) :
+         wl_fixed_to_int((wl_fixed_t)wl->buffer_scale * sy);
 }
 
 static void wl_pointer_handle_button(void *data,
@@ -331,10 +335,12 @@ static void wl_touch_handle_down(void *data,
          {
             wl->active_touch_positions[wl->num_active_touches].active = true;
             wl->active_touch_positions[wl->num_active_touches].id     = id;
-            wl->active_touch_positions[wl->num_active_touches].x      = (unsigned)
-               wl_fixed_to_int(x * (wl_fixed_t)wl->buffer_scale);
-            wl->active_touch_positions[wl->num_active_touches].y      = (unsigned)
-               wl_fixed_to_int(y * (wl_fixed_t)wl->buffer_scale);
+            wl->active_touch_positions[wl->num_active_touches].x      = wl->fractional_scale ?
+               FRACTIONAL_SCALE_MULT(wl_fixed_to_int(x), wl->fractional_scale_num) :
+               (unsigned) wl_fixed_to_int(x * (wl_fixed_t)wl->buffer_scale);
+            wl->active_touch_positions[wl->num_active_touches].y      = wl->fractional_scale ?
+               FRACTIONAL_SCALE_MULT(wl_fixed_to_int(y), wl->fractional_scale_num) :
+               (unsigned) wl_fixed_to_int(y * (wl_fixed_t)wl->buffer_scale);
             wl->num_active_touches++;
             break;
          }
@@ -415,10 +421,12 @@ static void wl_touch_handle_motion(void *data,
       if (  wl->active_touch_positions[i].active &&
             wl->active_touch_positions[i].id == id)
       {
-         wl->active_touch_positions[i].x = (unsigned) wl_fixed_to_int(
-            x * (wl_fixed_t)wl->buffer_scale);
-         wl->active_touch_positions[i].y = (unsigned) wl_fixed_to_int(
-            y * (wl_fixed_t)wl->buffer_scale);
+         wl->active_touch_positions[i].x = wl->fractional_scale ?
+            FRACTIONAL_SCALE_MULT(wl_fixed_to_int(x), wl->fractional_scale_num) :
+            (unsigned) wl_fixed_to_int(x * (wl_fixed_t)wl->buffer_scale);
+         wl->active_touch_positions[i].y = wl->fractional_scale ?
+            FRACTIONAL_SCALE_MULT(wl_fixed_to_int(y), wl->fractional_scale_num) :
+            (unsigned) wl_fixed_to_int(y * (wl_fixed_t)wl->buffer_scale);
       }
    }
 }
@@ -592,6 +600,13 @@ static bool wl_current_outputs_remove(gfx_ctx_wayland_data_t *wl,
    return false;
 }
 
+static void wp_fractional_scale_v1_preferred_scale(void *data, struct wp_fractional_scale_v1 *fractional_scale,
+      uint32_t scale)
+{
+   gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
+   wl->pending_fractional_scale_num = scale;
+}
+
 static void wl_surface_enter(void *data, struct wl_surface *wl_surface,
       struct wl_output *output)
 {
@@ -697,6 +712,9 @@ static void wl_registry_handle_global(void *data, struct wl_registry *reg,
    else if (string_is_equal(interface, wp_viewporter_interface.name))
       wl->viewporter = (struct wp_viewporter*)wl_registry_bind(reg,
             id, &wp_viewporter_interface, MIN(version, 1));
+   else if (string_is_equal(interface, wp_fractional_scale_manager_v1_interface.name))
+      wl->fractional_scale_manager = (struct wp_fractional_scale_manager_v1*)
+         wl_registry_bind(reg, id, &wp_fractional_scale_manager_v1_interface, MIN(version, 1));
    else if (string_is_equal(interface, wl_output_interface.name))
    {
       display_output_t *od = (display_output_t*)
@@ -1031,6 +1049,10 @@ const struct xdg_surface_listener xdg_surface_listener = {
     xdg_surface_handle_configure,
 };
 
+const struct wp_fractional_scale_v1_listener wp_fractional_scale_v1_listener = {
+    wp_fractional_scale_v1_preferred_scale,
+};
+
 const struct wl_surface_listener wl_surface_listener = {
     wl_surface_enter,
     wl_surface_leave,
diff --git a/input/common/wayland_common.h b/input/common/wayland_common.h
index bb597da742..47c4ff8dc7 100644
--- a/input/common/wayland_common.h
+++ b/input/common/wayland_common.h
@@ -34,6 +34,7 @@
 #endif
 
 /* Generated from wayland protocol files by generate_wayland_protos.sh */
+#include "../../gfx/common/wayland/fractional-scale-v1.h"
 #include "../../gfx/common/wayland/viewporter.h"
 #include "../../gfx/common/wayland/idle-inhibit-unstable-v1.h"
 #include "../../gfx/common/wayland/xdg-shell.h"
@@ -41,7 +42,11 @@
 #include "../../gfx/common/wayland/pointer-constraints-unstable-v1.h"
 #include "../../gfx/common/wayland/relative-pointer-unstable-v1.h"
 
-#define UDEV_KEY_MAX			     0x2ff
+#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 UDEV_KEY_MAX            0x2ff
 #define UDEV_MAX_KEYS           (UDEV_KEY_MAX + 7) / 8
 
 #define MAX_TOUCHES             16
@@ -144,9 +149,11 @@ typedef struct gfx_ctx_wayland_data
    struct wl_registry *registry;
    struct wl_compositor *compositor;
    struct wp_viewporter *viewporter;
+   struct wp_fractional_scale_manager_v1 *fractional_scale_manager;
    struct wl_surface *surface;
    struct xdg_surface *xdg_surface;
    struct wp_viewport *viewport;
+   struct wp_fractional_scale_v1 *fractional_scale;
    struct xdg_wm_base *xdg_shell;
    struct xdg_toplevel *xdg_toplevel;
    struct wl_keyboard *wl_keyboard;
@@ -203,6 +210,9 @@ typedef struct gfx_ctx_wayland_data
    unsigned last_buffer_scale;
    unsigned pending_buffer_scale;
    unsigned buffer_scale;
+   unsigned last_fractional_scale_num;
+   unsigned pending_fractional_scale_num;
+   unsigned fractional_scale_num;
 
    bool core_hw_context_enable;
    bool fullscreen;
@@ -239,6 +249,8 @@ extern const struct wl_touch_listener touch_listener;
 
 extern const struct wl_seat_listener seat_listener;
 
+extern const struct wp_fractional_scale_v1_listener wp_fractional_scale_v1_listener;
+
 extern const struct wl_surface_listener wl_surface_listener;
 
 extern const struct xdg_wm_base_listener xdg_shell_listener;