/* * Vencord, a Discord client mod * Copyright (c) 2026 Evandro * SPDX-License-Identifier: GPL-3.0-or-later */ import { Logger } from "@utils/Logger"; import definePlugin from "@utils/types"; import { AboutPanel } from "./about"; import { KeybindsStore, portalActive, portalTrigger, Shortcut } from "./discord"; import { adjustOptions, Callback, createDispatcher, Dispatcher, fixPortalState, KeyEventOptions, planRegistration, PortalState, Scheduler } from "./logic"; import { PATCHES } from "./patches"; import managedStyle from "./style.css?managed"; // Discord's own native keybind bridge (the object the KeybindsStore registers through) interface Bridge { inputEventRegister(id: number, shortcut: Shortcut, callback: Callback, options: KeyEventOptions): void; inputEventUnregister(id: number): void; } const dispatchers = new Map(); const scheduler: Scheduler = { now: () => performance.now(), setTimeout: (fn, ms) => setTimeout(fn, ms), clearTimeout: handle => clearTimeout(handle as ReturnType) }; const logger = new Logger("WaylandKeybinds"); export default definePlugin({ name: "WaylandKeybinds", description: "Makes Discord's keybinds work in every app on Linux Wayland: set them up in Discord's Keybinds page as usual and allow the system prompt", authors: [{ name: "Evandro", id: 281463271758888960n }], // only changes behaviour when Discord registers keybinds through the Wayland GlobalShortcuts portal enabledByDefault: true, patches: PATCHES, managedStyle, settingsAboutComponent: AboutPanel, wrapRegistration(bridge: Bridge, id: number, shortcut: Shortcut, callback: Callback, options: KeyEventOptions): [Callback, KeyEventOptions] { try { const active = portalActive(); const mode = planRegistration(options, active, active && portalTrigger(shortcut) !== ""); const adjusted = adjustOptions(options, mode); dispatchers.get(id)?.cancel(); const dispatcher = createDispatcher(callback, mode, () => { bridge.inputEventUnregister(id); bridge.inputEventRegister(id, shortcut, dispatcher.dispatch, adjusted); }, scheduler); dispatchers.set(id, dispatcher); return [dispatcher.dispatch, adjusted]; } catch (e) { logger.error("Failed to adapt keybind registration", e); return [callback, options]; } }, unregister(bridge: Bridge, id: number) { // stop probing first, or the next re-registration would bring a deleted keybind back dispatchers.get(id)?.cancel(); dispatchers.delete(id); bridge.inputEventUnregister(id); }, fixPortalState(state: S): S { try { const shortcut: Shortcut | undefined = KeybindsStore.getUserAgnosticState()?.[state.eventId]?.shortcut; return fixPortalState(state, shortcut?.length ? portalTrigger(shortcut) : undefined); } catch (e) { logger.error("Failed to check portal shortcut state", e); return state; } } });