Skip to content

Add KDE Wayland native hotkey support and fix clipboard paste#486

Merged
gabrielste1n merged 2 commits into
OpenWhispr:mainfrom
xAlcahest:feature/kde-wayland-native-shortcuts
Mar 23, 2026
Merged

Add KDE Wayland native hotkey support and fix clipboard paste#486
gabrielste1n merged 2 commits into
OpenWhispr:mainfrom
xAlcahest:feature/kde-wayland-native-shortcuts

Conversation

@xAlcahest

Copy link
Copy Markdown
Collaborator

globalShortcut.register() doesn't work on KDE native Wayland, the compositor blocks global input grabs. #470 forced XWayland as a workaround but broke clipboard paste (X11/Wayland clipboard desync).

Adds kdeShortcut.js that registers hotkeys through KGlobalAccel D-Bus and listens for globalShortcutPressed. Same approach as gnomeShortcut.js and hyprlandShortcut.js. KDE still runs on XWayland for clipboard, hotkeys go through KDE's shortcut system.

Clipboard writes on KDE use xclip/xsel instead of wl-copy. Both added as recommended deps in deb/rpm. Diagnostics check in Settings for KDE users missing xclip.

GNOME and Hyprland paths unchanged.

Supersedes #470

@xAlcahest
xAlcahest requested a review from gabrielste1n March 23, 2026 03:05

@gabrielste1n gabrielste1n left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, this follows the existing GNOME/Hyprland shortcut pattern well. Left a few comments, mostly around cleanup paths and one potential silent failure.

Comment thread src/helpers/kdeShortcut.js Outdated
for (const slotName of this.registeredSlots) {
const actionId = [COMPONENT_NAME, "OpenWhispr", slotName, `OpenWhispr ${slotName}`];
try {
this.kglobalaccel?.unRegister(actionId);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unRegister() is async (D-Bus call) but it's called synchronously here without awaiting, and then bus.disconnect() runs right after the loop. The unregister calls will probably never actually complete since the bus gets torn down immediately.

If this is intentional for app shutdown and you're ok with best-effort cleanup, a comment would help. Otherwise you'd want to await these or at least add a small delay before disconnecting.

// Register action then set shortcut
await this.kglobalaccel.doRegister(actionId);
const result = await this.kglobalaccel.setShortcut(actionId, [qtKey], 0x02);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If _listenForComponent() fails internally, it catches the error and just logs it. But registerKeybinding() still returns true here. So you'd end up with a keybinding that's registered in KDE but the app never receives press events because the component listener didn't get set up.

Might want to either return a boolean from _listenForComponent and check it here, or let the error propagate up.

Comment thread src/helpers/kdeShortcut.js Outdated

iface.on("globalShortcutPressed", (componentUnique, shortcutUnique, timestamp) => {
debugLogger.log("[KDEShortcut] Shortcut pressed", { componentUnique, shortcutUnique });
// KGlobalAccel may send actionUnique or actionFriendly depending on version

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment and the _findCallbackByFriendlyName fallback suggest we're not totally sure what KGlobalAccel sends as shortcutUnique. Have you tested this on a real KDE system? Would be good to nail down which value we're actually getting so we're not relying on the fallback in production.

debugLogger.log(
"[HotkeyManager] KDE keybinding failed, falling back to globalShortcut"
);
this.useKDE = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both here and in the catch block below, when KDE registration fails you set useKDE = false and fall back to globalShortcut, but kdeManager is still alive. Should probably also do this.kdeManager.close() and this.kdeManager = null before falling back, otherwise you have a half-initialized manager hanging around.

Comment thread src/helpers/ipcHandlers.js Outdated
return {
isUsingGnome: this.windowManager.isUsingGnomeHotkeys(),
isUsingHyprland: this.windowManager.isUsingHyprlandHotkeys(),
isUsingKDE: this.windowManager.isUsingKDEHotkeys?.() || false,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ?. is inconsistent with the lines right above it. isUsingGnomeHotkeys() and isUsingHyprlandHotkeys() don't use optional chaining. Since isUsingKDEHotkeys was added to WindowManager in this same PR, it'll always exist.

const { getYdotoolStatus } = require("./ensureYdotool");
return getYdotoolStatus();
const status = getYdotoolStatus();
const { execFileSync } = require("child_process");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this require is inside the IPC handler so it runs on every get-ydotool-status call. Would be cleaner to hoist it to the top of the file with the other requires.

@gabrielste1n
gabrielste1n merged commit 0892c71 into OpenWhispr:main Mar 23, 2026
6 checks passed
@gabrielste1n
gabrielste1n deleted the feature/kde-wayland-native-shortcuts branch March 23, 2026 15:46
gabrielste1n added a commit that referenced this pull request Mar 23, 2026
Revert all hotkeyManager.js changes — PR #486's KDE D-Bus as primary
path is confirmed working. Only keep windowConfig.js fixes (overlay
window type and always-on-top for KDE Wayland) and main.js comment.
charian47 pushed a commit to charian47/openwhispr that referenced this pull request Apr 25, 2026
…native-shortcuts

Add KDE Wayland native hotkey support and fix clipboard paste
charian47 pushed a commit to charian47/openwhispr that referenced this pull request Apr 25, 2026
Revert all hotkeyManager.js changes — PR OpenWhispr#486's KDE D-Bus as primary
path is confirmed working. Only keep windowConfig.js fixes (overlay
window type and always-on-top for KDE Wayland) and main.js comment.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants