Listening to keyboard events without trapping them

2019-03-15 04:42发布

问题:

I'm writing an command-line application which listens for Control key release events in X Windows and alerts another process when it detects them.

Being new to GNU/Linux, I'd prefer avoiding to fumble with GCC and therefore I'm looking for a scripting-based solution. Since I know a bit of Python, it seemed natural to go for a Python-based solution, and after scavenging the Internet for examples and reading Python Xlib docs, I've put together this programs which works, but with a caveat: it traps events instead of just listening for them (I mean such events are not passed anymore to the application which they were directed to in the first place).

I've tracked down Control key codes by running "xev". Since I've remapped my modifier keys, on your system they may be different.

To keep things simple, I've left out the code which deals with the external process.

Thank you for your help.

Software:

  • Python 2.7.2

  • Python Xlib 0.15 RC1

  • Perl v5.10.1

  • Debian GNU/Linux version: 6.0.3

  • Kernel version: Linux debian 2.6.32-5-686

EDIT: What I can't figure out is that keyboard events do not get trapped unless they are processed (in my programs, this means the line 'print "KeyRelease"' gets executed). Since in my code I don't call any method either on Xlib or on the event object, I don't understand where the difference in processing lies.

EDIT2: Suggestions about alternative solutions besides using Xlib are also welcome.

EDIT3: I know Perl too, and suggestions about Perl libraries which could help are welcome too, as long as they don't require recent versions of system libraries, since Debian notoriously lags behind when it comes to packages available in its repositories, and compiling and installing last versions of libraries can be difficult if they have many dependencies (I've tried installing PyGTK, but gave up after failing to reference an up-to-date GLib I had installed).

    #!/usr/bin/env python

    from Xlib.display import Display
    from Xlib import X

    Control_R  = 64 # Keycode for right Control.
    Control_L  = 108 # Keycode for left Control.
    keycodes = [Control_R, Control_L] # Keycodes we are listening for.

    # Handle X events.
    def handle_event(event):
        # Let us know whether this event is about a Key Release of
        # one of the key we are interest in.
        if event.type == X.KeyRelease:
            keycode = event.detail
            if keycode in keycodes:
                print "KeyRelease"

    # Objects needed to call Xlib.
    display = Display()
    root = display.screen().root

    # Tell the X server we want to catch KeyRelease events.
    root.change_attributes(event_mask = X.KeyReleaseMask)

    # Grab those keys.
    for keycode in keycodes:
        root.grab_key(keycode, X.AnyModifier, 1, X.GrabModeAsync, X.GrabModeAsync)

    # Event loop.
    while 1:
        event = root.display.next_event()
        handle_event(event)

回答1:

Thanks to the pykeylogger library mentioned by Croad Langshan, and to the helpful example code provided by Tim Alexander, the author of such library, I've been able to change my program to:

    #!/usr/bin/env python

    from pyxhook import HookManager

    watched_keys = ["Control_R", "Control_L"]

    def handle_event (event):
        if event.Key in watched_keys:
            print "KeyRelease"


    hm = HookManager()
    hm.HookKeyboard()
    hm.KeyUp = handle_event
    hm.start()

This program accomplishes my goal without any issue. You can read the fields of the "event" object for further information about the event (see source code of "pyxhook.py").



回答2:

You need to use the XRecord extension. It can be used with pyxlib (pykeylogger mentioned in the other answer uses this), or by wrapping libX11 and libXtst via ctypes (as I did in synaptiks).

Note however, that programming with xrecord (and to some degree also with XLib in general) is somehwat hard, because the API is badly documented, and quite baroque and counter-intuitive.



回答3:

Presumably this project must have code that solves that problem:

http://sourceforge.net/apps/mediawiki/pykeylogger/index.php?title=Main_Page



回答4:

Note that the XRecord extension continues to be broken in a few distributions. The reason I hadn't gone back and updated that library for a while was because it was broken in Ubuntu for multiple releases. There's a way to do it as well with an XInput overlay, (I'm told) but I never pursued that because I didn't want to deal with an overlay rather than hooking the X events directly.

Comment back if there's any issues with using the code in the pyxhook lib, I tried to make it as simple/robust as possible, but I may have missed stuff when putting it together. It was a while ago.