Listening for Global Key-Combinations in Python on Linux

Listening for global key-combinations in python on Linux

I do not know of any libraries that are designed to be extended. However as your link stated the backend of pykeylogger gives an example of how to do it, but it does seem a little overcomplicated for you would need.

pykeylogger uses the python-xlib module to capture keypresses on the X display. Someone has already created a lighter example of how to go through this on pastebin. Below is the source from it copied here as-is.

from Xlib.display import Display
from Xlib import X
from Xlib.ext import record
from Xlib.protocol import rq

disp = None

def handler(reply):
""" This function is called when a xlib event is fired """
data = reply.data
while len(data):
event, data = rq.EventField(None).parse_binary_value(data, disp.display, None, None)

# KEYCODE IS FOUND USERING event.detail
print(event.detail)

if event.type == X.KeyPress:
# BUTTON PRESSED
print("pressed")
elif event.type == X.KeyRelease:
# BUTTON RELEASED
print("released")

# get current display
disp = Display()
root = disp.screen().root

# Monitor keypress and button press
ctx = disp.record_create_context(
0,
[record.AllClients],
[{
'core_requests': (0, 0),
'core_replies': (0, 0),
'ext_requests': (0, 0, 0, 0),
'ext_replies': (0, 0, 0, 0),
'delivered_events': (0, 0),
'device_events': (X.KeyReleaseMask, X.ButtonReleaseMask),
'errors': (0, 0),
'client_started': False,
'client_died': False,
}])
disp.record_enable_context(ctx, handler)
disp.record_free_context(ctx)

while 1:
# Infinite wait, doesn't do anything as no events are grabbed
event = root.display.next_event()

You will have to extend the handler to fit your needs for instead of just printing to screen, and then make it into a separate thread.

The (painful) alternative is to listen to the keyboard directly, without relying on external libraries or the X session. In linux everything is a file and your keyboard input will be in /dev/input that you could read as a file, for example open('/dev/input/even2', 'rb'), in the background. This is not suggested as it requires escalated permissions, figuring out which device is the keyboard, and then create your own keymapping. Just wanted to let you know what's possible if necessary.

Edit: Also found Global keybinding on X using Python gtk3 which seems to have more example goodness.

How to listen for regular keys and key combinations in Python

sort of rewrite the pynput sample code so that the program can monitor combination of shift key.

I made a global variable SHIFT_STATE to record if the shift key is pressed, and I believe you can expand this to monitor ctrl, alt, cmd keys and make the code looks prettier.

By the way, the library has the power to monitor global-hotkeys
however I did not look into it too much. You can check it out here: https://pynput.readthedocs.io/en/latest/keyboard.html#global-hotkeys

from pynput import keyboard

SHIFT_STATE = False
def on_press(key):
global SHIFT_STATE
if key == keyboard.Key.shift:
SHIFT_STATE = True
else:
try:
if SHIFT_STATE:
print(f'shift + {key}')
else:
print(key)
except Exception as e:
print(e)

def on_release(key):
global SHIFT_STATE
if key == keyboard.Key.esc:
# Stop listener
return False
elif key == keyboard.Key.shift:
SHIFT_STATE = False

# Collect events until released
with keyboard.Listener(
on_press=on_press,
on_release=on_release) as listener:
listener.join()

and here's the screenshot I ran the code FYI
pynput rewrite

Global Keybindings?

This is what I found, which works well.

using the module pynput. It will listen to key press globally.
I found the answer here stackoverflow.com/questions/11918999/key-listeners-in-python

from pynput import keyboard

def on_press(key):
try:
print('alphanumeric key {0} pressed'.format(key.char))
except AttributeError:
print('special key {0} pressed'.format(key))

listener = keyboard.Listener(on_press=on_press)
listener.start()

Is there a way for my binary to react to some global hotkeys in Linux?

How global do your hotkeys need to be? Is it enough for them to be global for a X session? In that case you should be able to open an Xlib connection and listen for the events you need.

Ordinarily keyboard events in X are delivered to the window that currently has the focus, and propagated up the hierarchy until they are handled. Clearly this is not what we want. We need to process the event before any other window can get to it. We need to call XGrabKey on the root window with the keycode and modifiers of our hotkey to accomplish this.

I found a good example here.

Key Listeners in python?

It's unfortunately not so easy to do that. If you're trying to make some sort of text user interface, you may want to look into curses. If you want to display things like you normally would in a terminal, but want input like that, then you'll have to work with termios, which unfortunately appears to be poorly documented in Python. Neither of these options are that simple, though, unfortunately. Additionally, they do not work under Windows; if you need them to work under Windows, you'll have to use PDCurses as a replacement for curses or pywin32 rather than termios.


I was able to get this working decently. It prints out the hexadecimal representation of keys you type. As I said in the comments of your question, arrows are tricky; I think you'll agree.

#!/usr/bin/env python
import sys
import termios
import contextlib

@contextlib.contextmanager
def raw_mode(file):
old_attrs = termios.tcgetattr(file.fileno())
new_attrs = old_attrs[:]
new_attrs[3] = new_attrs[3] & ~(termios.ECHO | termios.ICANON)
try:
termios.tcsetattr(file.fileno(), termios.TCSADRAIN, new_attrs)
yield
finally:
termios.tcsetattr(file.fileno(), termios.TCSADRAIN, old_attrs)

def main():
print 'exit with ^C or ^D'
with raw_mode(sys.stdin):
try:
while True:
ch = sys.stdin.read(1)
if not ch or ch == chr(4):
break
print '%02x' % ord(ch),
except (KeyboardInterrupt, EOFError):
pass

if __name__ == '__main__':
main()

python qt wait in background for hotkey

Set global hotkey with Python 2.6

http://bytes.com/topic/python/answers/574341-how-create-global-hotkey

Python Global Hotkey

Looks like pyhook or wxPython for windows, and XLib for linux, or maybe keybinder for Gnome.

And if you were only listening for it while your program was active, Qt has something for it:

Respond to application-wide "hotkey" in Qt

Hope that helps.

Python cross-platform listening for keypresses?

I don't know of any cross-platform lightweight module that listens for keypresses. But here's a suggestion in case you want to implement something simple:

Check out this question on getting a single keypress at a time in the Python FAQ. You could experiment a bit with blocking reads from sys.stdin and threading. But this may only work on Unix. On Windows, you can use msvcrt.kbhit.

Combining the keypress recipe from the Python FAQ and the msvcrt module, the resulting kbhit function would go like this:

try:
from msvcrt import kbhit
except ImportError:
import termios, fcntl, sys, os
def kbhit():
fd = sys.stdin.fileno()
oldterm = termios.tcgetattr(fd)
newattr = termios.tcgetattr(fd)
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)
oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
try:
while True:
try:
c = sys.stdin.read(1)
return True
except IOError:
return False
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)


Related Topics



Leave a reply



Submit