summaryrefslogtreecommitdiffstats
path: root/linux/osd/SimpleXkbWrapper.py
diff options
context:
space:
mode:
Diffstat (limited to 'linux/osd/SimpleXkbWrapper.py')
-rwxr-xr-xlinux/osd/SimpleXkbWrapper.py472
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