Wayland Auto-Type with XDG Desktop Portals#13359
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a Wayland Auto-Type backend using XDG Desktop Portals (RemoteDesktop + GlobalShortcuts), adds UI support to configure portal-managed global shortcuts, and persists the RemoteDesktop restore token via the app config.
Changes:
- Add a new Wayland Auto-Type platform plugin that types via the XDG RemoteDesktop portal and stores a restore token for session persistence.
- Integrate the XDG GlobalShortcuts portal on Unix (NixUtils) and expose a “Configure…” button in settings when shortcuts are managed externally.
- Add DBus interface XMLs + CMake/vcpkg plumbing for portal interfaces and the xkbcommon dependency.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| vcpkg.json | Adds a new Linux/FreeBSD dependency for xkbcommon. |
| src/gui/osutils/OSUtilsBase.h | Adds hooks for “external shortcut configuration” and a slot to open a configurator UI. |
| src/gui/osutils/nixutils/NixUtils.h | Declares portal request helper + configurator-related overrides/state. |
| src/gui/osutils/nixutils/NixUtils.cpp | Implements GlobalShortcuts portal session creation, binding UI, and Wayland/X11 shortcut behavior changes. |
| src/gui/osutils/nixutils/dbus/org.freedesktop.portal.Session.xml | Adds DBus XML for the portal Session interface (Qt DBus codegen). |
| src/gui/osutils/nixutils/dbus/org.freedesktop.portal.Request.xml | Adds DBus XML for the portal Request interface (Qt DBus codegen). |
| src/gui/osutils/nixutils/dbus/org.freedesktop.portal.RemoteDesktop.xml | Adds DBus XML for the portal RemoteDesktop interface (Qt DBus codegen). |
| src/gui/osutils/nixutils/dbus/org.freedesktop.portal.GlobalShortcuts.xml | Adds DBus XML for the portal GlobalShortcuts interface (Qt DBus codegen). |
| src/gui/ApplicationSettingsWidgetGeneral.ui | Adds a “Configure…” button under Auto-Type shortcut settings. |
| src/gui/ApplicationSettingsWidget.cpp | Toggles shortcut widget vs. portal configure button based on OSUtils capability. |
| src/core/Config.h | Adds a new config key for the desktop portal restore token. |
| src/core/Config.cpp | Registers the restore token config directive (Local storage). |
| src/CMakeLists.txt | Adds Qt DBus interface code generation for the portal XMLs. |
| src/autotype/wayland/CMakeLists.txt | Introduces the Wayland Auto-Type plugin build target and links to xkbcommon/DBus. |
| src/autotype/wayland/AutoTypeWayland.h | Declares the Wayland Auto-Type platform plugin + executor. |
| src/autotype/wayland/AutoTypeWayland.cpp | Implements RemoteDesktop portal session lifecycle and key injection via NotifyKeyboardKeysym. |
| src/autotype/CMakeLists.txt | Adds the Wayland plugin subdirectory (currently under WITH_X11). |
| src/autotype/AutoType.cpp | Adds a guard for null plugin and queues matchActivated handling. |
| cmake/FindXkbcommon.cmake | Adds a custom find-module for xkbcommon (pkg-config or manual fallback). |
Comments suppressed due to low confidence (1)
src/autotype/wayland/AutoTypeWayland.cpp:171
- On
Start()failure you setm_errorand unlock, but you keepm_remoteDesktopSessionalive. This blocks retries becausetryStartSession()will treat the session as already started. Please close/reset the session on error to allow recovery and avoid using a half-initialized session object.
auto reply = s_remoteDesktopInterface->Start(QDBusObjectPath(m_remoteDesktopSession->path()),
"",
{
{QLatin1String("handle_token"), handleToken},
});
reply.waitForFinished();
if (reply.isError()) {
m_error = "Failed to start remote desktop session: " + reply.error().message();
m_startLock.unlock();
}
28df367 to
fac0cc2
Compare
|
I tested this branch on Tuxedo OS, which is currently on KDE Plasma 6.5.2. My only comment is that the three first options in the "Auto-Type" settings tab might be confusing for some people, since on Wayland there is no matching windows. |
|
Some problems occured when I use the fcitx5 input method. Wrong alphabet inputted on fcitx5 activatedConfirmed that keyboard-us layout is selected. It looks like fcitx5 receives the virtual keyboard events and corrupts or delays modifier state. Besides, after the auto-type operation, my shift key is pressed but not released properly.
I tried Fcitx5 wayland launcher or Fcitx5 in Virtual Keyboard of KDE settings, neither works.
When I switch to none, it works, so seem to be a IM compatibility problem. Input method state can't be changedWhen I use previous version of autoinput (using dbus-send script to trigger, with some modification with codex), I can use this script to manually switch to keyboard-us: fcitx5-remote -s keyboard-us && dbus-send --session --type=method_call --dest=org.keepassxc.KeePassXC.MainWindow /keepassxc org.keepassxc.KeePassXC.MainWindow.requestGlobalAutoTypeBut after global shortcut introduced, there is no way to inject my switch logic, so when I'm on rime, keepassxc use my fcitx to input "Chinese". Possible fixes / workarounds
|
|
@minortex Thank you for testing with a custom input method. Since KeePassXC sends keysyms that are directly derived with libxkbcommon from the unicode characters I believe the wrong conversion happens on the other side as KeePassXC does nothing regarding keyboard layouts or input methods and shouldn't be doing. You should try a dbus monitor command to see what's being sent exactly and if it matches the symbols of your password then you should file a bug against the desktop portal for converting them incorrectly with your input method. I'd be interested to hear if the symbols that KeePassXC sends are correct or not because that would confirm my hypothesis. |
fac0cc2 to
5364274
Compare
|
@Zahrun Thanks for the comments. I've now addressed this by hiding them if the plugin doesn't support window management. |
|
First of all, a huge thank you for this PR. I tested it on Arch Linux and the latest KDE Plasma, and it works flawlessly. All options work as expected and as they should. This was one of the more annoying features to be missing on Wayland, and to see that you made it work while cleaning up, plus extending the original PR (#10905), is seriously amazing. Thanks again and have a great day! |
5364274 to
d0238c1
Compare
|
@hifi Oh, the classic Linux desktop rabbit hole! 😂 Since keepassxc is emitting perfect keysyms, this is indeed an upstream/downstream issue with how the compositor and the Input Method Framework handle virtual keysym re-interpretation. If I report this to fcitx5 or kwin, it will likely end up in an endless game of pass-the-parcel regarding who owns the virtual keyboard timing. To make keepassxc's Wayland implementation genuinely bulletproof for CJK and non-US layout users without waiting years for upstream portal fixes, having a keycode-based injection toggle or a pre/post auto-type script hook inside keepassxc would be a lifesaver. It gives users the exact escape hatch they need when their desktop environment's IM framework decides to mess up the modifier states. |
|
Who receives and processes the dbus call? I suspect that is fcitx5. That is who gets the ticket to fix their implementation. Offering complex workarounds on our initial implementation is not the correct way to handle this. We already are working around awful handling of global shortcut setting across gnome versions. |
|
@droidmonkey Totally understand it's a pity to resolve different quirks on DEs. However, to isolate the issue, I tested three entirely different virtual keyboard backends in KDE system settings:
It will be easy for you to verify the bug since it is installed by default. Even under a standard US layout, you will see the modifier state corrupting. Since the same modifier/Shift corruption happens across all three frameworks, this is definitively a KDE/KWin/Portal bug, not an fcitx5 issue. Will report this to KDE, but please consider the impact on touchscreen users. Thanks for your help! |
d0238c1 to
d53542c
Compare
|
@minortex While I do understand your issue is very practical I don't think it would be worth the trouble adding sending keycodes when the only logical way to send characters of a password is with keysyms as you'd need to configure a virtual keymap within KeePassXC for it to convert characters to modifiers and keycodes which is the complete opposite of what it should be doing because it has no context where the output is going to. This should be done by the compositor which knows what layout is currently active and if there's something to do with input methods it must be a dance between them to figure that out. There are a few ways for you to go forward without doing this within KeePassXC:
These proxies could even be written fairly quickly with an LLM in something like python and you'd have a temporary fix for your whole system. The XDG Desktop Portal thing is quite flexible so you should be able to just configure your proxy as the backend and make your proxy directly call the real implementation. This of course assumes some programming knowledge but just throwing it out there. Also do note that KDE got a fix for keysym mishandling for this very reason last year in Plasma 6.5 so if you're on any older release it's known to be buggy. I'm currently on Debian trixie which has the bug but I only use a single layout so it doesn't affect me. |
d53542c to
eee5df8
Compare
|
All right, I ask llm to craft a script to resolve this problem. Here is the implemenetation strategy:
Hope this helps anyone else stuck in wayland and cjk limbo. My issue is fully resolved now, huge thanks to everyone for the helpful suggestions and insights. And the script: #!/bin/bash
# ==============================================================================
# KeePassXC Wayland Auto-Type Fix for KDE Plasma 6 & Fcitx 5
# ==============================================================================
# This script eliminates character-dropping, sequence-scrambling, and casing bugs
# caused by race conditions between KWin's virtual keyboard pipeline (text-input)
# and KeePassXC's simulated keysyms under Wayland.
#
# Added Watchdog: Prevents the script from hanging indefinitely and freezing the
# IME input state if Auto-Type is cancelled or KeePassXC is locked.
# ==============================================================================
# 1. Temporarily unbind the IME pipeline to restore pure hardware-level input timing
qdbus org.kde.KWin /VirtualKeyboard org.kde.kwin.VirtualKeyboard.enabled false
sleep 0.05
# 2. Trigger KeePassXC Global Auto-Type via KDE 6's precise global shortcut pathway
qdbus org.kde.kglobalaccel /component/org_keepassxc_KeePassXC \
org.kde.kglobalaccel.Component.invokeShortcut "autotype"
# 3. Dynamic Traffic Monitor (Spawned in background)
# Wait for typing to begin, then track simulated keystrokes.
# It blocks until it detects a 0.3-second "silence" (stream timeout),
# which dynamically adapts to passwords of any length.
(
while read -r line; do
while read -t 0.3 -r next_line; do
true
done
break
done < <(dbus-monitor --session "type='method_call',interface='org.freedesktop.portal.RemoteDesktop',member='NotifyKeyboardKeysym'" | grep --line-buffered "NotifyKeyboardKeysym")
) &
MONITOR_PID=$!
# 4. Fail-Safe Watchdog (Spawned in background)
# If typing doesn't finish within 15 seconds (e.g., user cancels or database is locked),
# it forcibly terminates the monitor to prevent freezing the system's input method.
(
sleep 15
if kill -0 $MONITOR_PID 2>/dev/null; then
echo "[KeePassXC Fix] Auto-Type timed out or cancelled. Activating fail-safe." >&2
kill $MONITOR_PID 2>/dev/null
fi
) &
WATCHDOG_PID=$!
# Wait for the monitor to finish (either normally or killed by the watchdog)
wait $MONITOR_PID 2>/dev/null
# 5. Re-enable KWin's virtual keyboard component now that typing is finished
qdbus org.kde.KWin /VirtualKeyboard org.kde.kwin.VirtualKeyboard.enabled true
sleep 0.1
# 6. Fix Lost Input Context (KDE 6 Wayland Ghost Bug)
# Toggling the component destroys the Wayland socket connection. To prevent the active
# window from being stuck in "English-only" mode until manually clicked, we briefly
# trigger a 20ms "Show Desktop" cycle. This forces KWin to refresh the focus state
# and seamlessly rebuilds the text-input context for the active input field.
qdbus org.kde.KWin /KWin org.kde.KWin.showDesktop true
sleep 0.02
qdbus org.kde.KWin /KWin org.kde.KWin.showDesktop false
# ==============================================================================
# 7. Housekeeping: Process Group Clean-up
# ==============================================================================
# Clear exit traps to prevent potential infinite loops
trap - EXIT
# Kill the watchdog timer if it's still running
kill $WATCHDOG_PID 2>/dev/null || true
# Nuclear Option: Terminate the entire process group (-$$)
# This instantly wipes out any remaining sub-processes (like dbus-monitor or grep)
# generated by this script, ensuring no zombie or orphan processes are left behind.
kill -- -$$ 2>/dev/null || trueDisable the original keybinding, and simply bind this script to KDE's shortcuts and it will work. |
|
@minortex for our awareness, what version of plasma are you running? |
|
The specific MRs were: xdg-desktop-portal-kde/!342, plasma-wayland-protocols/!92, and kwin/!6992, for KDE bug 489021. @minortex If you post a followup bug to KDE, please link to it here. |
| bool NixUtils::externalGlobalShortcutsConfigurator() | ||
| { | ||
| return QApplication::platformName() == "wayland"; | ||
| } |
There was a problem hiding this comment.
We do this check elsewhere by testing for WAYLAND_DISPLAY environment variable. Let's rename this function "isWayland" or similar and replace those env var tests with this.
There was a problem hiding this comment.
This isn't a NixUtil special, though, the idea was to have a generic thing to know if external configuration is needed but we can make it a special case because nothing else needs this.
There was a problem hiding this comment.
There's a refactoring issue if we're going to use this everywhere to check for Wayland as core would depend on GUI OSUtils. It'd also be good for it to be an inline static function instead of requiring an instance.
There was a problem hiding this comment.
If its too much just ignore this for now or move it to core/tools.cpp
There was a problem hiding this comment.
That's probably a better place. I'll do it when I get a chance.
There was a problem hiding this comment.
Ah, it hits another problem. We need to import QApplication and it's the GUI companion to QCoreApplication so even that is somewhat problematic to import in core. We probably need to refactor this somehow else to keep the GUI boundaries but normalize Wayland detection.
|
Just minor comments, code is clean and readable, great work! |
eee5df8 to
86319c7
Compare
I looked closely into the diffs of those MRs, and while they indeed removed the hardcoded KEY_LEFTSHIFT injection from portal, then re-introduced the exact same KEY_LEFTSHIFT automated injection logic inside KWin's backend. In the MRs you mentioned, It solved the problem that European/American keyboard with different key places. But they don't take virtual keyboard into consideration. Therefore, my problem is not related to that. It's necessary to explain how the IME works:
In KDE, simulating keyboard input will conflict with IME, because IME will process the original key input, which causes the problem. I thought it's never be a good idea to simulate the keyboard press event as the input event, causing endless bugs. Look at windows, they treats the input as unicode so auto type works well, KeePassXC doesn't face such problem. This PR is going to be merged and it's not the KeePassXC team's responsibility, the only real solution is to request KDE's team to implement a better accessibility framework, and I won't clutter this thread any further. |
Needs an implementation of global shortcuts to bind the global trigger and an implementation of remote desktop to type entries.
86319c7 to
7f7bbe5
Compare
da214ed to
7989994
Compare
|
@droidmonkey I added support for showing the bound trigger from the desktop and integrated it back into the shortcut widget instead of having a separate button. Focus with the field works correctly and clicking or hitting return will try to open the (re)configuration window of the desktop. Left it as a fixup for easier re-reviewing. This should be as good as it gets on KDE now. On GNOME you do see the binding and the field reacts to it live so the only missing feature on GNOME is reconfiguration from within KeePassXC. |
|
If someone wants to test this on something else than KDE or GNOME, I whipped up a minimal portal in python that can bind global shortcuts on any compositor and implements enough remote desktop on most: https://github.com/hifi/xdg-desktop-portal-pyrtal I've tested this on labwc and it works fine for me so expecting it to work on any wlroots based compositor and Hyprland. |


This is a major rewrite of #10905 with emphasis on error handling and usability. The original PR did not have any way to bind the global trigger.
There are definitely still shortcomings but the intention is to keep hammering this until it can be merged and keep building on it to improve the experience.
Features implemented:
Fixes #2281
Closes #10905
Screenshots
New configuration options if the plugin is loaded:

Initial configuration dialog on KDE:

Testing strategy
There are multiple quirks in place for global shortcuts, mostly because of GNOME, but if all stars align with fresh enough KDE everything works like one would expect.
Type of change