Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if key is pressed using python (a daemon in the background)

I've created a python script in which an event needs to be executed each time I press the Super (or WinKey) on my keyboard.

How can one achieve this without the python process being "focused" - as it is running in the background waiting for the key to be pressed to execute the event?

I've seen a lot of posts around the web showing me how to read input - but they have all required one to have the process "focused" and none have showed me how to capture the Super (or WinKey) using a python script.

I'm running Ubuntu 9.10.

like image 740
torger Avatar asked Jan 22 '23 21:01

torger


2 Answers

This allows me to get the state of modifier keys on my *nix system.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""OSD Neo2
   ========
   On screen display for learning the keyboard layout Neo2
   Copyright (c) 2009 Martin Zuther (http://www.mzuther.de/)
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
   Thank you for using free software!
"""
# Here follows a plea in German to keep the comments in English so
# that you may understand them, dear visitor ...
#
# Meine Kommentare in den Quellcodes sind absichtlich auf Englisch
# gehalten, damit Leute, die im Internet nach Lösungen suchen, den
# Code nachvollziehen können.  Daher bitte ich darum, zusätzliche
# Kommentare ebenfalls auf Englisch zu schreiben.  Vielen Dank!
import ctypes
import ctypes.util
import gettext
import os
import types
# initialise localisation settings
module_path = os.path.dirname(os.path.realpath(__file__))
gettext.bindtextdomain('OSDneo2', os.path.join(module_path, 'po/'))
gettext.textdomain('OSDneo2')
_ = gettext.lgettext
class SimpleXkbWrapper:
    """
    Far from complete wrapper for the "X Keyboard Extension" (well, to
    be honest, it just wraps what I need using Python's "ctypes"
    library <g>).
    """
    # set this to true to get lots of debugging information (and
    # considerably slow things down)
    DEBUG_XKB = False
    # "C defines" from file /usr/include/X11/extensions/XKB.h (Ubuntu 9.04):
    # $XFree86: xc/include/extensions/XKB.h,v 1.5tsi Exp $
    #
    # XkbUseCoreKbd is used to specify the core keyboard without having to
    # look up its X input extension identifier.
    XkbUseCoreKbd            = 0x0100
    # "C defines" from file /usr/include/X11/XKBlib.h (Ubuntu 9.04):
    # $XFree86: xc/lib/X11/XKBlib.h,v 3.5 2003/04/17 02:06:31 dawes Exp $ #
    #
    # XkbOpenDisplay error codes
    XkbOD_Success            = 0
    XkbOD_BadLibraryVersion  = 1
    XkbOD_ConnectionRefused  = 2
    XkbOD_NonXkbServer       = 3
    XkbOD_BadServerVersion   = 4
    # "C typedef" from file /usr/include/X11/extensions/XKBstr.h (Ubuntu 9.04):
    # $Xorg: XKBstr.h,v 1.3 2000/08/18 04:05:45 coskrey Exp $
    #
    # Common data structures and access macros
    #
    # typedef struct _XkbStateRec {
    #         unsigned char   group;
    #         unsigned char   locked_group;
    #         unsigned short  base_group;
    #         unsigned short  latched_group;
    #         unsigned char   mods;
    #         unsigned char   base_mods;
    #         unsigned char   latched_mods;
    #         unsigned char   locked_mods;
    #         unsigned char   compat_state;
    #         unsigned char   grab_mods;
    #         unsigned char   compat_grab_mods;
    #         unsigned char   lookup_mods;
    #         unsigned char   compat_lookup_mods;
    #         unsigned short  ptr_buttons;
    # } XkbStateRec,*XkbStatePtr;
    class XkbStateRec(ctypes.Structure):
        _fields_ = [
                        ('group',              ctypes.c_ubyte), \
                        ('locked_group',       ctypes.c_ubyte), \
                        ('base_group',         ctypes.c_ushort), \
                        ('latched_group',      ctypes.c_ushort), \
                        ('mods',               ctypes.c_ubyte), \
                        ('base_mods',          ctypes.c_ubyte), \
                        ('latched_mods',       ctypes.c_ubyte), \
                        ('locked_mods',        ctypes.c_ubyte), \
                        ('compat_state',       ctypes.c_ubyte), \
                        ('grab_mods',          ctypes.c_ubyte), \
                        ('compat_grab_mods',   ctypes.c_ubyte), \
                        ('lookup_mods',        ctypes.c_ubyte), \
                        ('compat_lookup_mods', ctypes.c_ubyte), \
                        ('ptr_buttons',        ctypes.c_ushort) \
                   ]
    # "C defines" from file /usr/include/X11/X.h (Ubuntu 9.04):
    # $XFree86: xc/include/X.h,v 1.6 2003/07/09 15:27:28 tsi Exp $
    #
    # Key masks. Used as modifiers to GrabButton and GrabKey, results of
    # QueryPointer, state in various key-, mouse-, and button-related
    # events.
    ShiftMask                = 1
    LockMask                 = 2
    ControlMask              = 4
    Mod1Mask                 = 8
    Mod2Mask                 = 16
    Mod3Mask                 = 32
    Mod4Mask                 = 64
    Mod5Mask                 = 128
    def __init__(self):
        # dynamically link to "X Keyboard Extension" library
        library_xf86misc = ctypes.CDLL(ctypes.util.find_library('Xxf86misc'))
        # print debugging information if requested
        if self.DEBUG_XKB:
            print
            print '  %s' % library_xf86misc
        # define "ctypes" prototype for the function
        #
        # Display *XkbOpenDisplay(display_name, event_rtrn, error_rtrn,
        #                             major_in_out, minor_in_out, reason_rtrn)
        #
        #    char * display_name;
        #    int * event_rtrn;
        #    int * error_rtrn;
        #    int * major_in_out;
        #    int * minor_in_out;
        #    int * reason_rtrn;
        paramflags_xkbopendisplay = \
            (1, 'display_name'), \
            (2, 'event_rtrn'), \
            (2, 'error_rtrn'), \
            (3, 'major_in_out'), \
            (3, 'minor_in_out'), \
            (2, 'reason_rtrn')
        prototype_xkbopendisplay = ctypes.CFUNCTYPE( \
            ctypes.c_uint, \
                ctypes.c_char_p, \
                ctypes.POINTER(ctypes.c_int), \
                ctypes.POINTER(ctypes.c_int), \
                ctypes.POINTER(ctypes.c_int), \
                ctypes.POINTER(ctypes.c_int), \
                ctypes.POINTER(ctypes.c_int) \
                )
        # set-up function (low-level)
        self.__XkbOpenDisplay__ = prototype_xkbopendisplay( \
            ('XkbOpenDisplay', library_xf86misc), \
                paramflags_xkbopendisplay \
                )
        # define error handler
        def errcheck_xkbopendisplay(result, func, args):
            # print debugging information if requested
            if self.DEBUG_XKB:
                print
                print '  [XkbOpenDisplay]'
                print '  Display:       %#010x' % result
                print '  display_name:  %s' % args[0].value
                print '  event_rtrn:    %d' % args[1].value
                print '  error_rtrn:    %d' % args[2].value
                print '  major_in_out:  %d' % args[3].value
                print '  minor_in_out:  %d' % args[4].value
                print '  reason_rt:     %d' % args[5].value
            # function didn't return display handle, so let's see why
            # not
            if result == 0:
                # values were taken from file /usr/include/X11/XKBlib.h (Ubuntu 9.04):
                # $XFree86: xc/lib/X11/XKBlib.h,v 3.5 2003/04/17 02:06:31 dawes Exp $ #
                error_id = args[5].value
                if error_id == self.XkbOD_Success:
                    error_name = 'XkbOD_Success'
                elif error_id == self.XkbOD_BadLibraryVersion:
                    error_name = 'XkbOD_BadLibraryVersion'
                elif error_id == self.XkbOD_ConnectionRefused:
                    error_name = 'XkbOD_ConnectionRefused'
                elif error_id == self.XkbOD_NonXkbServer:
                    error_name = 'XkbOD_NonXkbServer'
                elif error_id == self.XkbOD_BadServerVersion:
                    error_name = 'XkbOD_BadServerVersion'
                else:
                    error_name = _('undefined')
                error_message = \
                    _('"XkbOpenDisplay" reported an error (%(error_name)s).') % \
                    {'error_name': error_name}
                raise OSError(error_message)
            # return display handle and all function arguments
            return (ctypes.c_uint(result), args)
        # connect error handler to function
        self.__XkbOpenDisplay__.errcheck = errcheck_xkbopendisplay
        # define "ctypes" prototype for the function
        #
        # Bool XkbGetState(display, device_spec, state_return)
        #
        #     Display *             display;
        #     unsigned int          device_spec;
        #     XkbStatePtr           state_return;
        paramflags_xkbgetstate = \
            (1, 'display'), \
            (1, 'device_spec'), \
            (3, 'state_return')
        prototype_xkbgetstate = ctypes.CFUNCTYPE( \
            ctypes.c_int, # Python 2.5 doesn't yet know c_bool \
                ctypes.c_uint, \
                ctypes.c_uint, \
                ctypes.POINTER(self.XkbStateRec) \
                )
        # set-up function (low-level)
        self.__XkbGetState__ = prototype_xkbgetstate( \
            ('XkbGetState', library_xf86misc), \
                paramflags_xkbgetstate \
                )
        # define error handler
        def errcheck_xkbgetstate(result, func, args):
            # print debugging information if requested
            if self.DEBUG_XKB:
                print
                print '  [XkbGetState]'
                print '  Status:        %s' % result
                print '  display:       %#010x' % args[0].value
                print '  device_spec:   %d\n' % args[1].value
                print '  state_return.group:               %d' % \
                    args[2].group
                print '  state_return.locked_group:        %d' % \
                    args[2].locked_group
                print '  state_return.base_group:          %d' % \
                    args[2].base_group
                print '  state_return.latched_group:       %d' % \
                    args[2].latched_group
                print '  state_return.mods:                %d' % \
                    args[2].mods
                print '  state_return.base_mods:           %d' % \
                    args[2].base_mods
                print '  state_return.latched_mods:        %d' % \
                    args[2].latched_mods
                print '  state_return.locked_mods:         %d' % \
                    args[2].locked_mods
                print '  state_return.compat_state:        %d' % \
                    args[2].compat_state
                print '  state_return.grab_mods:           %d' % \
                    args[2].grab_mods
                print '  state_return.compat_grab_mods:    %d' % \
                    args[2].compat_grab_mods
                print '  state_return.lookup_mods:         %d' % \
                    args[2].lookup_mods
                print '  state_return.compat_lookup_mods:  %d' % \
                    args[2].compat_lookup_mods
                print '  state_return.ptr_buttons:         %d\n' % \
                    args[2].ptr_buttons
                print '  Mask          mods   base_mods   latched_mods   locked_mods   compat_state'
                print '  --------------------------------------------------------------------------'
                print '  ShiftMask     %-5s  %-5s       %-5s          %-5s         %-5s' % \
                    ((args[2].mods         & self.ShiftMask) != 0, \
                     (args[2].base_mods    & self.ShiftMask) != 0, \
                     (args[2].latched_mods & self.ShiftMask) != 0, \
                     (args[2].locked_mods  & self.ShiftMask) != 0, \
                     (args[2].compat_state & self.ShiftMask) != 0)
                print '  LockMask      %-5s  %-5s       %-5s          %-5s         %-5s' % \
                    ((args[2].mods         & self.LockMask) != 0, \
                     (args[2].base_mods    & self.LockMask) != 0, \
                     (args[2].latched_mods & self.LockMask) != 0, \
                     (args[2].locked_mods  & self.LockMask) != 0, \
                     (args[2].compat_state & self.LockMask) != 0)
                print '  ControlMask   %-5s  %-5s       %-5s          %-5s         %-5s' % \
                    ((args[2].mods         & self.ControlMask) != 0, \
                     (args[2].base_mods    & self.ControlMask) != 0, \
                     (args[2].latched_mods & self.ControlMask) != 0, \
                     (args[2].locked_mods  & self.ControlMask) != 0, \
                     (args[2].compat_state & self.ControlMask) != 0)
                print '  Mod1Mask      %-5s  %-5s       %-5s          %-5s         %-5s' % \
                    ((args[2].mods         & self.Mod1Mask) != 0, \
                     (args[2].base_mods    & self.Mod1Mask) != 0, \
                     (args[2].latched_mods & self.Mod1Mask) != 0, \
                     (args[2].locked_mods  & self.Mod1Mask) != 0, \
                     (args[2].compat_state & self.Mod1Mask) != 0)
                print '  Mod2Mask      %-5s  %-5s       %-5s          %-5s         %-5s' % \
                    ((args[2].mods         & self.Mod2Mask) != 0, \
                     (args[2].base_mods    & self.Mod2Mask) != 0, \
                     (args[2].latched_mods & self.Mod2Mask) != 0, \
                     (args[2].locked_mods  & self.Mod2Mask) != 0, \
                     (args[2].compat_state & self.Mod2Mask) != 0)
                print '  Mod3Mask      %-5s  %-5s       %-5s          %-5s         %-5s' % \
                    ((args[2].mods         & self.Mod3Mask) != 0, \
                     (args[2].base_mods    & self.Mod3Mask) != 0, \
                     (args[2].latched_mods & self.Mod3Mask) != 0, \
                     (args[2].locked_mods  & self.Mod3Mask) != 0, \
                     (args[2].compat_state & self.Mod3Mask) != 0)
                print '  Mod4Mask      %-5s  %-5s       %-5s          %-5s         %-5s' % \
                    ((args[2].mods         & self.Mod4Mask) != 0, \
                     (args[2].base_mods    & self.Mod4Mask) != 0, \
                     (args[2].latched_mods & self.Mod4Mask) != 0, \
                     (args[2].locked_mods  & self.Mod4Mask) != 0, \
                     (args[2].compat_state & self.Mod4Mask) != 0)
                print '  Mod5Mask      %-5s  %-5s       %-5s          %-5s         %-5s' % \
                    ((args[2].mods         & self.Mod5Mask) != 0, \
                     (args[2].base_mods    & self.Mod5Mask) != 0, \
                     (args[2].latched_mods & self.Mod5Mask) != 0, \
                     (args[2].locked_mods  & self.Mod5Mask) != 0, \
                     (args[2].compat_state & self.Mod5Mask) != 0)
            # return function return value and all function arguments
            return (result, args)
        # connect error handler to function
        self.__XkbGetState__.errcheck = errcheck_xkbgetstate
    # define high-level version of "XkbOpenDisplay"
    def XkbOpenDisplay(self, display_name, major_in_out, minor_in_out):
        # if we don't do type checking, nobody ever will
        assert (type(display_name) == types.NoneType) or \
            (type(display_name) == types.StringType)
        assert type(major_in_out) == types.IntType
        assert type(minor_in_out) == types.IntType
        # convert function arguments to "ctypes", ...
        __display_name__ = ctypes.c_char_p(display_name)
        __major_in_out__ = ctypes.c_int(major_in_out)
        __minor_in_out__ = ctypes.c_int(minor_in_out)
        # ... call low-level function ...
        ret = self.__XkbOpenDisplay__(__display_name__, __major_in_out__, \
                                         __minor_in_out__)
        # ... and return converted return value and function arguments
        return {'display_handle': ret[0].value, \
                    'server_major_version': ret[1][3].value, \
                    'server_minor_version': ret[1][4].value}
    # define high-level version of "XkbGetState"
    def XkbGetState(self, display_handle, device_spec):
        # if we don't do type checking, nobody ever will
        assert type(display_handle) == types.LongType
        assert type(device_spec) == types.IntType
        # convert function arguments to "ctypes", ...
        __display_handle__ = ctypes.c_uint(display_handle)
        __device_spec__ = ctypes.c_uint(device_spec)
        __xkbstaterec__ = self.XkbStateRec()
        # ... call low-level function ...
        ret = self.__XkbGetState__(__display_handle__, __device_spec__, \
                                  __xkbstaterec__)
        # ... and return converted function argument
        xkbstaterec = ret[1][2]
        return xkbstaterec
    # extract modifier status using bitmasks
    def ExtractLocks(self, xkbstaterec):
        return {'group': xkbstaterec.group, \
                'shift': \
                    (xkbstaterec.base_mods & self.ShiftMask) != 0, \
                'shift_lock': \
                    (xkbstaterec.locked_mods & self.ShiftMask) != 0, \
                'lock': \
                    (xkbstaterec.base_mods & self.LockMask) != 0, \
                'lock_lock': \
                    (xkbstaterec.locked_mods & self.LockMask) != 0, \
                'control': \
                    (xkbstaterec.base_mods & self.ControlMask) != 0, \
                'control_lock': \
                    (xkbstaterec.locked_mods & self.ControlMask) != 0, \
                'mod1': \
                    (xkbstaterec.base_mods & self.Mod1Mask) != 0, \
                'mod1_lock': \
                    (xkbstaterec.locked_mods & self.Mod1Mask) != 0, \
                'mod2': \
                    (xkbstaterec.base_mods & self.Mod2Mask) != 0, \
                'mod2_lock': \
                    (xkbstaterec.locked_mods & self.Mod2Mask) != 0, \
                'mod3': \
                    (xkbstaterec.base_mods & self.Mod3Mask) != 0, \
                'mod3_lock': \
                    (xkbstaterec.locked_mods & self.Mod3Mask) != 0, \
                'mod4': \
                    (xkbstaterec.base_mods & self.Mod4Mask) != 0, \
                'mod4_lock': \
                    (xkbstaterec.locked_mods & self.Mod4Mask) != 0, \
                'mod5': \
                    (xkbstaterec.base_mods & self.Mod5Mask) != 0, \
                'mod5_lock': \
                    (xkbstaterec.locked_mods & self.Mod5Mask) != 0}

To call state to a dictionary,from another file, just call from FILENAME import * and do the following:

# simple demonstration of this wrapper
xkb = SimpleXkbWrapper()
# initialise wrapper for the X Keyboard Extension (v1.0) and
# open connection to default X display
display_name = None
major_in_out = 1
minor_in_out = 0
try:
    ret = xkb.XkbOpenDisplay(display_name, major_in_out, minor_in_out)
except OSError, error:
    print
    print '  Error: %s' % error
    print
    exit(1)
# ... get modifier state of core keyboard ...
display_handle = ret['display_handle']
device_spec = xkb.XkbUseCoreKbd
xkbstaterec = xkb.XkbGetState(display_handle, device_spec)
# ... and extract and the information we need
print xkb.ExtractLocks(xkbstaterec)

Cheers to the creator whose code can be found here, and to Jason Orendorffl for drawing my attention to it.

like image 120
torger Avatar answered Feb 01 '23 10:02

torger


My previous answer apparently was completely wrong, sorry. I think the correct approach would be to read from /dev/input/event1 (?)

This short test showed scancodes for me, even if the terminal did not have focus:

from struct import unpack
port = open("/dev/input/event1","rb")    

while 1:    
  a,b,c,d = unpack("4B",port.read(4))    
  print a,b,c,d

I do not know if /dev/input/event1 is always the keyboard or how to determine which one is, but at least for me it worked

like image 41
Kimvais Avatar answered Feb 01 '23 11:02

Kimvais