Search code examples
pythondaemonkeypressubuntu-9.10binding

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.


Solution

  • 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.