📌 Context
- Status: WORKAROUND
- Environment: CachyOS Linux (rolling) / KDE Plasma 6.8 (KWin Wayland) / Remmina 1.4.43 / FreeRDP 3.32.2 / AMD Radeon Cezanne (amdgpu) / Xwayland 24.1.14
- Date: 2026-10-20
- Detected in: Local desktop environment / remote work RDP session to Windows host (
<windows_host>) - Component: Remmina RDP plugin (
remmina-plugin-rdp.so), KWin Wayland DRM cursor plane, Xwayland, FreeRDP
🛑 Symptoms & Error Log
- Multi-Monitor Unavailable on Wayland: In a native KDE Plasma Wayland session, Remmina cannot span multiple monitors or pass client monitor topology to remote RDP sessions.
- Missing X11 Session Option: SDDM no longer offers a “Plasma (X11)” login option.
- Invisible Mouse Cursor on Secondary Monitor: When running Remmina under Xwayland (
GDK_BACKEND=x11 remmina) across a dual-monitor setup (eDP-1landscape 1920×1200 +HDMI-A-1portrait 1080×1920 rotated 90°), the mouse pointer completely vanishes when moved into the rotated portrait screen. Clicks and interactions still register, but the pointer graphic is invisible.
Log output during session initialization:
remmina-Message: 09:44:16.848: [X2GO] X2Go plugin loaded.
Failed to load plugin: /usr/lib/remmina/plugins/remmina-plugin-kwallet.so.
Error: libKF5Wallet.so.5: cannot open shared object file: No such file or directory
Failed to load plugin: /usr/lib/remmina/plugins/remmina-plugin-spice.so.
Error: libspice-client-gtk-3.0.so.5: cannot open shared object file: No such file or directory
Failed to load plugin: /usr/lib/remmina/plugins/remmina-plugin-gvnc.so.
Error: libgvnc-1.0.so.0: cannot open shared object file: No such file or directory
(org.remmina.Remmina:27042): Gtk-WARNING **: 09:44:17.192: gtk_menu_attach_to_widget(): menu already attached to GtkMenuItem
(org.remmina.Remmina:27042): Gtk-CRITICAL **: 09:44:28.484: gtk_widget_get_display: assertion 'GTK_IS_WIDGET (widget)' failed
[09:44:28:825] [27042:00007829] [WARN][com.freerdp.crypto] - [verify_cb]: Certificate verification failure 'self-signed certificate (18)' at stack position 0
[09:44:28:825] [27042:00007829] [WARN][com.freerdp.crypto] - [verify_cb]: CN = <windows_host>
[09:44:35:031] [27042:00007829] [INFO][com.freerdp.gdi] - [gdi_init_ex]: Local framebuffer format PIXEL_FORMAT_BGRA32
[09:44:35:031] [27042:00007829] [INFO][com.freerdp.gdi] - [gdi_init_ex]: Remote framebuffer format PIXEL_FORMAT_BGRA32
[09:44:35:034] [27042:00007829] [INFO][com.freerdp.channels.rdpsnd.client] - [rdpsnd_load_device_plugin]: [static] Loaded fake backend for rdpsnd
[09:44:35:034] [27042:00007829] [INFO][com.freerdp.channels.drdynvc.client] - [dvcman_load_addin]: Loading Dynamic Virtual Channel disp
[09:44:35:035] [27042:00007829] [INFO][com.freerdp.channels.drdynvc.client] - [dvcman_load_addin]: Loading Dynamic Virtual Channel rdpgfx
[09:44:35:035] [27042:00007829] [INFO][com.freerdp.channels.drdynvc.client] - [dvcman_load_addin]: Loading Dynamic Virtual Channel ainput
[09:44:35:035] [27042:00007829] [INFO][com.freerdp.channels.drdynvc.client] - [dvcman_load_addin]: Loading Dynamic Virtual Channel rdpsnd
Detection: The failure occurred silently when dragging the mouse from the primary landscape monitor across the boundary into the vertical display. The cursor vanished immediately upon entering the vertical screen coordinates, while UI hover highlights and clicks on the remote desktop continued to work.
🔍 Root Cause Analysis / Current Hypotheses
Two distinct issues combined to cause this failure:
- Wayland Multi-Monitor Spanning Restriction:
- Upstream Wayland design does not allow client windows to arbitrarily position or span single surfaces across physical display outputs (
GDK_FULLSCREEN_ON_ALL_MONITORSis unsupported under native Wayland). - Upstream KDE Plasma 6.8 officially dropped the native X11 session (
plasma-workspace-x11), leaving Plasma as Wayland-only.
- Upstream Wayland design does not allow client windows to arbitrarily position or span single surfaces across physical display outputs (
- AMD GPU DRM Hardware Cursor Plane Bug on 90° Rotated Displays (FreeRDP #12210):
- When Remmina runs under Xwayland (
GDK_BACKEND=x11), Remmina’s RDP plugin creates an X11 ARGB cursor usinggdk_cursor_new_from_pixbuffor the 32×32 remote pointer bitmap. - Xwayland sends this surface to KWin Wayland (
SurfaceCursorSource). - On AMD Radeon GPUs (
amdgpu), the kernel DRM hardware cursor plane reports a maximum capability of 256×256 (DRM_CAP_CURSOR_WIDTH = 256). - When the cursor size is <= 256x256, KWin's compositor assigns the cursor surface to the AMD DRM hardware cursor plane. However, on outputs rotated 90° (portrait orientation,
Rotation: 2), the AMD DRM hardware cursor plane fails to render cursor buffers <= 64x64 pixels, rendering the pointer invisible. - If a cursor surface exceeds 256×256 (
> 256),KWin::Compositor::setupLayersautomatically rejects the hardware cursor plane and falls back to OpenGL software composition, which renders rotated displays correctly.
- When Remmina runs under Xwayland (
🚫 Attempted Solutions & Failed Approaches
- Approach 1: Switch login session back to native Plasma X11 -> Result: Failed -> Upstream KDE Plasma 6.8 permanently dropped
plasma-workspace-x11; CachyOS and Arch Linux repositories have removed the split X11 packages. - Approach 2: Toggle Remmina Color Depth to “True colour (32 bpp)” (
colordepth=32) -> Result: Failed -> While disabling therdpgfxdynamic channel forces standard GDI bitmap streaming, Windows RDP and FreeRDP still pass standard 32×32 pointer updates togdk_cursor_new_from_pixbuf, triggering the AMD DRM hardware cursor plane failure on the rotated output. - Approach 3: Adjust profile resolution and view mode settings (
scale=0,span=1,multimon=0/1) -> Result: Failed -> Changing span/scale parameters inside Remmina did not change the 32×32 pixel dimension of the pointer sprite, leaving the cursor invisible on the secondary display.
💡 Working Solution / Resolution (or Temporary Workaround)
A comprehensive client-side workaround was implemented combining an Xwayland execution wrapper and a transparent cursor padding shim.
1. Compile the Transparent Cursor Padding Shim (libremmina_cursor_fix.so)
The shim intercepts gdk_cursor_new_from_pixbuf(). When the incoming cursor pixbuf is smaller than 260×260, it allocates a 260×260 pixbuf filled with 100% transparent pixels (0x00000000), copies the original 32×32 cursor pixels to (0, 0), and passes the padded pixbuf to the real function.
Source file ~/.local/lib/remmina_cursor_fix.c:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <gdk/gdk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
typedef GdkCursor* (*gdk_cursor_new_from_pixbuf_t)(GdkDisplay *display, GdkPixbuf *pixbuf, gint x, gint y);
GdkCursor* gdk_cursor_new_from_pixbuf(GdkDisplay *display, GdkPixbuf *pixbuf, gint x, gint y) {
static gdk_cursor_new_from_pixbuf_t real_fn = NULL;
if (!real_fn) {
real_fn = (gdk_cursor_new_from_pixbuf_t)dlsym(RTLD_NEXT, "gdk_cursor_new_from_pixbuf");
}
if (!pixbuf) {
return real_fn(display, pixbuf, x, y);
}
int w = gdk_pixbuf_get_width(pixbuf);
int h = gdk_pixbuf_get_height(pixbuf);
/* Pad cursor surface to 260x260 (> 256x256 AMD DRM hardware cursor cap)
* with transparent pixels so KWin falls back to software OpenGL cursor
* composition on 90-degree rotated AMD displays. */
const int TARGET_SIZE = 260;
if (w < TARGET_SIZE || h < TARGET_SIZE) {
int new_w = (w < TARGET_SIZE) ? TARGET_SIZE : w;
int new_h = (h < TARGET_SIZE) ? TARGET_SIZE : h;
GdkPixbuf *padded = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, new_w, new_h);
if (padded) {
gdk_pixbuf_fill(padded, 0x00000000);
gdk_pixbuf_copy_area(pixbuf, 0, 0, w, h, padded, 0, 0);
GdkCursor *cursor = real_fn(display, padded, x, y);
g_object_unref(padded);
return cursor;
}
}
return real_fn(display, pixbuf, x, y);
}
Compilation:
gcc -shared -fPIC -O2 ~/.local/lib/remmina_cursor_fix.c \
-o ~/.local/lib/libremmina_cursor_fix.so \
$(pkg-config --cflags --libs gdk-3.0 gdk-pixbuf-2.0) -ldl
2. Global Executable Wrapper (~/.local/bin/remmina)
Created an executable wrapper at ~/.local/bin/remmina (chmod +x):
#!/bin/sh
export GDK_BACKEND=x11
export LD_PRELOAD="/home/<user>/.local/lib/libremmina_cursor_fix.so${LD_PRELOAD:+:$LD_PRELOAD}"
exec /usr/bin/remmina "$@"
Since ~/.local/bin precedes /usr/bin in $PATH, all commands calling remmina inherit both GDK_BACKEND=x11 and the cursor shim.
3. Autostart & Desktop Launcher Integration
- Autostart Tray Applet (
~/.config/autostart/remmina-applet.desktop):
[Desktop Entry]
Version=1.0
Name=Remmina Applet
Comment=Connect to remote desktops through the applet menu
Icon=org.remmina.Remmina
Exec=env GDK_BACKEND=x11 LD_PRELOAD=/home/<user>/.local/lib/libremmina_cursor_fix.so remmina -i
Terminal=false
Type=Application
Hidden=false
- Application Launcher (
~/.local/share/applications/org.remmina.Remmina.desktop):
Updated all Exec= directives to prepend env GDK_BACKEND=x11 LD_PRELOAD=/home/<user>/.local/lib/libremmina_cursor_fix.so.
🛡 Prevention / Next Steps
- Testing Rotated Multi-Monitor Setups: When updating KDE Plasma or kernel DRM drivers, verify that cursor visibility across mixed-orientation displays (landscape + 90° portrait) remains functional under Xwayland applications.
- Upstream Bug Tracking:
- Track FreeRDP Issue #12210 (“Mouse cursor is invisible on vertical monitor when using multiple monitors”).
- Track KDE / KWin DRM pipeline handling for rotated hardware cursor planes on AMD GPUs.
- Alternative Global Workaround: If other Xwayland applications encounter invisible cursors on rotated displays, software cursor rendering can be enforced system-wide by setting
KWIN_FORCE_SW_CURSOR=1in/etc/environment.