sway icon indicating copy to clipboard operation
sway copied to clipboard

Sway not registering global hotkeys from other applications under certain conditions

Open Ghosthree3 opened this issue 4 years ago • 12 comments

  • Sway Version:

    • sway version 1.5-36c3c222 (Jul 19 2020, branch 'master')
  • Debug Log:

    • sway.log
    • (Nothing in this is useful/got added while the issue was happening)
  • Configuration File:

    • bindsym Mod4+Return exec kitty
  • Description: Sway seems to not be catching or sending key presses (or their release) which are global hotkeys to their relevant application when certain windows (or lack of any window) is currently selected. In my use case the issue is the registration of the Push to Talk hotkey in both mumble and discord (so not isolated to discord because it's terrible or something). The hotkey fails to work whenever my active window is either a blank workspace or mpv. At first I thought that the issue was sway only registering/sending the key press when any window was selected and the bug was only in the case of there being no window at all (blank workspace), but then I noticed it happening whenever I selected/was hovering over my mpv window as well, so I'm really not sure what's going on. I tested just about every type of keybind I could think of in case it was exclusively an issue with the LCTRL key, but LALT, LSHIFT, F1 and G all have the same problem.

  • Reproduction steps:

    • Launch sway using the one line config so I could use the terminal.
    • Open terminal and run discord.
    • I have three monitors and at this point monitor one had the terminal running discord, monitor two had discord, and monitor three was an empty workspace.
    • Join a channel and then try pressing my push to talk hotkey while moving my mouse over different windows/areas.
    • Push to talk bind registered and voice activated/deactivated when the selected window (via mouse hover) was either the terminal on monitor one or discord on monitor two, but would either not activate or would remain activated (stuck down) while I had my mouse on monitor three (which had nothing open).
    • Quit and exit sway.

Ghosthree3 avatar Jul 20 '20 00:07 Ghosthree3

AFAIK the Push to talk for discord and mumble are by default implemented using X11 global keybinds, these will only work as long as an X11 application is currently focused. This is in line with your report, since mpv should be Wayland-native and with an empty workspace there is no selected application. For mumble, you can use the mumble DBUS interface, sway has the --no-repeat keybind option for this, see https://github.com/swaywm/sway/pull/5132. I think there may be no solution for discord, but YMMV.

Emantor avatar Jul 20 '20 06:07 Emantor

Is there anything sway can do about this then or is it a wayland issue (and thus I should look to take this elsewhere)? Is the issue that xwayland and wayland are separate and inputs aren't shared between the two interfaces or does wayland lack the functionality altogether? If so, looking forward to when these applications are using wayland by default and not the compatibility layer of xwayland, how are you supposed to use a feature like this?

Ghosthree3 avatar Jul 20 '20 06:07 Ghosthree3

Wayland does not support global keybindings at all. For now the preferred way is for applications to provide a DBUS or CLI interface. Then you define a keybinding in the compositor that uses the cli or dbus.

I think there may be no solution for discord, but YMMV.

You could try a sway keybinding with xdotool keydown/keyup events.

Edit: KDE seems to use the XDG Desktop Actions and allows assigning hotkeys for them: https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#extra-actions

progandy avatar Jul 20 '20 07:07 progandy

Related: https://github.com/swaywm/sway/issues/4937

Xyene avatar Jul 21 '20 00:07 Xyene

That would work, except that at least in my case I'd prefer it if the key was sent to the matching window in addition to whatever happens normally. It'd be quite bad for my LCTRL to be permanently swallowed because it's getting sent to the discord window. It also doesn't quite work when I want the behavior for multiple windows (say I also want it sent to mumble as well, potentially at the same time).

Ghosthree3 avatar Jul 21 '20 01:07 Ghosthree3

Posting here so others may find this workaround: the following script works for the specific use-case of getting push-to-talk on Discord.

import sys
import evdev

from Xlib import X, XK, display
from Xlib.ext import xtest

display = display.Display()
device = evdev.InputDevice(sys.argv[1])
key = display.keysym_to_keycode(XK.string_to_keysym('Control_L'))

def press():
    xtest.fake_input(display, event_type=X.KeyPress, detail=key)
    display.flush()

def release():
    xtest.fake_input(display, event_type=X.KeyRelease, detail=key)
    display.flush()

try:
    for event in device.read_loop():
        if event.type == evdev.ecodes.EV_KEY:
            if event.code == 29:
                print(key)
                if event.value == 1:
                    press()
                elif event.value == 0:
                    release()
                else:
                    continue

                print("Forwarding %s" % evdev.categorize(event))
except KeyboardInterrupt:
    release()

Needs python-evdev and python-xlib (the latter, AFAICT, is Python 2-only). Needs sudo or being in the input group. Takes the /dev/input/eventX device corresponding to a keyboard as its only parameter.

This works by just listening to raw evdev events and forwarding them off to X11.

Xyene avatar Jan 04 '21 07:01 Xyene

I'm going to list my much simpler solution here too. This is just a button that mutes your mic completely:

.config/sway/config

bindsym f4 exec pactl set-source-mute @DEFAULT_SOURCE@ toggle

Not that sway users don't know how to conf, but it took me a while to even think of this option. It helps if you have a mic icon in your top bar (waybar in my case) so you actually know if your mic is on.

KristerV avatar Feb 16 '21 09:02 KristerV

This only solves the one case of using a microphone though, while good, it does nothing to assist with for example pressing Ctrl-F10 to begin recording in OBS.

Ghosthree3 avatar Feb 16 '21 10:02 Ghosthree3

This can be used for OBS: https://github.com/muesli/obs-cli

emersion avatar Feb 16 '21 10:02 emersion

I think this issue should be closed in favour of #4937. It has already been said here that Wayland doesn't allow global keybindings, and that applications should provide command-line or dbus interfaces.

Just to add another workaround: for applications running under xwayland (which is the case for Discord, for now), forwarding the keys to X works fine, e.g.:

bindsym Control_R exec xdotool keydown Control_R
bindsym --release Control_R exec xdotool keyup Control_R

Update: xdotool no longer works with XWayland

cauebs avatar Apr 10 '21 00:04 cauebs

I think this issue should be closed in favour of #4937. It has already been said here that Wayland doesn't allow global keybindings, and that applications should provide command-line or dbus interfaces.

Just to add another workaround: for applications running under xwayland (which is the case for Discord, for now), forwarding the keys to X works fine, e.g.:

bindsym Control_R exec xdotool keydown Control_R
bindsym --release Control_R exec xdotool keyup Control_R

I agree that #4937 is the way forward. The xdotool trick you supplied almost works, but the key isn't released when another key is pressed. I'm using Control_L, and if I do for example, Ctrl+T to open a tab, the Ctrl key gets stuck down until I press it again on it's own. I'm using the Python script method that Xyene posted, it works, it's just too bad it's not flexible. Hopefully one day it won't be necessary at all.

Ghosthree3 avatar May 30 '21 15:05 Ghosthree3

In gnome-shell using wayland global hotkeys from X apps works, why do need a work around for sway?

Edit: I think XWayland lets X to X globals work. its' not unique to Gnome.

YellowOnion avatar Jun 06 '22 10:06 YellowOnion