diff options
Diffstat (limited to 'linux/osd/SimpleXkbWrapper.py')
-rwxr-xr-x | linux/osd/SimpleXkbWrapper.py | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/linux/osd/SimpleXkbWrapper.py b/linux/osd/SimpleXkbWrapper.py new file mode 100755 index 0000000..9ce9572 --- /dev/null +++ b/linux/osd/SimpleXkbWrapper.py @@ -0,0 +1,472 @@ +#!/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} + + +if __name__ == '__main__': + # 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 + mod_states = xkb.ExtractLocks(xkbstaterec) + print + for mod in mod_states: + print ' %-13s %s' % (mod + ':', mod_states[mod]) + print |