Multimedia Keys in Python (Linux)
Asked Answered
S

4

9

I want to detect when the XF86Launch1 key is pressed on my keyboard, using Python.

I have a headless server with a Bluetooth connected keyboard. I'd like to launch a command-line program whenever a specific multimedia key is pressed.

At the moment, I'm using:

import sys
import tty, termios

def getch():
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
        tty.setraw(fd)
        ch = sys.stdin.read(1)
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    return ch

print getch()

But it won't detect multimedia keys. Nothing prints out when I press them.

Is there a way to detect these keys on a headless Ubuntu box - or a better way to launch a program on keypress?

Skyrocket answered 20/2, 2016 at 16:54 Comment(0)
P
2

Rather than trying to read stdin of the tty, you can use linux's input device api to read them.

On linux, all input devices show up as a file under /dev/input (such as /dev/input/event0) and when one read()s from these files, structured data indicating when keys are pressed and released is returned.

In C, the libevdev library provides a wrapper around those. In python, you could use the python-evdev library. It should also be possible to read directly from the input device (though you may need to carefully read the kernel documentation & source to handle that properly).

Presentiment answered 21/2, 2016 at 7:15 Comment(0)
B
1

I think that your problem is that multimedia keys do not map to terminal input.

It's possible that you could make progress by running xev to trap the key and xmodmap to map the key to a different input.

Alternatively, use something like TKinter and see if a graphical program doesn't collect the keypresses.

from Tkinter import *

root = Tk()

def key(event):
    print "pressed", repr(event.char)

def callback(event):
    frame.focus_set()

frame = Frame(root, width=100, height=100)
frame.bind("<Key>", key)
frame.bind("<Button-1>", callback)
frame.pack()

root.mainloop()

Another possibility is to map to an F key instead of a multimedia key. (i.e. F9)


Edit: Further research into this resulted in these two links:

Extra Keyboard Keys

Extra Keyboard Keys in Console

The console itself does not support multimedia keys. But it does support custom F keys. F30-F246 are always free. Rather than map to XF86Launch1, map to F70. Then map F70 to keyboard input in your keymap, or use the Python script you already wrote to handle it.

Brandabrandais answered 21/2, 2016 at 0:28 Comment(0)
W
1

pycopia may be an option. I am using it with this bluetooth button and it seems to work fairly well. I am still working on getting it to reconnect to the button when the button goes to sleep and then comes back. Here's part of the script that I'm using:
keyboard.py:

from pycopia.OS.Linux import Input
from pycopia.OS.Linux import event

class Satechi(Input.EventDevice):
    DEVNAME = 'Satechi'

    def register_callback(self, cb):
        self._callback = cb

    def poll(self):
        while 1:
            ev = self.read()
            if ev.evtype == event.EV_KEY:
                self._callback(ev)

    read_handler = poll

button.py

from keyboard import Satechi

def callback(event):
    pass #Do something fun

if __name__ == '__main__':
    pm = Satechi()
    pm.find()
    pm.register_callback(callback)

    while 1:
        try:
            pm.poll()
        except OSError:
            pm = Satechi()
            while True:
                try:
                    pm.find()
                    pm.register_callback(callback)
                    break
                except IOError:
                    pass
    pm.close()

Where DEVNAME is the devices name in /proc/bus/input/devices.
You can print event in the callback to figure out what the code and value is for the button you are looking for

Womera answered 14/7, 2016 at 19:42 Comment(2)
hey @Womera did you ever finish the reconnect part once the button goes to sleep?Yellows
Original question asker wrote about the button sleeping here - shkspr.mobi/blog/2016/02/cheap-bluetooth-buttons-and-linuxCoolish
G
-2

Try to read xinput test <id> stdout in a loop and catch the events you need.

Here is some example in Bash:

#!/bin/bash

keyboard_id=9 # use xinput to find your keyboard id

xinput test $keyboard_id | while read line ; do
    case $line in
        "key press   44") echo -e "\n == j pressed ==" ;;
        "key press   45") echo -e "\n == k pressed ==" ;;
    esac
done
Grimbald answered 20/2, 2016 at 22:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.