There’s no good standalone controller-driven on-screen keyboard on Linux. Steam’s keyboard requires Steam running. Other virtual keyboards (Onboard, Florence) need a mouse pointer or are dead projects. I wanted something that takes D-pad and stick input directly, like a console, but can be launched independently of specific games or platforms.
So I built one written in Go with SDL2 originally, but I have a vastly updated SDL3/Wayland branch that’s WIP and ready for testing. It works as a daemon with a configurable controller combo to toggle show/hide.
The SDL3/Wayland branch adds layer-shell support, fullscreen-aware positioning, and a lot of improvements over the v1.1 release. Getting Wayland functional doubled the codebase. X11 is solid, but I need more testing in Wayland compositors (Sway, Hyprland, KDE, GNOME) before merging since I don’t run Wayland on my machines.
If you’re on Wayland with a gamepad, I’d appreciate testers. Bug reports and PRs welcome. AUR packages won’t be updated until the SDL3 branch is confirmed stable and I bump to v2.

Features :
- Full QWERTY with shortcuts row (undo, redo, cut, copy, paste, select all, Alt+Tab, media keys)
- D-pad navigation with stick-driven mouse cursor
- Modifier support (Shift, Ctrl, Alt, Super, Caps) with visual status indicators
- Button-hold key repeat for backspace, space, enter
- Alt+Tab key for multiple Tab presses
- Auto-reconnect on controller disconnect/timeout
- Configurable button mapping, 60 themes, adjustable opacity
- Sub-1% CPU idle, under 4% during active input
Have you already tested this on Steam Deck? That would be awesome.
Nope. Just on my Arch systems (XFCE w/ X11) & Void Linux w/ X11. I am planning to run through some manual tests in KDE Plasma which should be applicable to Steam Deck and CatchyOS, but I need to build a proper test env. Ive done simple tests in Sway and KDE with nested sessions, but those are just spot checks not full testing. I need to boot into real systems for full testing.
I’ve been wanting a better on-screen keyboard for my TV gaming box. The Steam on-screen keyboard gets cut off at the edges of the screen when I run KDE at 1.25x DPI scale.
Is there any chance that this would work as a Flatpak? The machine I want to use this on runs Bazzite, though it’d be helpful for running it on other distros too.
Doing so defies the Flatpak security model since the app needs raw evdev and uinput device access. Since Bazzite has a read-only root, you’ll need to layer the runtime dependencies:
rpm-ostree install SDL3 SDL3_ttfThen reboot. The udev rule for uinput access goes in /etc which is writable on Bazzite:
sudo cp gamepad-osk.udev /etc/udev/rules.d/80-gamepad-osk.rules sudo udevadm control --reload-rulesAfter that you can build inside a distrobox and run it from ~/.local/bin. I think these will work to build the current branch, I’m not on Bazzite (Fedora):
distrobox create --name build --image fedora:41 distrobox enter build sudo dnf install golang SDL3-devel SDL3_ttf-devel libX11-devel wayland-devel git clone -b sdl3-migration https://github.com/0x90shell/gamepad-osk.git cd gamepad-osk go build -o gamepad-osk . cp gamepad-osk ~/.local/bin/ exitPromptfont is optional (shows controller button icons on keys), but I recommend it. To install:
wget https://github.com/Shinmera/promptfont/raw/main/promptfont.ttf mkdir -p ~/.local/share/fonts cp promptfont.ttf ~/.local/share/fonts/ fc-cacheIs it theoretically possible for an on-screen keyboard to not need raw device access?
Yeah, if it only handles the display side and relies on an accessibility framework like AT-SPI or IBus for input injection. That’s how Onboard and GNOME’s built-in keyboard work. But those frameworks need a cooperative desktop environment and don’t work consistently across compositors.
This tool needs raw device access because it does more than just display a keyboard:
- Reads gamepad input directly from evdev (d-pad, sticks, buttons, triggers). Accessibility frameworks don’t handle gamepad input, only keyboard/mouse.
- Injects keyboard and mouse events via uinput. AT-SPI/IBus can inject keystrokes but not mouse movement or arbitrary key combos like Ctrl+Z.
- Grabs the gamepad exclusively while visible so input doesn’t bleed to the game. No framework exposes device grabbing.
- Emulates a mouse cursor from analog stick input. Continuous analog-to-mouse translation isn’t something accessibility buses are built for.
TL;DR
Raw evdev/uinput is what lets it work standalone on any compositor without depending on desktop-specific APIs. The trade-off is device permissions instead of accessibility bus access.



