Skip to content

Compositor & Desktop Support

JuhRadial MX runs natively across the major Linux desktops. Two things have to work for the radial menu to land on your cursor: the daemon has to find the cursor position when you press the gesture button, and the overlay window has to position itself at that point. Different compositors expose this in different ways, so the daemon picks the most accurate source available for your session.

This page documents how cursor detection works per compositor, what is needed to set each one up, and the known limitations.

See also: Installation, Configuration, Troubleshooting, Architecture, FAQ.


Status at a glance

Desktop / Compositor Cursor detection Overlay positioning Status
GNOME (Wayland) Shell extension over D-Bus (org.juhradial.CursorHelper) XWayland Fully supported
KDE Plasma 6 (Wayland) KWin script (workspace.cursorPos) XWayland Fully supported
Hyprland IPC socket (cursorpos), hyprctl fallback XWayland Fully supported
COSMIC XWayland XQueryPointer XWayland Fully supported
Sway / wlroots XWayland XQueryPointer XWayland Supported
niri Pointer position from the layer-shell surface itself (gtk4-layer-shell); XWayland XQueryPointer via xwayland-satellite otherwise Layer-shell (native), XWayland fallback Supported
X11 (any DE) XQueryPointer / xdotool Native X11 Supported

Note

On every Wayland session the overlay relies on XWayland for window positioning. Make sure XWayland is installed and running before reporting a positioning bug. The cursor detection method varies per compositor, as described below, but the overlay window itself is placed through XWayland on all Wayland desktops.


How cursor detection works

When you trigger the menu, the daemon resolves the cursor position. On KDE this happens directly through a KWin script that calls the daemon back with the position. On every other desktop the daemon walks an ordered fallback chain and uses the first source that answers:

  1. Hyprland IPC (only if HYPRLAND_INSTANCE_SIGNATURE is set)
  2. KWin D-Bus cursorPos property (older Plasma only)
  3. GNOME Shell extension D-Bus (only if XDG_CURRENT_DESKTOP contains GNOME)
  4. XWayland XQueryPointer (any compositor with XWayland, i.e. DISPLAY is set)
  5. xdotool (X11)
  6. Screen-center fallback (so the menu is always visible if nothing else answers)

The sections below explain each desktop in detail.


GNOME (Wayland)

GNOME does not expose the global pointer position to external clients, so JuhRadial MX ships a small GNOME Shell extension (juhradial-cursor@dev.juhlabs.com) that exposes global.get_pointer() over D-Bus. The daemon queries it at:

dest:   org.juhradial.CursorHelper
path:   /org/juhradial/CursorHelper
method: org.juhradial.CursorHelper.GetCursorPosition

The extension is only consulted when XDG_CURRENT_DESKTOP contains GNOME.

Setup

The installer enables the extension for you. If the menu appears in the top-left corner, the extension is not loaded yet:

gnome-extensions enable juhradial-cursor@dev.juhlabs.com

Tip

GNOME loads new Shell extensions at session start. If enabling it from the terminal does not take effect immediately, log out and back in.

Supported GNOME versions

The extension declares GNOME Shell 45 to 51. GNOME refuses to load an extension whose shell-version list does not name the running major version and reports it as out of date, which is why the menu fell back to the top-left corner on GNOME 51 before JuhRadial MX 0.4.5 (#144). The extension uses only global.get_pointer() and a session D-Bus export, both unchanged in GNOME 51. After updating, log out and back in once so GNOME re-reads the extension. To check it loaded:

gnome-extensions info juhradial-cursor@dev.juhlabs.com   # State: ACTIVE
gdbus call --session --dest org.juhradial.CursorHelper --object-path /org/juhradial/CursorHelper \
  --method org.juhradial.CursorHelper.GetCursorPosition

When a future GNOME release is out and not yet listed, State: OUT OF DATE means the same thing; please open an issue.

Known limitation: per-app profiles on GNOME Wayland

Per-application profiles (auto-switching DPI, buttons, and scroll on focus change) need a source of truth for the focused window's application class. The daemon's active-window tracker has a native source only for KDE (a persistent KWin script) and Hyprland (the event socket). Every other session falls back to watching _NET_ACTIVE_WINDOW with xprop -spy and reading WM_CLASS on each change, which only reliably reflects native windows under X11.

Under GNOME Wayland there is no equivalent native focus signal exposed to external clients, and the xprop fallback sees XWayland clients only, not native Wayland toplevels. As a result:

Warning

Per-application profiles do not switch reliably on GNOME Wayland. The radial menu, cursor positioning, and all other features work normally; only the automatic per-app DPI/button/scroll switching is affected. Use a single global profile on GNOME Wayland, or run a KDE Plasma / Hyprland / X11 session if you depend on per-app switching.


KDE Plasma 6 (Wayland)

KDE uses KWin scripting, which is both the most accurate path and the only one that natively understands multi-monitor logical geometry on Plasma 6.

Cursor positioning

When the gesture button is pressed, the daemon loads a one-shot KWin script that reads workspace.cursorPos and calls the daemon's ShowMenuAtCursor D-Bus method directly. KWin reports workspace.cursorPos in logical pixels, which is the same device-independent coordinate space the overlay's window placement uses on the XWayland (xcb) platform, so the position is passed through unchanged:

var pos = workspace.cursorPos;
callDBus("org.kde.juhradialmx", "/org/kde/juhradialmx/Daemon",
         "org.kde.juhradialmx.Daemon", "ShowMenuAtCursor",
         Math.round(pos.x), Math.round(pos.y));

Note

Fractional scaling: earlier builds applied a devicePixelRatio correction to this position. Because KWin's logical coordinates already match the overlay's point space, multiplying overshot the cursor toward the bottom-right and dividing overshot toward the top-left, with the error growing the further the cursor sat from the top-left of the monitor. The identity pass-through is what lands the menu on the cursor at any scale, including 125 percent, 150 percent, and other fractional factors.

A D-Bus fallback (org.kde.KWin → cursorPos) exists for older Plasma versions; this property is not available on Plasma 6, so Plasma 6 always uses the script path above.

Per-app profiles

KDE installs a persistent KWin script (loaded with loadScript + Script.run) that connects to the activation signal and calls the daemon's ReportActiveWindow method on every focus change. It handles both Plasma 6 (windowActivated / activeWindow) and Plasma 5 (clientActivated / activeClient), so per-application profiles work out of the box.

Setup

No manual setup is required. KWin scripting is driven over the session bus, which the daemon already uses.


Hyprland

Hyprland exposes the cursor position over its IPC socket, which is the fastest source and avoids spawning a subprocess on each menu open.

Cursor positioning

The daemon connects to the per-session Hyprland socket and sends cursorpos:

$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket.sock

If the socket is unavailable it falls back to the hyprctl cursorpos subprocess. Screen bounds for edge-clamping are read from hyprctl monitors -j (Hyprland reports monitor x/y in logical coordinates but width/height in physical pixels, so the daemon divides by each monitor's scale to recover logical dimensions).

Per-app profiles

Hyprland's per-app tracking reads the .socket2 event stream and parses activewindow>>CLASS,TITLE lines, so per-application profiles work natively.

Setup

The installer detects Hyprland and adds the overlay window rules to the config Hyprland loads: hyprland.lua when it exists (Hyprland 0.55 and newer load it instead of hyprland.conf), otherwise custom/rules.conf (end-4's dots-hyprland) or hyprland.conf, in the syntax of the running Hyprland version. When it cannot add them, for example because the config is read-only or lives somewhere else, it prints the rules and says they need adding by hand.

To add them manually, use the block for your Hyprland version, then run hyprctl reload.

Hyprland 0.55 and newer, in hyprland.lua or a module it loads:

-- JuhRadial MX overlay window rules
hl.window_rule({
    name = "juhradial-overlay",
    match = { title = "^(JuhRadial MX)$" },
    float = true,
    pin = true,
    no_blur = true,
    no_shadow = true,
    no_anim = true,
    border_size = 0,
})

Hyprland 0.53 and newer with hyprland.conf, in hyprland.conf or a sourced file:

# JuhRadial MX overlay window rules
windowrule {
    name = juhradial-overlay
    match:title = ^(JuhRadial MX)$
    float = on
    pin = on
    no_blur = on
    no_shadow = on
    no_anim = on
    border_size = 0
}

Hyprland 0.52 and older, in hyprland.conf or a sourced file:

# JuhRadial MX overlay window rules
windowrulev2 = float,    title:^(JuhRadial MX)$
windowrulev2 = noblur,   title:^(JuhRadial MX)$
windowrulev2 = noborder, title:^(JuhRadial MX)$
windowrulev2 = noshadow, title:^(JuhRadial MX)$
windowrulev2 = pin,      title:^(JuhRadial MX)$
windowrulev2 = noanim,   title:^(JuhRadial MX)$

Tip

If the menu is drawn but blurred, bordered, or animated into place, the window rules above are missing. The float and pin rules are what keep the overlay where the daemon positions it.


COSMIC

COSMIC does not expose a dedicated cursor IPC, so the daemon reads the pointer through XWayland using XQueryPointer on the root window (the daemon loads libX11.so.6 directly and queries it whenever DISPLAY is set). The overlay is placed as a synchronized override-redirect XWayland surface.

Setup

Ensure XWayland is installed and running (it is enabled by default on COSMIC). No further configuration is required for the radial menu and cursor positioning.


Sway / wlroots

Sway (and other wlroots compositors) use the same XWayland XQueryPointer path as COSMIC. The session is identified by SWAYSOCK; cursor detection itself does not depend on the Sway IPC, it relies on XWayland.

Setup

Make sure XWayland is enabled in your Sway config (xwayland enable, which is the default), and that XWayland is installed. The overlay positions itself through XWayland once DISPLAY is set.

Note

As with all non-KDE/non-Hyprland Wayland sessions, per-application profiles rely on the xprop fallback and therefore only reflect XWayland clients, not native Wayland toplevels. Use a global profile if per-app switching does not behave as expected.


niri

niri exposes NIRI_SOCKET but has no cursor IPC, and it tiles XWayland toplevels rather than allowing free override-redirect placement. Since 0.4.5 the overlay therefore opens the menu on a Wayland layer-shell surface when gtk4-layer-shell is installed: the surface covers the output, the ring is drawn where the pointer enters it, and clicks outside the ring still reach the windows below. That path does not depend on XWayland at all and lands the ring where the cursor is on every output.

Setup

  1. Install gtk4-layer-shell. The installer installs it on Fedora, Arch and Debian/Ubuntu; on openSUSE it is skipped when the default repositories lack it, and an image-based install (install.sh --user on Bazzite or Fedora Atomic) does not layer it, so add it by hand there. With it present the native path is used automatically.
  2. Without the library the previous path is used: run xwayland-satellite so that DISPLAY is set in your niri session, and confirm echo $DISPLAY prints a value (for example :0) before launching JuhRadial MX. Without either, the daemon has no pointer position and falls back to screen-center.

Note

The layer-shell path was validated on a wlroots compositor; reports from real niri sessions are welcome on #22.


X11 (any desktop)

On a native X11 session, cursor detection is the most straightforward: the daemon queries the pointer through XQueryPointer (via libX11, since DISPLAY is always set on X11), with xdotool getmouselocation as an additional fallback. Screen bounds for edge-clamping come from xrandr (multi-monitor aware), falling back to xdotool getdisplaygeometry.

The overlay is a normal X11 window placed at the reported coordinates, so positioning is native and does not depend on XWayland translation.

Per-app profiles

X11 per-app tracking keeps xprop -spy _NET_ACTIVE_WINDOW running and reads WM_CLASS only when focus changes, so per-application profiles work natively on X11 without periodic process polling.

Setup

Install xdotool (used as the cursor and screen-bounds fallback) and xrandr for multi-monitor edge-clamping. These are typically already present on an X11 desktop.


How the daemon and overlay detect your session

The daemon and overlay both branch on environment variables rather than guessing. The overlay reads these flags at startup:

Flag Set when
IS_HYPRLAND HYPRLAND_INSTANCE_SIGNATURE is present
IS_GNOME XDG_CURRENT_DESKTOP contains GNOME
IS_COSMIC XDG_CURRENT_DESKTOP contains COSMIC
IS_KDE XDG_CURRENT_DESKTOP contains KDE or PLASMA
IS_SWAY SWAYSOCK is present
IS_NIRI NIRI_SOCKET is present
IS_X11 XDG_SESSION_TYPE equals x11
_HAS_XWAYLAND DISPLAY is present

If detection looks wrong (for example, the menu opens in the top-left corner or at screen center), the usual cause is a missing or misreported variable in your session. Confirm them:

echo "$XDG_CURRENT_DESKTOP $XDG_SESSION_TYPE"
echo "DISPLAY=$DISPLAY  HYPRLAND=$HYPRLAND_INSTANCE_SIGNATURE  NIRI=$NIRI_SOCKET"

Quick reference: what to check per desktop

Symptom Desktop Fix
Menu in top-left corner GNOME Enable the cursor extension, then log out/in (see GNOME section)
Menu blurred / animated / off-position Hyprland Add the overlay window rules
Menu at screen center niri Install gtk4-layer-shell, or run xwayland-satellite so DISPLAY is set
Menu off-position at non-100% scale KDE Plasma 6 Update to a current build (the identity cursor pass-through fixes fractional scaling)
Per-app profiles never switch GNOME Wayland Known limitation; use a global profile or a KDE/Hyprland/X11 session
Menu does not appear at all Any Confirm the daemon is running (pgrep juhradiald) and XWayland is available

For deeper diagnosis see Troubleshooting. For how the daemon, overlay, and D-Bus service fit together, see Architecture.