98 lines
5.7 KiB
Markdown
98 lines
5.7 KiB
Markdown
<p align="center">
|
|
<img src="./assets/readme/hero.svg" width="100%" alt="WaylandKeybinds: Discord global mute, deafen and push-to-talk that keep working when GNOME drops the key release">
|
|
</p>
|
|
|
|
<p align="center">
|
|
<b>Home:</b> <a href="https://git.evandro.dev.br/evandro/vencord-wayland-keybinds">git.evandro.dev.br/evandro/vencord-wayland-keybinds</a>, on my self-hosted Gitea. GitHub only carries this copy for reach.
|
|
</p>
|
|
|
|
**WaylandKeybinds** is a [Vencord](https://vencord.dev) plugin for the Discord desktop app on Linux. Discord already hands your keybinds to GNOME through the `GlobalShortcuts` portal, but GNOME loses the key release whenever a modifier is let go first, and Discord's mute and deafen only act on the release. The plugin fixes this inside Discord's own **Keybinds** screen: whatever key you choose there becomes global, like Discord on Windows.
|
|
|
|
<p align="center">
|
|
<img src="./assets/readme/proof.svg" width="100%" alt="Timeline of one Ctrl+Shift+F10 hold with Ctrl+Shift let go first: GNOME sends a press and key repeats but no release; Discord alone never toggles and ignores the next press; with WaylandKeybinds the mute toggles on the press and silence after a re-registration means the key was released">
|
|
</p>
|
|
|
|
Measured on GNOME 50 with Discord 1.0.168: every press toggled the mute exactly once, 11 to 14 ms after GNOME's signal, including a 6-second hold that GNOME repeated 57 times.
|
|
|
|
## Why Discord's keybinds break on GNOME
|
|
|
|
- GNOME's compositor looks up a shortcut's release using the modifiers still held at that moment. Let Ctrl or Shift go before the main key and the release never matches, so the portal never sends `Deactivated` ([`process_event`](https://gitlab.gnome.org/GNOME/mutter/-/blob/main/src/core/keybindings.c)).
|
|
- Mute, deafen and most other toggles fire on the key **release** only.
|
|
- Discord's native module ignores further presses until it sees a release, so after one lost release the next gesture disappears too.
|
|
- GNOME describes the trigger as `Press <Shift><Control>F10` while Discord expects `CTRL+SHIFT+F10`, so every working shortcut is flagged as different from the system one.
|
|
|
|
## What the plugin does
|
|
|
|
- Toggle actions (mute, deafen, streamer mode and the rest) fire on the **press**.
|
|
- One second after a press it re-registers the shortcut, which clears Discord's stuck state, then listens for 300 ms. While the keys are held, GNOME's key repeat answers within a few milliseconds; silence means they were released. That is how push-to-talk stops even when the release is lost. A real release still stops it at once.
|
|
- It reads GNOME's trigger format, so the Keybinds screen stops warning about a mismatch.
|
|
- It changes nothing unless Discord registers the keybind through the Wayland portal. It runs in the renderer only: no native code, no new dependency, no DOM clicks.
|
|
|
|
## Install
|
|
|
|
You need a [Vencord source checkout](https://docs.vencord.dev/installing/), because user plugins are compiled into your build.
|
|
|
|
```sh
|
|
cd /path/to/Vencord
|
|
git clone https://git.evandro.dev.br/evandro/vencord-wayland-keybinds.git src/userplugins/waylandKeybinds.discordDesktop
|
|
pnpm build
|
|
pnpm inject
|
|
```
|
|
|
|
Restart Discord. The plugin is on by default; you can find it as **WaylandKeybinds** in Vencord's plugin list. Its info panel shows whether the fix is active, how many keybinds GNOME has registered, and a button that opens Discord's Keybinds page.
|
|
|
|
> [!IMPORTANT]
|
|
> Keep the folder name `waylandKeybinds.discordDesktop`. The suffix limits the plugin to the Discord desktop app, the only place where Discord's native keybind module exists.
|
|
|
|
## Use
|
|
|
|
1. Open Discord **User Settings → System → Keybinds** and click **Add a Keybind**.
|
|
2. Pick an action (Toggle Mute, Toggle Deafen, Push to Talk…) and press your combination.
|
|
3. GNOME asks once to allow the shortcut. Click **Add**.
|
|
|
|
Change the keys whenever you want on the same screen; GNOME asks again for the new combination.
|
|
|
|
> [!TIP]
|
|
> For push-to-talk, a single key without modifiers (Pause, F13 to F24) keeps both press and release reliable.
|
|
|
|
On Wayland, Discord records only combinations the portal can represent: one main key plus optional modifiers. Mouse buttons cannot be global shortcuts there.
|
|
|
|
## Compatibility
|
|
|
|
| Component | Tested with |
|
|
| --- | --- |
|
|
| Discord | 1.0.168 (Electron 44) |
|
|
| Vencord | 1.15.15 |
|
|
| GNOME | 50.5 (mutter 18, xdg-desktop-portal-gnome 50) |
|
|
| KDE Plasma, Hyprland | Not tested. The portal path is the same, the release behavior may differ. |
|
|
|
|
## Limits
|
|
|
|
- Release detection relies on GNOME's key repeat (500 ms, then every 30 ms by default). With key repeat turned off, push-to-talk ends about 1.3 s after the press even while you hold it.
|
|
- When the release is lost, push-to-talk stops up to about 1.3 s after you let go.
|
|
- The live checks covered toggle mute; push-to-talk is covered by the state-machine tests.
|
|
- Soundboard hold stays off the portal on Linux. That is Discord's own choice.
|
|
- The patches target Discord's minified code and can break with Discord updates. Run `check-patches.mjs` before you rebuild.
|
|
|
|
## Development
|
|
|
|
From the root of your Vencord checkout:
|
|
|
|
```sh
|
|
# one test case per independent path of every function in logic.ts
|
|
pnpm exec esbuild src/userplugins/waylandKeybinds.discordDesktop/logic.test.ts \
|
|
--bundle --platform=node --format=esm --outfile=/tmp/wk-test.mjs
|
|
node --test /tmp/wk-test.mjs
|
|
|
|
# apply the patches to the Discord build that discord.com serves right now
|
|
node src/userplugins/waylandKeybinds.discordDesktop/check-patches.mjs
|
|
```
|
|
|
|
The design notes, measurements and acceptance criteria (in Portuguese) are in [`specs/2026-11-01-wayland-keybinds.md`](specs/2026-11-01-wayland-keybinds.md).
|
|
|
|
---
|
|
|
|
<p align="center">
|
|
Made by Evandro · <a href="https://git.evandro.dev.br/evandro">git.evandro.dev.br/evandro</a> · <a href="https://evandro.dev.br">evandro.dev.br</a>
|
|
</p>
|