From 3b098b897c0b2f1eb3c9c1b61391bbf02ed36828 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 3 Jul 2014 13:39:45 +0700 Subject: [PATCH 01/29] correct spacing on special keys Always add space between special keys (like modifiers or Esc) and alphanumerical oneletter symbols. --- Screenkey/listenkbd.py | 57 ++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 6ca9ae9..c2619a2 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -26,33 +26,33 @@ MODE_NORMAL = 1 REPLACE_KEYS = { - 'XK_Escape':_('Esc '), - 'XK_Tab':u'\u21B9 ', - 'XK_Return':u'\u23CE ', + 'XK_Escape':_('Esc'), + 'XK_Tab':u'\u21B9', + 'XK_Return':u'\u23CE', 'XK_Space':u' ', - 'XK_Caps_Lock':_('Caps '), - 'XK_F1':u'F1 ', - 'XK_F2':u'F2 ', - 'XK_F3':u'F3 ', - 'XK_F4':u'F4 ', - 'XK_F5':u'F5 ', - 'XK_F6':u'F6 ', - 'XK_F7':u'F7 ', - 'XK_F8':u'F8 ', - 'XK_F9':u'F9 ', - 'XK_F10':u'F10 ', - 'XK_F11':u'F11 ', - 'XK_F12':u'F12 ', - 'XK_Home':_('Home '), + 'XK_Caps_Lock':_('Caps'), + 'XK_F1':u'F1', + 'XK_F2':u'F2', + 'XK_F3':u'F3', + 'XK_F4':u'F4', + 'XK_F5':u'F5', + 'XK_F6':u'F6', + 'XK_F7':u'F7', + 'XK_F8':u'F8', + 'XK_F9':u'F9', + 'XK_F10':u'F10', + 'XK_F11':u'F11', + 'XK_F12':u'F12', + 'XK_Home':_('Home'), 'XK_Up':u'\u2191', - 'XK_Page_Up':_('PgUp '), + 'XK_Page_Up':_('PgUp'), 'XK_Left':u'\u2190', 'XK_Right':u'\u2192', - 'XK_End':_('End '), + 'XK_End':_('End'), 'XK_Down':u'\u2193', - 'XK_Next':_('PgDn '), - 'XK_Insert':_('Ins '), - 'XK_Delete':_('Del '), + 'XK_Next':_('PgDn'), + 'XK_Insert':_('Ins'), + 'XK_Delete':_('Del'), 'XK_KP_Home':u'(7)', 'XK_KP_Up':u'(8)', 'XK_KP_Prior':u'(9)', @@ -68,8 +68,8 @@ 'XK_KP_Subtract':u'(-)', 'XK_KP_Multiply':u'(*)', 'XK_KP_Divide':u'(/)', - 'XK_Num_Lock':u'NumLock ', - 'XK_KP_Enter':u'\u23CE ', + 'XK_Num_Lock':u'NumLock', + 'XK_KP_Enter':u'\u23CE', } class ListenKbd(threading.Thread): @@ -84,6 +84,7 @@ def __init__(self, label, logger, mode): self.text = "" self.command = None self.shift = None + self.prev_key = '' self.cmd_keys = { 'shift': False, 'ctrl': False, @@ -293,9 +294,11 @@ def key_normal_mode(self, event): key = string if mod != '': - key = "%s%s " % (mod, key) - else: - key = "%s%s" % (mod, key) + key = mod + key + prev_key = self.prev_key + self.prev_key = key + if len(prev_key) > 1 or len(key) > 1: + key = " " + key else: return From bd9d19bc16c9b764b3f92378415239f55ef63f4d Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 3 Jul 2014 13:42:40 +0700 Subject: [PATCH 02/29] correct way to handle modifiers based on event state --- Screenkey/listenkbd.py | 71 +++++++++--------------------------------- Screenkey/modmap.py | 7 ++--- 2 files changed, 18 insertions(+), 60 deletions(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index c2619a2..215ad93 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -210,64 +210,23 @@ def key_normal_mode(self, event): self.logger.debug('No mapping for scan_code %d' % event.detail) return - - # Alt key - if event.detail in self.modifiers['mod1']: - if event.type == X.KeyPress: - self.cmd_keys['alt'] = True - else: - self.cmd_keys['alt'] = False - return - # Meta key - # Fixme: it must use self.modifiers['mod5'] - # but doesn't work - if event.detail == 108: - if event.type == X.KeyPress: - self.cmd_keys['meta'] = True - else: - self.cmd_keys['meta'] = False - return - # Super key - if event.detail in self.modifiers['mod4']: - if event.type == X.KeyPress: - self.cmd_keys['super'] = True - else: - self.cmd_keys['super'] = False - return - # Ctrl keys - elif event.detail in self.modifiers['control']: - if event.type == X.KeyPress: - self.cmd_keys['ctrl'] = True + masks = { + 1<<0: 'shift', + 1<<1: 'lock', + 1<<2: 'ctrl', + 1<<3: 'alt', + 1<<4: 'mod2', + 1<<5: 'mod3', + 1<<6: 'super', + 1<<7: 'meta' + } + for k in masks: + if k & event.state: + self.cmd_keys[masks[k]] = True else: - self.cmd_keys['ctrl'] = False - return - # Shift keys - elif event.detail in self.modifiers['shift']: - if event.type == X.KeyPress: - self.cmd_keys['shift'] = True - else: - self.cmd_keys['shift'] = False - return - # Capslock key - elif event.detail in self.modifiers['lock']: - if event.type == X.KeyPress: - if self.cmd_keys['capslock']: - self.cmd_keys['capslock'] = False - else: - self.cmd_keys['capslock'] = True + self.cmd_keys[masks[k]] = False + if event.detail in self.modifiers: return - # Backspace key - elif event.detail == 22 and event.type == X.KeyPress: - gtk.gdk.threads_enter() - if len(self.label.get_text()) > 0: - self.label.set_text( - unicode(self.label.get_text(), 'utf-8')[:-1] - ) - key = "" - gtk.gdk.threads_leave() - else: - gtk.gdk.threads_leave() - return else: if event.type == X.KeyPress: key = key_normal diff --git a/Screenkey/modmap.py b/Screenkey/modmap.py index 4e1cad5..cd4d863 100644 --- a/Screenkey/modmap.py +++ b/Screenkey/modmap.py @@ -84,7 +84,7 @@ def get_keymap_table(): return keymap def get_modifier_map(): - modifiers = {} + modifiers = [] modifier_map = cmd_modifier_map() @@ -97,9 +97,8 @@ def get_modifier_map(): keycodes = re_line.findall(line) # Convert key codes from hex to dec for use them # with the keymap table - keycodes =[ int(kc, 16) for kc in keycodes] - - modifiers[mod_name] = keycodes + for kc in keycodes: + modifiers.append(int(kc, 16)) return modifiers From 9e7b18102d701a2844983126f1802c5da6d00be8 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 3 Jul 2014 13:42:57 +0700 Subject: [PATCH 03/29] <= for backspace key --- Screenkey/listenkbd.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 215ad93..99d403d 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -247,6 +247,8 @@ def key_normal_mode(self, event): key = key_dead if self.cmd_keys['shift'] and self.cmd_keys['meta']: key = key_deadshift + if event.detail == 22: + key = u'\u21D0' string = self.replace_key(key, keysym) if string: From 5d6b50d3f40b7d0f70655ad2f4ba8929b19abb6d Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 3 Jul 2014 13:46:33 +0700 Subject: [PATCH 04/29] keep pos option, bg and fg cmd flags, fix quit * KEEP_POS is not by default, means user can drag window wherever one wants; * background and foreground colors can be changed via command line flags; * process will be terminated no matter how it's closed (bug in Xlib impl); --- Screenkey/screenkey.py | 61 +++++++++++++++++++++++++++++++----------- screenkey | 19 ++++++++++--- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/Screenkey/screenkey.py b/Screenkey/screenkey.py index a292ed9..ba218c6 100644 --- a/Screenkey/screenkey.py +++ b/Screenkey/screenkey.py @@ -28,6 +28,7 @@ POS_TOP = 0 POS_CENTER = 1 POS_BOTTOM = 2 +POS_KEEP = 3 SIZE_LARGE = 0 SIZE_MEDIUM = 1 @@ -42,6 +43,7 @@ class Screenkey(gtk.Window): POS_TOP:_('Top'), POS_CENTER:_('Center'), POS_BOTTOM:_('Bottom'), + POS_KEEP:_('Keep'), } SIZES = { SIZE_LARGE:_('Large'), @@ -56,9 +58,8 @@ class Screenkey(gtk.Window): STATE_FILE = os.path.join(glib.get_user_cache_dir(), 'screenkey.dat') - def __init__(self, logger, nodetach): + def __init__(self, logger, nodetach, nohide, bg, fg): gtk.Window.__init__(self) - self.timer = None self.logger = logger @@ -66,7 +67,7 @@ def __init__(self, logger, nodetach): if not self.options: self.options = { 'timeout': 2.5, - 'position': POS_BOTTOM, + 'position': POS_KEEP, 'size': SIZE_MEDIUM, 'mode': MODE_NORMAL, } @@ -83,10 +84,15 @@ def __init__(self, logger, nodetach): self.set_property('accept-focus', False) self.set_property('focus-on-map', False) self.set_position(gtk.WIN_POS_CENTER) - bgcolor = gtk.gdk.color_parse("black") + bgcolor = gtk.gdk.color_parse(bg) self.modify_bg(gtk.STATE_NORMAL, bgcolor) self.set_opacity(0.7) + self.fg = fg + + self.pos_x = 0 + self.pos_y = 0 + gobject.signal_new("text-changed", gtk.Label, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()) self.label = gtk.Label() @@ -157,11 +163,19 @@ def __init__(self, logger, nodetach): self.logger.debug("Using StatusIcon.") + self.connect("destroy-event", self.quit) self.connect("delete-event", self.quit) + self.connect("configure-event", self.on_configure) + + self.no_hide = nohide + if nohide: + self.show() def quit(self, widget, data=None): - self.listenkbd.stop() - gtk.main_quit() + try: + self.listenkbd.stop() + finally: + gtk.main_quit() def load_state(self): """Load stored options""" @@ -202,14 +216,6 @@ def set_window_size(self, setting): if setting == SIZE_SMALL: window_height = 8 * self.screen_height / 100 - attr = pango.AttrList() - attr.change(pango.AttrSize(( - 50 * window_height / 100) * 1000, 0, -1)) - attr.change(pango.AttrFamily("Sans", 0, -1)) - attr.change(pango.AttrWeight(pango.WEIGHT_BOLD, 0, -1)) - attr.change(pango.AttrForeground(65535, 65535, 65535, 0, -1)) - - self.label.set_attributes(attr) self.resize(window_width, window_height) def set_xy_position(self, setting): @@ -221,6 +227,8 @@ def set_xy_position(self, setting): self.move(0, self.screen_height / 2) if setting == POS_BOTTOM: self.move(0, self.screen_height - window_height * 2) + if setting == POS_KEEP: + self.move(self.pos_x, self.pos_y) def on_statusicon_popup(self, widget, button, timestamp, data=None): if button == 3: @@ -244,7 +252,8 @@ def on_label_change(self, widget, data=None): def on_timeout(self): gtk.gdk.threads_enter() - self.hide() + if not self.no_hide: + self.hide() self.label.set_text("") gtk.gdk.threads_leave() @@ -264,6 +273,28 @@ def on_show_keys(self, widget, data=None): self.logger.debug("Screenkey disabled.") self.listenkbd.stop() + def on_configure(self, _, event): + window_height = event.height + attr = pango.AttrList() + attr.change(pango.AttrSize(( + 50 * window_height / 100) * 1000, 0, -1)) + attr.change(pango.AttrFamily("Sans", 0, -1)) + attr.change(pango.AttrWeight(pango.WEIGHT_BOLD, 0, -1)) + + fgcolor = gtk.gdk.color_parse(self.fg) + attr.change(pango.AttrForeground(fgcolor.red, fgcolor.green, fgcolor.blue, 0, -1)) + + self.pos_x = event.x + self.pos_y = event.y + + if self.timer: + self.timer.cancel() + + self.timer = Timer(self.options['timeout'], self.on_timeout) + self.timer.start() + + self.label.set_attributes(attr) + def on_preferences_dialog(self, widget, data=None): prefs = gtk.Dialog(APP_NAME, None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, diff --git a/screenkey b/screenkey index 46674cb..32c4996 100755 --- a/screenkey +++ b/screenkey @@ -21,6 +21,7 @@ pygtk.require('2.0') import gtk import gettext import locale +import threading locale.setlocale(locale.LC_ALL, '') gettext.install('screenkey', unicode=True) @@ -37,6 +38,12 @@ def Main(): help=_("do not detach from the parent")) parser.add_option("-d", "--debug", action="store_true", dest="debug", default=False, help=_("show debug information")) + parser.add_option("-n", "--no-hide", action="store_true", + dest="nohide", default=False, help=_("do not hide")) + parser.add_option("--bg", action="store", + dest="bg", default="black", help=_("background color")) + parser.add_option("--fg", action="store", + dest="fg", default="white", help=_("foreground color")) (options, args) = parser.parse_args() if options.debug: @@ -56,9 +63,15 @@ def Main(): logging.basicConfig(level=logging.INFO) logger = logging.getLogger(APP_NAME) - s = Screenkey(logger=logger, nodetach=options.nodetach) - gtk.main() - + s = Screenkey(logger=logger, nodetach=options.nodetach, + nohide=options.nohide, + bg=options.bg, fg=options.fg) + try: + gtk.main() + finally: + # listenkbd thread do not terminate due to bug in Xlib implementation + if threading.active_count() > 1: + os.system('kill %d' % (os.getpid())) if __name__ == "__main__": Main() From 2de7d26594e9fc09fabc3b6f7e07fdf079794d4f Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 3 Jul 2014 13:48:43 +0700 Subject: [PATCH 05/29] use python2 --- screenkey | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screenkey b/screenkey index 32c4996..c7de2e3 100755 --- a/screenkey +++ b/screenkey @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # Copyright (c) 2010 Pablo Seminario # 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 From 90708ddb8e888e03eee48340a6f30ffce729b381 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 3 Jul 2014 13:58:59 +0700 Subject: [PATCH 06/29] visual space character --- Screenkey/listenkbd.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 99d403d..3e64722 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -249,6 +249,8 @@ def key_normal_mode(self, event): key = key_deadshift if event.detail == 22: key = u'\u21D0' + if event.detail == 65: + key = u'\u2423' string = self.replace_key(key, keysym) if string: From 8ab6483181b8ebb48a4bc2ad30033211b775dc77 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 3 Jul 2014 14:57:31 +0700 Subject: [PATCH 07/29] add --no-sudo flag (major speedup) and fix CTRL+F1 --- Screenkey/listenkbd.py | 14 ++++++++------ Screenkey/screenkey.py | 12 ++++++++---- screenkey | 5 ++++- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 3e64722..d9eb912 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -76,7 +76,7 @@ class ListenKbd(threading.Thread): # Add in a shortcut to disable _disabled = False - def __init__(self, label, logger, mode): + def __init__(self, label, logger, mode, nosudo): threading.Thread.__init__(self) self.mode = mode self.logger = logger @@ -85,6 +85,7 @@ def __init__(self, label, logger, mode): self.command = None self.shift = None self.prev_key = '' + self.nosudo = nosudo self.cmd_keys = { 'shift': False, 'ctrl': False, @@ -141,7 +142,7 @@ def update_text(self, string=None): gtk.gdk.threads_enter() if not string is None: # TODO: make this configurable - if string == 'Ctrl+F1 ': + if string.strip() == 'Ctrl+F1': if self._disabled: self._disabled=False self.text = "[ENABLED]" @@ -165,10 +166,11 @@ def key_press(self, reply): # FIXME: # This is not the most efficient way to detect the # use of sudo/gksudo but it works. - sudo_is_running = subprocess.call(['ps', '-C', 'sudo'], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if not sudo_is_running: - return + if not self.nosudo: + sudo_is_running = subprocess.call(['ps', '-C', 'sudo'], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if not sudo_is_running: + return if reply.category != record.FromServer: return diff --git a/Screenkey/screenkey.py b/Screenkey/screenkey.py index ba218c6..1ed88bc 100644 --- a/Screenkey/screenkey.py +++ b/Screenkey/screenkey.py @@ -58,7 +58,7 @@ class Screenkey(gtk.Window): STATE_FILE = os.path.join(glib.get_user_cache_dir(), 'screenkey.dat') - def __init__(self, logger, nodetach, nohide, bg, fg): + def __init__(self, logger, nodetach, nohide, bg, fg, nosudo): gtk.Window.__init__(self) self.timer = None self.logger = logger @@ -109,8 +109,11 @@ def __init__(self, logger, nodetach, nohide, bg, fg): self.set_gravity(gtk.gdk.GRAVITY_CENTER) self.set_xy_position(self.options['position']) + self.nosudo = nosudo + self.listenkbd = ListenKbd(self.label, logger=self.logger, - mode=self.options['mode']) + mode=self.options['mode'], + nosudo=self.nosudo) self.listenkbd.start() @@ -260,14 +263,15 @@ def on_timeout(self): def on_change_mode(self, mode): self.listenkbd.stop() self.listenkbd = ListenKbd(self.label, logger=self.logger, - mode=mode) + mode=mode, nosudo=self.nosudo) self.listenkbd.start() def on_show_keys(self, widget, data=None): if widget.get_active(): self.logger.debug("Screenkey enabled.") self.listenkbd = ListenKbd(self.label, logger=self.logger, - mode=self.options['mode']) + mode=self.options['mode'], + nosudo=self.nosudo) self.listenkbd.start() else: self.logger.debug("Screenkey disabled.") diff --git a/screenkey b/screenkey index c7de2e3..5c70e6e 100755 --- a/screenkey +++ b/screenkey @@ -44,6 +44,8 @@ def Main(): dest="bg", default="black", help=_("background color")) parser.add_option("--fg", action="store", dest="fg", default="white", help=_("foreground color")) + parser.add_option("--no-sudo", action="store_true", + dest="nosudo", default="white", help=_("do not perform very expensive sudo running check")) (options, args) = parser.parse_args() if options.debug: @@ -65,7 +67,8 @@ def Main(): s = Screenkey(logger=logger, nodetach=options.nodetach, nohide=options.nohide, - bg=options.bg, fg=options.fg) + bg=options.bg, fg=options.fg, + nosudo=options.nosudo) try: gtk.main() finally: From af4b5233ead55a6e61aa157dbe202e619058033b Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 3 Jul 2014 15:21:44 +0700 Subject: [PATCH 08/29] fix .gitignore --- .gitignore | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 6425e29..0d20b64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1 @@ -[Ll]ibrary/ -[Tt]emp/ -[Oo]bj/ - -# Autogenerated VS/MD solution and project files -*.csproj -*.unityproj -*.sln +*.pyc From 38f80412fc068db61e9de07c15948c63182d2cce Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 3 Jul 2014 19:25:12 +0700 Subject: [PATCH 09/29] layout detecting, caps -> keyboard symbol --- Screenkey/listenkbd.py | 25 +- Screenkey/modmap.py | 815 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 793 insertions(+), 47 deletions(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index d9eb912..e39da9f 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -29,7 +29,7 @@ 'XK_Escape':_('Esc'), 'XK_Tab':u'\u21B9', 'XK_Return':u'\u23CE', - 'XK_Space':u' ', + 'XK_Space':u'', 'XK_Caps_Lock':_('Caps'), 'XK_F1':u'F1', 'XK_F2':u'F2', @@ -132,7 +132,7 @@ def lookup_keysym(self, keysym): return name[3:] return "" - def replace_key(self, key, keysym): + def replace_xk_key(self, key, keysym): for name in dir(XK): if name[:3] == "XK_" and getattr(XK, name) == keysym: if name in REPLACE_KEYS: @@ -201,8 +201,15 @@ def key_normal_mode(self, event): keysym = self.local_dpy.keycode_to_keysym(event.detail, 0) if event.detail in self.keymap: - key_normal, key_shift, key_dead, key_deadshift = \ - self.keymap[event.detail] + key_normal_1, key_shift_1, \ + key_normal_2, key_shift_2, \ + key_dead, key_deadshift = self.keymap[event.detail] + if event.state & 1 << 13 != 0: + key_normal = key_normal_2 + key_shift = key_shift_2 + else: + key_normal = key_normal_1 + key_shift = key_shift_1 self.logger.debug("Key %s(keycode) %s. Symbols %s" % (event.detail, event.type == X.KeyPress and "pressed" or "released", @@ -240,7 +247,8 @@ def key_normal_mode(self, event): mod = mod + _("Super+") if self.cmd_keys['shift']: - mod = mod + _("Shift+") + if key_shift == key_normal or key_normal == u'\x00' or key_shift == u'\x00': + mod = mod + _("Shift+") key = key_shift if self.cmd_keys['capslock'] \ and ord(key_normal) in range(97,123): @@ -253,8 +261,10 @@ def key_normal_mode(self, event): key = u'\u21D0' if event.detail == 65: key = u'\u2423' + if event.detail == 66: + key = u'\u2328' - string = self.replace_key(key, keysym) + string = self.replace_xk_key(key, keysym) if string: key = string @@ -264,6 +274,9 @@ def key_normal_mode(self, event): self.prev_key = key if len(prev_key) > 1 or len(key) > 1: key = " " + key + + if event.detail == 65: + key += u'\u200A' else: return diff --git a/Screenkey/modmap.py b/Screenkey/modmap.py index cd4d863..80b8228 100644 --- a/Screenkey/modmap.py +++ b/Screenkey/modmap.py @@ -38,47 +38,14 @@ def get_keymap_table(): new_keysyms = [] keycode = int(keycode.group(1)) keysyms = re_line.findall(line) - # When you press only one key - unicode_char = '' - try: - unicode_char = unichr(int(keysyms[0], 16)) - except: - unicode_char = '' - if unicode_char == u'\x00': - unicode_char = '' - new_keysyms.append(unicode_char) - - # When you press a key plus Shift key - unicode_char = '' - try: - unicode_char = unichr(int(keysyms[1], 16)) - except: - unicode_char = '' - if unicode_char == u'\x00': - unicode_char = '' - new_keysyms.append(unicode_char) - - # When you press a key plus meta (dead keys) - unicode_char = '' - try: - unicode_char = unichr(int(keysyms[4], 16)) - except: - unicode_char = '' - if unicode_char == u'\x00': - unicode_char = '' - new_keysyms.append(unicode_char) - - # When you press a key plus meta plus Shift key - unicode_char = '' - try: - unicode_char = unichr(int(keysyms[5], 16)) - except: - unicode_char = '' - if unicode_char == u'\x00': - unicode_char = '' - new_keysyms.append(unicode_char) - - #keymap[keycode-8] = new_keysyms + + for i in range(0, 6): + if i < len(keysyms): + keysym = keysyms[i] + else: + keysym = '0x0' + new_keysyms.append(keysym_to_unicode(int(keysym, 16))) + keymap[keycode] = new_keysyms return keymap @@ -102,3 +69,769 @@ def get_modifier_map(): return modifiers +def keysym_to_unicode(keysym): + if keysym in mapping: + return unichr(mapping[keysym]) + else: + return unichr(keysym) + +mapping = { + 0x01a1: 0x0104, + 0x01a2: 0x02d8, + 0x01a3: 0x0141, + 0x01a5: 0x013d, + 0x01a6: 0x015a, + 0x01a9: 0x0160, + 0x01aa: 0x015e, + 0x01ab: 0x0164, + 0x01ac: 0x0179, + 0x01ae: 0x017d, + 0x01af: 0x017b, + 0x01b1: 0x0105, + 0x01b2: 0x02db, + 0x01b3: 0x0142, + 0x01b5: 0x013e, + 0x01b6: 0x015b, + 0x01b7: 0x02c7, + 0x01b9: 0x0161, + 0x01ba: 0x015f, + 0x01bb: 0x0165, + 0x01bc: 0x017a, + 0x01bd: 0x02dd, + 0x01be: 0x017e, + 0x01bf: 0x017c, + 0x01c0: 0x0154, + 0x01c3: 0x0102, + 0x01c5: 0x0139, + 0x01c6: 0x0106, + 0x01c8: 0x010c, + 0x01ca: 0x0118, + 0x01cc: 0x011a, + 0x01cf: 0x010e, + 0x01d0: 0x0110, + 0x01d1: 0x0143, + 0x01d2: 0x0147, + 0x01d5: 0x0150, + 0x01d8: 0x0158, + 0x01d9: 0x016e, + 0x01db: 0x0170, + 0x01de: 0x0162, + 0x01e0: 0x0155, + 0x01e3: 0x0103, + 0x01e5: 0x013a, + 0x01e6: 0x0107, + 0x01e8: 0x010d, + 0x01ea: 0x0119, + 0x01ec: 0x011b, + 0x01ef: 0x010f, + 0x01f0: 0x0111, + 0x01f1: 0x0144, + 0x01f2: 0x0148, + 0x01f5: 0x0151, + 0x01f8: 0x0159, + 0x01f9: 0x016f, + 0x01fb: 0x0171, + 0x01fe: 0x0163, + 0x01ff: 0x02d9, + 0x02a1: 0x0126, + 0x02a6: 0x0124, + 0x02a9: 0x0130, + 0x02ab: 0x011e, + 0x02ac: 0x0134, + 0x02b1: 0x0127, + 0x02b6: 0x0125, + 0x02b9: 0x0131, + 0x02bb: 0x011f, + 0x02bc: 0x0135, + 0x02c5: 0x010a, + 0x02c6: 0x0108, + 0x02d5: 0x0120, + 0x02d8: 0x011c, + 0x02dd: 0x016c, + 0x02de: 0x015c, + 0x02e5: 0x010b, + 0x02e6: 0x0109, + 0x02f5: 0x0121, + 0x02f8: 0x011d, + 0x02fd: 0x016d, + 0x02fe: 0x015d, + 0x03a2: 0x0138, + 0x03a3: 0x0156, + 0x03a5: 0x0128, + 0x03a6: 0x013b, + 0x03aa: 0x0112, + 0x03ab: 0x0122, + 0x03ac: 0x0166, + 0x03b3: 0x0157, + 0x03b5: 0x0129, + 0x03b6: 0x013c, + 0x03ba: 0x0113, + 0x03bb: 0x0123, + 0x03bc: 0x0167, + 0x03bd: 0x014a, + 0x03bf: 0x014b, + 0x03c0: 0x0100, + 0x03c7: 0x012e, + 0x03cc: 0x0116, + 0x03cf: 0x012a, + 0x03d1: 0x0145, + 0x03d2: 0x014c, + 0x03d3: 0x0136, + 0x03d9: 0x0172, + 0x03dd: 0x0168, + 0x03de: 0x016a, + 0x03e0: 0x0101, + 0x03e7: 0x012f, + 0x03ec: 0x0117, + 0x03ef: 0x012b, + 0x03f1: 0x0146, + 0x03f2: 0x014d, + 0x03f3: 0x0137, + 0x03f9: 0x0173, + 0x03fd: 0x0169, + 0x03fe: 0x016b, + 0x047e: 0x203e, + 0x04a1: 0x3002, + 0x04a2: 0x300c, + 0x04a3: 0x300d, + 0x04a4: 0x3001, + 0x04a5: 0x30fb, + 0x04a6: 0x30f2, + 0x04a7: 0x30a1, + 0x04a8: 0x30a3, + 0x04a9: 0x30a5, + 0x04aa: 0x30a7, + 0x04ab: 0x30a9, + 0x04ac: 0x30e3, + 0x04ad: 0x30e5, + 0x04ae: 0x30e7, + 0x04af: 0x30c3, + 0x04b0: 0x30fc, + 0x04b1: 0x30a2, + 0x04b2: 0x30a4, + 0x04b3: 0x30a6, + 0x04b4: 0x30a8, + 0x04b5: 0x30aa, + 0x04b6: 0x30ab, + 0x04b7: 0x30ad, + 0x04b8: 0x30af, + 0x04b9: 0x30b1, + 0x04ba: 0x30b3, + 0x04bb: 0x30b5, + 0x04bc: 0x30b7, + 0x04bd: 0x30b9, + 0x04be: 0x30bb, + 0x04bf: 0x30bd, + 0x04c0: 0x30bf, + 0x04c1: 0x30c1, + 0x04c2: 0x30c4, + 0x04c3: 0x30c6, + 0x04c4: 0x30c8, + 0x04c5: 0x30ca, + 0x04c6: 0x30cb, + 0x04c7: 0x30cc, + 0x04c8: 0x30cd, + 0x04c9: 0x30ce, + 0x04ca: 0x30cf, + 0x04cb: 0x30d2, + 0x04cc: 0x30d5, + 0x04cd: 0x30d8, + 0x04ce: 0x30db, + 0x04cf: 0x30de, + 0x04d0: 0x30df, + 0x04d1: 0x30e0, + 0x04d2: 0x30e1, + 0x04d3: 0x30e2, + 0x04d4: 0x30e4, + 0x04d5: 0x30e6, + 0x04d6: 0x30e8, + 0x04d7: 0x30e9, + 0x04d8: 0x30ea, + 0x04d9: 0x30eb, + 0x04da: 0x30ec, + 0x04db: 0x30ed, + 0x04dc: 0x30ef, + 0x04dd: 0x30f3, + 0x04de: 0x309b, + 0x04df: 0x309c, + 0x05ac: 0x060c, + 0x05bb: 0x061b, + 0x05bf: 0x061f, + 0x05c1: 0x0621, + 0x05c2: 0x0622, + 0x05c3: 0x0623, + 0x05c4: 0x0624, + 0x05c5: 0x0625, + 0x05c6: 0x0626, + 0x05c7: 0x0627, + 0x05c8: 0x0628, + 0x05c9: 0x0629, + 0x05ca: 0x062a, + 0x05cb: 0x062b, + 0x05cc: 0x062c, + 0x05cd: 0x062d, + 0x05ce: 0x062e, + 0x05cf: 0x062f, + 0x05d0: 0x0630, + 0x05d1: 0x0631, + 0x05d2: 0x0632, + 0x05d3: 0x0633, + 0x05d4: 0x0634, + 0x05d5: 0x0635, + 0x05d6: 0x0636, + 0x05d7: 0x0637, + 0x05d8: 0x0638, + 0x05d9: 0x0639, + 0x05da: 0x063a, + 0x05e0: 0x0640, + 0x05e1: 0x0641, + 0x05e2: 0x0642, + 0x05e3: 0x0643, + 0x05e4: 0x0644, + 0x05e5: 0x0645, + 0x05e6: 0x0646, + 0x05e7: 0x0647, + 0x05e8: 0x0648, + 0x05e9: 0x0649, + 0x05ea: 0x064a, + 0x05eb: 0x064b, + 0x05ec: 0x064c, + 0x05ed: 0x064d, + 0x05ee: 0x064e, + 0x05ef: 0x064f, + 0x05f0: 0x0650, + 0x05f1: 0x0651, + 0x05f2: 0x0652, + 0x06a1: 0x0452, + 0x06a2: 0x0453, + 0x06a3: 0x0451, + 0x06a4: 0x0454, + 0x06a5: 0x0455, + 0x06a6: 0x0456, + 0x06a7: 0x0457, + 0x06a8: 0x0458, + 0x06a9: 0x0459, + 0x06aa: 0x045a, + 0x06ab: 0x045b, + 0x06ac: 0x045c, + 0x06ae: 0x045e, + 0x06af: 0x045f, + 0x06b0: 0x2116, + 0x06b1: 0x0402, + 0x06b2: 0x0403, + 0x06b3: 0x0401, + 0x06b4: 0x0404, + 0x06b5: 0x0405, + 0x06b6: 0x0406, + 0x06b7: 0x0407, + 0x06b8: 0x0408, + 0x06b9: 0x0409, + 0x06ba: 0x040a, + 0x06bb: 0x040b, + 0x06bc: 0x040c, + 0x06be: 0x040e, + 0x06bf: 0x040f, + 0x06c0: 0x044e, + 0x06c1: 0x0430, + 0x06c2: 0x0431, + 0x06c3: 0x0446, + 0x06c4: 0x0434, + 0x06c5: 0x0435, + 0x06c6: 0x0444, + 0x06c7: 0x0433, + 0x06c8: 0x0445, + 0x06c9: 0x0438, + 0x06ca: 0x0439, + 0x06cb: 0x043a, + 0x06cc: 0x043b, + 0x06cd: 0x043c, + 0x06ce: 0x043d, + 0x06cf: 0x043e, + 0x06d0: 0x043f, + 0x06d1: 0x044f, + 0x06d2: 0x0440, + 0x06d3: 0x0441, + 0x06d4: 0x0442, + 0x06d5: 0x0443, + 0x06d6: 0x0436, + 0x06d7: 0x0432, + 0x06d8: 0x044c, + 0x06d9: 0x044b, + 0x06da: 0x0437, + 0x06db: 0x0448, + 0x06dc: 0x044d, + 0x06dd: 0x0449, + 0x06de: 0x0447, + 0x06df: 0x044a, + 0x06e0: 0x042e, + 0x06e1: 0x0410, + 0x06e2: 0x0411, + 0x06e3: 0x0426, + 0x06e4: 0x0414, + 0x06e5: 0x0415, + 0x06e6: 0x0424, + 0x06e7: 0x0413, + 0x06e8: 0x0425, + 0x06e9: 0x0418, + 0x06ea: 0x0419, + 0x06eb: 0x041a, + 0x06ec: 0x041b, + 0x06ed: 0x041c, + 0x06ee: 0x041d, + 0x06ef: 0x041e, + 0x06f0: 0x041f, + 0x06f1: 0x042f, + 0x06f2: 0x0420, + 0x06f3: 0x0421, + 0x06f4: 0x0422, + 0x06f5: 0x0423, + 0x06f6: 0x0416, + 0x06f7: 0x0412, + 0x06f8: 0x042c, + 0x06f9: 0x042b, + 0x06fa: 0x0417, + 0x06fb: 0x0428, + 0x06fc: 0x042d, + 0x06fd: 0x0429, + 0x06fe: 0x0427, + 0x06ff: 0x042a, + 0x07a1: 0x0386, + 0x07a2: 0x0388, + 0x07a3: 0x0389, + 0x07a4: 0x038a, + 0x07a5: 0x03aa, + 0x07a7: 0x038c, + 0x07a8: 0x038e, + 0x07a9: 0x03ab, + 0x07ab: 0x038f, + 0x07ae: 0x0385, + 0x07af: 0x2015, + 0x07b1: 0x03ac, + 0x07b2: 0x03ad, + 0x07b3: 0x03ae, + 0x07b4: 0x03af, + 0x07b5: 0x03ca, + 0x07b6: 0x0390, + 0x07b7: 0x03cc, + 0x07b8: 0x03cd, + 0x07b9: 0x03cb, + 0x07ba: 0x03b0, + 0x07bb: 0x03ce, + 0x07c1: 0x0391, + 0x07c2: 0x0392, + 0x07c3: 0x0393, + 0x07c4: 0x0394, + 0x07c5: 0x0395, + 0x07c6: 0x0396, + 0x07c7: 0x0397, + 0x07c8: 0x0398, + 0x07c9: 0x0399, + 0x07ca: 0x039a, + 0x07cb: 0x039b, + 0x07cc: 0x039c, + 0x07cd: 0x039d, + 0x07ce: 0x039e, + 0x07cf: 0x039f, + 0x07d0: 0x03a0, + 0x07d1: 0x03a1, + 0x07d2: 0x03a3, + 0x07d4: 0x03a4, + 0x07d5: 0x03a5, + 0x07d6: 0x03a6, + 0x07d7: 0x03a7, + 0x07d8: 0x03a8, + 0x07d9: 0x03a9, + 0x07e1: 0x03b1, + 0x07e2: 0x03b2, + 0x07e3: 0x03b3, + 0x07e4: 0x03b4, + 0x07e5: 0x03b5, + 0x07e6: 0x03b6, + 0x07e7: 0x03b7, + 0x07e8: 0x03b8, + 0x07e9: 0x03b9, + 0x07ea: 0x03ba, + 0x07eb: 0x03bb, + 0x07ec: 0x03bc, + 0x07ed: 0x03bd, + 0x07ee: 0x03be, + 0x07ef: 0x03bf, + 0x07f0: 0x03c0, + 0x07f1: 0x03c1, + 0x07f2: 0x03c3, + 0x07f3: 0x03c2, + 0x07f4: 0x03c4, + 0x07f5: 0x03c5, + 0x07f6: 0x03c6, + 0x07f7: 0x03c7, + 0x07f8: 0x03c8, + 0x07f9: 0x03c9, + 0x08a1: 0x23b7, + 0x08a2: 0x250c, + 0x08a3: 0x2500, + 0x08a4: 0x2320, + 0x08a5: 0x2321, + 0x08a6: 0x2502, + 0x08a7: 0x23a1, + 0x08a8: 0x23a3, + 0x08a9: 0x23a4, + 0x08aa: 0x23a6, + 0x08ab: 0x239b, + 0x08ac: 0x239d, + 0x08ad: 0x239e, + 0x08ae: 0x23a0, + 0x08af: 0x23a8, + 0x08b0: 0x23ac, + 0x08bc: 0x2264, + 0x08bd: 0x2260, + 0x08be: 0x2265, + 0x08bf: 0x222b, + 0x08c0: 0x2234, + 0x08c1: 0x221d, + 0x08c2: 0x221e, + 0x08c5: 0x2207, + 0x08c8: 0x223c, + 0x08c9: 0x2243, + 0x08cd: 0x21d4, + 0x08ce: 0x21d2, + 0x08cf: 0x2261, + 0x08d6: 0x221a, + 0x08da: 0x2282, + 0x08db: 0x2283, + 0x08dc: 0x2229, + 0x08dd: 0x222a, + 0x08de: 0x2227, + 0x08df: 0x2228, + 0x08ef: 0x2202, + 0x08f6: 0x0192, + 0x08fb: 0x2190, + 0x08fc: 0x2191, + 0x08fd: 0x2192, + 0x08fe: 0x2193, + 0x09e0: 0x25c6, + 0x09e1: 0x2592, + 0x09e2: 0x2409, + 0x09e3: 0x240c, + 0x09e4: 0x240d, + 0x09e5: 0x240a, + 0x09e8: 0x2424, + 0x09e9: 0x240b, + 0x09ea: 0x2518, + 0x09eb: 0x2510, + 0x09ec: 0x250c, + 0x09ed: 0x2514, + 0x09ee: 0x253c, + 0x09ef: 0x23ba, + 0x09f0: 0x23bb, + 0x09f1: 0x2500, + 0x09f2: 0x23bc, + 0x09f3: 0x23bd, + 0x09f4: 0x251c, + 0x09f5: 0x2524, + 0x09f6: 0x2534, + 0x09f7: 0x252c, + 0x09f8: 0x2502, + 0x0aa1: 0x2003, + 0x0aa2: 0x2002, + 0x0aa3: 0x2004, + 0x0aa4: 0x2005, + 0x0aa5: 0x2007, + 0x0aa6: 0x2008, + 0x0aa7: 0x2009, + 0x0aa8: 0x200a, + 0x0aa9: 0x2014, + 0x0aaa: 0x2013, + 0x0aae: 0x2026, + 0x0aaf: 0x2025, + 0x0ab0: 0x2153, + 0x0ab1: 0x2154, + 0x0ab2: 0x2155, + 0x0ab3: 0x2156, + 0x0ab4: 0x2157, + 0x0ab5: 0x2158, + 0x0ab6: 0x2159, + 0x0ab7: 0x215a, + 0x0ab8: 0x2105, + 0x0abb: 0x2012, + 0x0abc: 0x2329, + 0x0abe: 0x232a, + 0x0ac3: 0x215b, + 0x0ac4: 0x215c, + 0x0ac5: 0x215d, + 0x0ac6: 0x215e, + 0x0ac9: 0x2122, + 0x0aca: 0x2613, + 0x0acc: 0x25c1, + 0x0acd: 0x25b7, + 0x0ace: 0x25cb, + 0x0acf: 0x25af, + 0x0ad0: 0x2018, + 0x0ad1: 0x2019, + 0x0ad2: 0x201c, + 0x0ad3: 0x201d, + 0x0ad4: 0x211e, + 0x0ad6: 0x2032, + 0x0ad7: 0x2033, + 0x0ad9: 0x271d, + 0x0adb: 0x25ac, + 0x0adc: 0x25c0, + 0x0add: 0x25b6, + 0x0ade: 0x25cf, + 0x0adf: 0x25ae, + 0x0ae0: 0x25e6, + 0x0ae1: 0x25ab, + 0x0ae2: 0x25ad, + 0x0ae3: 0x25b3, + 0x0ae4: 0x25bd, + 0x0ae5: 0x2606, + 0x0ae6: 0x2022, + 0x0ae7: 0x25aa, + 0x0ae8: 0x25b2, + 0x0ae9: 0x25bc, + 0x0aea: 0x261c, + 0x0aeb: 0x261e, + 0x0aec: 0x2663, + 0x0aed: 0x2666, + 0x0aee: 0x2665, + 0x0af0: 0x2720, + 0x0af1: 0x2020, + 0x0af2: 0x2021, + 0x0af3: 0x2713, + 0x0af4: 0x2717, + 0x0af5: 0x266f, + 0x0af6: 0x266d, + 0x0af7: 0x2642, + 0x0af8: 0x2640, + 0x0af9: 0x260e, + 0x0afa: 0x2315, + 0x0afb: 0x2117, + 0x0afc: 0x2038, + 0x0afd: 0x201a, + 0x0afe: 0x201e, + 0x0ba3: 0x003c, + 0x0ba6: 0x003e, + 0x0ba8: 0x2228, + 0x0ba9: 0x2227, + 0x0bc0: 0x00af, + 0x0bc2: 0x22a5, + 0x0bc3: 0x2229, + 0x0bc4: 0x230a, + 0x0bc6: 0x005f, + 0x0bca: 0x2218, + 0x0bcc: 0x2395, + 0x0bce: 0x22a4, + 0x0bcf: 0x25cb, + 0x0bd3: 0x2308, + 0x0bd6: 0x222a, + 0x0bd8: 0x2283, + 0x0bda: 0x2282, + 0x0bdc: 0x22a2, + 0x0bfc: 0x22a3, + 0x0cdf: 0x2017, + 0x0ce0: 0x05d0, + 0x0ce1: 0x05d1, + 0x0ce2: 0x05d2, + 0x0ce3: 0x05d3, + 0x0ce4: 0x05d4, + 0x0ce5: 0x05d5, + 0x0ce6: 0x05d6, + 0x0ce7: 0x05d7, + 0x0ce8: 0x05d8, + 0x0ce9: 0x05d9, + 0x0cea: 0x05da, + 0x0ceb: 0x05db, + 0x0cec: 0x05dc, + 0x0ced: 0x05dd, + 0x0cee: 0x05de, + 0x0cef: 0x05df, + 0x0cf0: 0x05e0, + 0x0cf1: 0x05e1, + 0x0cf2: 0x05e2, + 0x0cf3: 0x05e3, + 0x0cf4: 0x05e4, + 0x0cf5: 0x05e5, + 0x0cf6: 0x05e6, + 0x0cf7: 0x05e7, + 0x0cf8: 0x05e8, + 0x0cf9: 0x05e9, + 0x0cfa: 0x05ea, + 0x0da1: 0x0e01, + 0x0da2: 0x0e02, + 0x0da3: 0x0e03, + 0x0da4: 0x0e04, + 0x0da5: 0x0e05, + 0x0da6: 0x0e06, + 0x0da7: 0x0e07, + 0x0da8: 0x0e08, + 0x0da9: 0x0e09, + 0x0daa: 0x0e0a, + 0x0dab: 0x0e0b, + 0x0dac: 0x0e0c, + 0x0dad: 0x0e0d, + 0x0dae: 0x0e0e, + 0x0daf: 0x0e0f, + 0x0db0: 0x0e10, + 0x0db1: 0x0e11, + 0x0db2: 0x0e12, + 0x0db3: 0x0e13, + 0x0db4: 0x0e14, + 0x0db5: 0x0e15, + 0x0db6: 0x0e16, + 0x0db7: 0x0e17, + 0x0db8: 0x0e18, + 0x0db9: 0x0e19, + 0x0dba: 0x0e1a, + 0x0dbb: 0x0e1b, + 0x0dbc: 0x0e1c, + 0x0dbd: 0x0e1d, + 0x0dbe: 0x0e1e, + 0x0dbf: 0x0e1f, + 0x0dc0: 0x0e20, + 0x0dc1: 0x0e21, + 0x0dc2: 0x0e22, + 0x0dc3: 0x0e23, + 0x0dc4: 0x0e24, + 0x0dc5: 0x0e25, + 0x0dc6: 0x0e26, + 0x0dc7: 0x0e27, + 0x0dc8: 0x0e28, + 0x0dc9: 0x0e29, + 0x0dca: 0x0e2a, + 0x0dcb: 0x0e2b, + 0x0dcc: 0x0e2c, + 0x0dcd: 0x0e2d, + 0x0dce: 0x0e2e, + 0x0dcf: 0x0e2f, + 0x0dd0: 0x0e30, + 0x0dd1: 0x0e31, + 0x0dd2: 0x0e32, + 0x0dd3: 0x0e33, + 0x0dd4: 0x0e34, + 0x0dd5: 0x0e35, + 0x0dd6: 0x0e36, + 0x0dd7: 0x0e37, + 0x0dd8: 0x0e38, + 0x0dd9: 0x0e39, + 0x0dda: 0x0e3a, + 0x0ddf: 0x0e3f, + 0x0de0: 0x0e40, + 0x0de1: 0x0e41, + 0x0de2: 0x0e42, + 0x0de3: 0x0e43, + 0x0de4: 0x0e44, + 0x0de5: 0x0e45, + 0x0de6: 0x0e46, + 0x0de7: 0x0e47, + 0x0de8: 0x0e48, + 0x0de9: 0x0e49, + 0x0dea: 0x0e4a, + 0x0deb: 0x0e4b, + 0x0dec: 0x0e4c, + 0x0ded: 0x0e4d, + 0x0df0: 0x0e50, + 0x0df1: 0x0e51, + 0x0df2: 0x0e52, + 0x0df3: 0x0e53, + 0x0df4: 0x0e54, + 0x0df5: 0x0e55, + 0x0df6: 0x0e56, + 0x0df7: 0x0e57, + 0x0df8: 0x0e58, + 0x0df9: 0x0e59, + 0x0ea1: 0x3131, + 0x0ea2: 0x3132, + 0x0ea3: 0x3133, + 0x0ea4: 0x3134, + 0x0ea5: 0x3135, + 0x0ea6: 0x3136, + 0x0ea7: 0x3137, + 0x0ea8: 0x3138, + 0x0ea9: 0x3139, + 0x0eaa: 0x313a, + 0x0eab: 0x313b, + 0x0eac: 0x313c, + 0x0ead: 0x313d, + 0x0eae: 0x313e, + 0x0eaf: 0x313f, + 0x0eb0: 0x3140, + 0x0eb1: 0x3141, + 0x0eb2: 0x3142, + 0x0eb3: 0x3143, + 0x0eb4: 0x3144, + 0x0eb5: 0x3145, + 0x0eb6: 0x3146, + 0x0eb7: 0x3147, + 0x0eb8: 0x3148, + 0x0eb9: 0x3149, + 0x0eba: 0x314a, + 0x0ebb: 0x314b, + 0x0ebc: 0x314c, + 0x0ebd: 0x314d, + 0x0ebe: 0x314e, + 0x0ebf: 0x314f, + 0x0ec0: 0x3150, + 0x0ec1: 0x3151, + 0x0ec2: 0x3152, + 0x0ec3: 0x3153, + 0x0ec4: 0x3154, + 0x0ec5: 0x3155, + 0x0ec6: 0x3156, + 0x0ec7: 0x3157, + 0x0ec8: 0x3158, + 0x0ec9: 0x3159, + 0x0eca: 0x315a, + 0x0ecb: 0x315b, + 0x0ecc: 0x315c, + 0x0ecd: 0x315d, + 0x0ece: 0x315e, + 0x0ecf: 0x315f, + 0x0ed0: 0x3160, + 0x0ed1: 0x3161, + 0x0ed2: 0x3162, + 0x0ed3: 0x3163, + 0x0ed4: 0x11a8, + 0x0ed5: 0x11a9, + 0x0ed6: 0x11aa, + 0x0ed7: 0x11ab, + 0x0ed8: 0x11ac, + 0x0ed9: 0x11ad, + 0x0eda: 0x11ae, + 0x0edb: 0x11af, + 0x0edc: 0x11b0, + 0x0edd: 0x11b1, + 0x0ede: 0x11b2, + 0x0edf: 0x11b3, + 0x0ee0: 0x11b4, + 0x0ee1: 0x11b5, + 0x0ee2: 0x11b6, + 0x0ee3: 0x11b7, + 0x0ee4: 0x11b8, + 0x0ee5: 0x11b9, + 0x0ee6: 0x11ba, + 0x0ee7: 0x11bb, + 0x0ee8: 0x11bc, + 0x0ee9: 0x11bd, + 0x0eea: 0x11be, + 0x0eeb: 0x11bf, + 0x0eec: 0x11c0, + 0x0eed: 0x11c1, + 0x0eee: 0x11c2, + 0x0eef: 0x316d, + 0x0ef0: 0x3171, + 0x0ef1: 0x3178, + 0x0ef2: 0x317f, + 0x0ef3: 0x3181, + 0x0ef4: 0x3184, + 0x0ef5: 0x3186, + 0x0ef6: 0x318d, + 0x0ef7: 0x318e, + 0x0ef8: 0x11eb, + 0x0ef9: 0x11f0, + 0x0efa: 0x11f9, + 0x0eff: 0x20a9, + 0x13a4: 0x20ac, + 0x13bc: 0x0152, + 0x13bd: 0x0153, + 0x13be: 0x0178, + 0x20ac: 0x20ac, +} From 49cbb7a07c29c154ac317e3bc8d0a1fd92bba4c2 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Fri, 4 Jul 2014 04:51:01 +0700 Subject: [PATCH 10/29] make tab and caps looks like detached --- Screenkey/listenkbd.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index e39da9f..6d5ac48 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -84,7 +84,7 @@ def __init__(self, label, logger, mode, nosudo): self.text = "" self.command = None self.shift = None - self.prev_key = '' + self.detached = False self.nosudo = nosudo self.cmd_keys = { 'shift': False, @@ -257,11 +257,14 @@ def key_normal_mode(self, event): key = key_dead if self.cmd_keys['shift'] and self.cmd_keys['meta']: key = key_deadshift + if event.detail == 23: + self.detached = True if event.detail == 22: key = u'\u21D0' if event.detail == 65: key = u'\u2423' if event.detail == 66: + self.detached = True key = u'\u2328' string = self.replace_xk_key(key, keysym) @@ -270,11 +273,21 @@ def key_normal_mode(self, event): if mod != '': key = mod + key - prev_key = self.prev_key - self.prev_key = key - if len(prev_key) > 1 or len(key) > 1: + + detached = self.detached + self.detached = False + if len(key) > 1: + self.detached = True + if event.detail == 66: + self.detached = True + if event.detail == 23: + self.detached = True + + if detached or len(key) > 1: key = " " + key + print(self.detached) + if event.detail == 65: key += u'\u200A' else: From 284923f24ec0a68b24543222acb8310470f53500 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Fri, 4 Jul 2014 05:21:19 +0700 Subject: [PATCH 11/29] README.md: info about fork --- README.md | 77 ++++++++++++++++++++++++++----------------------------- screenkey | 2 +- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index c9d77d4..fe4e126 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,42 @@ screenkey ========= -Screenkey - https://bugs.launchpad.net/screenkey/+bugs -This is screenkey trunk with the following patch: -https://bugs.launchpad.net/screenkey/+bug/1133068 - -No other modifications, kudos to the guys below: - -Screenkey v0.2 -http://launchpad.net/screenkey - -About -===== -Screencast your keys. -A screencast tool to display your keys inspired by Screenflick -and initially based on the key-mon project code. - -Usage -===== -Download the latest version from https://launchpad.net/screenkey/+download -and you need install python-xlib. - -To run without installing (change x.x by current version number) - - tar xvfz screenkey-x.x.tar.gz - cd screenkey-x.x/ - sudo ./screenkey - -To install - - tar xvfz screenkey-x.x.tar.gz - cd screenkey-x.x/ - sudo ./setup.py install - -Or you can use dpkg (Debian and Ubuntu) - - sudo dpkg -i screenkey_x.x-y_all.deb - - -Author -====== +Fork of http://launchpad.net/screenkey + +What's up? +========== + +I've made significant changes, including: + +* new command line flags: + * `-fg` to set font color; + * `-bg` to set background color; + * `--no-sudo` do not perform check on launched sudo (it's currently VERY slow, + because of this check will launch subprocess on EVERY key typed); + * `-n, --no-hide` do not hide window after timeout, hide only text (make it + usable with tile managers, for example, with i3, which can not have + unfocusable windows); +* instead of removing text on `backspace` just add symbol `⇐`; it was broken + anyway, so it will happily erase `Ctrl+...` and other text; +* space is changed to visible character: `␣`; +* supported second keyboard layout, so not only ascii letters can be typed; +* way of detecting modifiers was changed and now based on pure event state + field; it's more robust and precise, so, for example, if you remap arrow + keys into Mod+[HJKL] (as I do) you get correct output (arrows, not Mod+arrows + or something); +* `Shift+` is now not printed for characters that have two distinct states + with and without Shift; it means, that when you type `Shift+q` you will see + `Q`. However, if you type `Shift+Left`, you will see `Shift+←`; +* added another position modifier, and now window can be dragged around to + choose optimal position and size; it will remembered and will not change + after window hide/show cycle; +* font size is now binded to window height, so if you resize window you will + get different font size; +* spacing between several keys are tweaked, so they will not nestle to each + other, like this: `Ctrl+vhello`; instead, you will see: `Ctrl+v hello`. + +Original author +=============== Pablo Seminario Thanks to @@ -64,5 +61,3 @@ 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 . - - diff --git a/screenkey b/screenkey index 5c70e6e..271aa17 100755 --- a/screenkey +++ b/screenkey @@ -41,7 +41,7 @@ def Main(): parser.add_option("-n", "--no-hide", action="store_true", dest="nohide", default=False, help=_("do not hide")) parser.add_option("--bg", action="store", - dest="bg", default="black", help=_("background color")) + dest="bg", default="black", help=_("background color (in #RRGGBB format)")) parser.add_option("--fg", action="store", dest="fg", default="white", help=_("foreground color")) parser.add_option("--no-sudo", action="store_true", From c3e3cf981b055ffc806e2959a230e14bdc897340 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Fri, 4 Jul 2014 05:23:16 +0700 Subject: [PATCH 12/29] README.md: fix some typos and spelling --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fe4e126..9e82292 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,13 @@ I've made significant changes, including: * `-n, --no-hide` do not hide window after timeout, hide only text (make it usable with tile managers, for example, with i3, which can not have unfocusable windows); -* instead of removing text on `backspace` just add symbol `⇐`; it was broken - anyway, so it will happily erase `Ctrl+...` and other text; +* instead of removing text on `backspace` symbol `⇐` will be added; it was + broken anyway, so it will happily erase `Ctrl+...` and other text; * space is changed to visible character: `␣`; -* supported second keyboard layout, so not only ascii letters can be typed; +* supported second keyboard layout, so not only ASCII letters can be recorded; * way of detecting modifiers was changed and now based on pure event state field; it's more robust and precise, so, for example, if you remap arrow - keys into Mod+[HJKL] (as I do) you get correct output (arrows, not Mod+arrows + keys into Mod+\[HJKL\] (as I do) you get correct output (arrows, not Mod+arrows or something); * `Shift+` is now not printed for characters that have two distinct states with and without Shift; it means, that when you type `Shift+q` you will see From 6b84f39b96e2adc52084360258519c208988f5bf Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 3 Jul 2014 23:28:10 +0700 Subject: [PATCH 13/29] README.md: add gif --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 9e82292..ef35f69 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,12 @@ I've made significant changes, including: * spacing between several keys are tweaked, so they will not nestle to each other, like this: `Ctrl+vhello`; instead, you will see: `Ctrl+v hello`. +Preview +======= +Some awkward highlights of this features shown here: + +![output](https://cloud.githubusercontent.com/assets/674812/3472704/d75e249e-02ce-11e4-90f0-d65b575b1574.gif) + Original author =============== Pablo Seminario From 3bcf7ce829997a75d16ebf673ac5763dfc150205 Mon Sep 17 00:00:00 2001 From: Egor Kovetskiy Date: Wed, 4 Feb 2015 16:43:02 +0600 Subject: [PATCH 14/29] align to another window --- Screenkey/screenkey.py | 64 ++++++++++++++++++++++++++++++++++++++---- screenkey | 8 ++++-- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/Screenkey/screenkey.py b/Screenkey/screenkey.py index 1ed88bc..b2ad0f9 100644 --- a/Screenkey/screenkey.py +++ b/Screenkey/screenkey.py @@ -13,6 +13,7 @@ # along with this program. If not, see . import os +import re import pygtk pygtk.require('2.0') import gtk @@ -24,6 +25,7 @@ from Screenkey import APP_NAME, APP_DESC, APP_URL, VERSION, AUTHOR from listenkbd import ListenKbd +from subprocess import Popen, PIPE, check_call, STDOUT POS_TOP = 0 POS_CENTER = 1 @@ -58,7 +60,7 @@ class Screenkey(gtk.Window): STATE_FILE = os.path.join(glib.get_user_cache_dir(), 'screenkey.dat') - def __init__(self, logger, nodetach, nohide, bg, fg, nosudo): + def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): gtk.Window.__init__(self) self.timer = None self.logger = logger @@ -67,8 +69,8 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo): if not self.options: self.options = { 'timeout': 2.5, - 'position': POS_KEEP, - 'size': SIZE_MEDIUM, + 'position': POS_BOTTOM, + 'size': SIZE_SMALL, 'mode': MODE_NORMAL, } @@ -104,10 +106,20 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo): self.screen_width = gtk.gdk.screen_width() self.screen_height = gtk.gdk.screen_height() - self.set_window_size(self.options['size']) self.set_gravity(gtk.gdk.GRAVITY_CENTER) - self.set_xy_position(self.options['position']) + + if window_id == 0: + self.set_xy_position(self.options['position']) + self.set_window_size(self.options['size']) + else: + other_win_pos = self.get_window_pos(window_id) + + window_width, window_height = self.set_window_size_of_other_win( + other_win_pos, self.options['size']) + + self.set_xy_position_of_other_win(other_win_pos, self.options['position'], + window_width, window_height) self.nosudo = nosudo @@ -207,6 +219,21 @@ def store_state(self, options): except IOError: self.logger.debug("Cannot open %s." % self.STATE_FILE) + def set_window_size_of_other_win(self, window_pos, setting): + """Set window and label size.""" + window_width = int(window_pos['width'] * 0.8) + window_height = -1 + + if setting == SIZE_LARGE: + window_height = 24 * self.screen_height / 100 + if setting == SIZE_MEDIUM: + window_height = 12 * self.screen_height / 100 + if setting == SIZE_SMALL: + window_height = 1 * self.screen_height / 100 + + self.resize(window_width, window_height) + return window_width, window_height + def set_window_size(self, setting): """Set window and label size.""" window_width = self.screen_width @@ -221,6 +248,33 @@ def set_window_size(self, setting): self.resize(window_width, window_height) + def get_window_pos(self, win_id): + """ get window position x,y and size width, height" """ + p = Popen(["xwininfo", "-id", win_id], stdout=PIPE) + out = p.communicate()[0] + #if p.returncode != 0: + x = int(re.search("Absolute upper-left X:.*?(\d+)", out).groups()[0]) + y = int(re.search("Absolute upper-left Y:.*?(\d+)", out).groups()[0]) + width = int(re.search("Width:.*?(\d+)", out).groups()[0]) + height = int(re.search("Height:.*?(\d+)", out).groups()[0]) + + return {'x': x, 'y': y, 'width': width, 'height': height} + + def set_xy_position_of_other_win(self, window_pos, setting, + window_width, window_height): + """Set window position.""" + + if setting == POS_TOP: + self.move(0, window_pos['height'] * 2) + if setting == POS_CENTER: + self.move(0, self.screen_height / 2) + if setting == POS_BOTTOM: + self.move(window_pos['x']+window_pos['width']-window_width, + int(window_pos['y'] + window_pos['height'] - \ + window_height*5 - window_pos['height']*0.05)) + if setting == POS_KEEP: + self.move(self.pos_x, self.pos_y) + def set_xy_position(self, setting): """Set window position.""" window_width, window_height = self.get_size() diff --git a/screenkey b/screenkey index 271aa17..8d3e2de 100755 --- a/screenkey +++ b/screenkey @@ -45,7 +45,10 @@ def Main(): parser.add_option("--fg", action="store", dest="fg", default="white", help=_("foreground color")) parser.add_option("--no-sudo", action="store_true", - dest="nosudo", default="white", help=_("do not perform very expensive sudo running check")) + dest="nosudo", default="white", + help=_("do not perform very expensive sudo running check")) + parser.add_option("-w", "--window", + dest="window_id", default=0, help=_("align to other window")) (options, args) = parser.parse_args() if options.debug: @@ -68,7 +71,8 @@ def Main(): s = Screenkey(logger=logger, nodetach=options.nodetach, nohide=options.nohide, bg=options.bg, fg=options.fg, - nosudo=options.nosudo) + nosudo=options.nosudo, + window_id=options.window_id) try: gtk.main() finally: From 3b4f1ccc073fa239ac9d5057d11b9fa64b625d9e Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Sun, 7 Jun 2015 23:41:47 +0600 Subject: [PATCH 15/29] mapped backspace, fix font size --- Screenkey/listenkbd.py | 3 +-- Screenkey/screenkey.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 6d5ac48..7aa1de1 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -52,6 +52,7 @@ 'XK_Down':u'\u2193', 'XK_Next':_('PgDn'), 'XK_Insert':_('Ins'), + 'XK_BackSpace':_(u'\u21d0'), 'XK_Delete':_('Del'), 'XK_KP_Home':u'(7)', 'XK_KP_Up':u'(8)', @@ -286,8 +287,6 @@ def key_normal_mode(self, event): if detached or len(key) > 1: key = " " + key - print(self.detached) - if event.detail == 65: key += u'\u200A' else: diff --git a/Screenkey/screenkey.py b/Screenkey/screenkey.py index b2ad0f9..9102275 100644 --- a/Screenkey/screenkey.py +++ b/Screenkey/screenkey.py @@ -229,7 +229,7 @@ def set_window_size_of_other_win(self, window_pos, setting): if setting == SIZE_MEDIUM: window_height = 12 * self.screen_height / 100 if setting == SIZE_SMALL: - window_height = 1 * self.screen_height / 100 + window_height = 3 * self.screen_height / 100 self.resize(window_width, window_height) return window_width, window_height @@ -336,7 +336,7 @@ def on_configure(self, _, event): attr = pango.AttrList() attr.change(pango.AttrSize(( 50 * window_height / 100) * 1000, 0, -1)) - attr.change(pango.AttrFamily("Sans", 0, -1)) + attr.change(pango.AttrFamily("DejaVu Sans", 0, -1)) attr.change(pango.AttrWeight(pango.WEIGHT_BOLD, 0, -1)) fgcolor = gtk.gdk.color_parse(self.fg) From 28804ed8110b9de378c66014f5573ce9ed9d18f7 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Fri, 19 Jun 2015 15:39:01 +0600 Subject: [PATCH 16/29] fix iso5_level_shift --- Screenkey/listenkbd.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 7aa1de1..2401553 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -134,6 +134,9 @@ def lookup_keysym(self, keysym): return "" def replace_xk_key(self, key, keysym): + if key == '\x00': + return '' + print(key, keysym) for name in dir(XK): if name[:3] == "XK_" and getattr(XK, name) == keysym: if name in REPLACE_KEYS: @@ -267,6 +270,9 @@ def key_normal_mode(self, event): if event.detail == 66: self.detached = True key = u'\u2328' + if event.detail == 108: + self.detached = True + key = u'\u2623' string = self.replace_xk_key(key, keysym) if string: From e7fe7c17d2c359ad75661f331f141136f20fbc57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denilson=20Figueiredo=20de=20S=C3=A1?= Date: Thu, 2 Jul 2015 12:19:41 -0300 Subject: [PATCH 17/29] =?UTF-8?q?Changing=20Backspace=20character=20to=20?= =?UTF-8?q?=E2=8C=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This proposed character is Unicode 'ERASE TO THE LEFT' (U+232B) --- Screenkey/listenkbd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 2401553..57d0edd 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -52,7 +52,7 @@ 'XK_Down':u'\u2193', 'XK_Next':_('PgDn'), 'XK_Insert':_('Ins'), - 'XK_BackSpace':_(u'\u21d0'), + 'XK_BackSpace':_(u'\u232B'), 'XK_Delete':_('Del'), 'XK_KP_Home':u'(7)', 'XK_KP_Up':u'(8)', From 7c4b8ca8d9dc51011f4f4de80cb1fd96e4470cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denilson=20Figueiredo=20de=20S=C3=A1?= Date: Thu, 2 Jul 2015 12:21:21 -0300 Subject: [PATCH 18/29] =?UTF-8?q?Changing=20from=20"=E2=87=90"=20to=20"?= =?UTF-8?q?=E2=8C=AB"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef35f69..1a043c0 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ I've made significant changes, including: * `-n, --no-hide` do not hide window after timeout, hide only text (make it usable with tile managers, for example, with i3, which can not have unfocusable windows); -* instead of removing text on `backspace` symbol `⇐` will be added; it was +* instead of removing text on `backspace` symbol `⌫` will be added; it was broken anyway, so it will happily erase `Ctrl+...` and other text; * space is changed to visible character: `␣`; * supported second keyboard layout, so not only ASCII letters can be recorded; From c110971b5e3d0b1a1f4dda3a536e9dc36f1392c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denilson=20S=C3=A1?= Date: Thu, 2 Jul 2015 13:08:59 -0300 Subject: [PATCH 19/29] Removing shebang from non-executable file. --- Screenkey/modmap.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Screenkey/modmap.py b/Screenkey/modmap.py index 80b8228..ae7acf5 100644 --- a/Screenkey/modmap.py +++ b/Screenkey/modmap.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (c) 2010 Pablo Seminario # 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 From da283dd9aee5444786c3973f210730cb53af5426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denilson=20S=C3=A1?= Date: Thu, 2 Jul 2015 13:09:22 -0300 Subject: [PATCH 20/29] Converting tab to spaces. --- Screenkey/modmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Screenkey/modmap.py b/Screenkey/modmap.py index ae7acf5..c118b4e 100644 --- a/Screenkey/modmap.py +++ b/Screenkey/modmap.py @@ -45,7 +45,7 @@ def get_keymap_table(): keysym = '0x0' new_keysyms.append(keysym_to_unicode(int(keysym, 16))) - keymap[keycode] = new_keysyms + keymap[keycode] = new_keysyms return keymap From 199fffeadd41f9385b70e1cc8ba69a3873ee9989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denilson=20S=C3=A1?= Date: Thu, 2 Jul 2015 13:10:57 -0300 Subject: [PATCH 21/29] Stripping whitespace at the end of lines. --- Screenkey/modmap.py | 4 ++-- Screenkey/screenkey.py | 54 +++++++++++++++++++++--------------------- screenkey | 12 +++++----- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Screenkey/modmap.py b/Screenkey/modmap.py index c118b4e..efb887f 100644 --- a/Screenkey/modmap.py +++ b/Screenkey/modmap.py @@ -3,12 +3,12 @@ # 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 . diff --git a/Screenkey/screenkey.py b/Screenkey/screenkey.py index 9102275..d2b0883 100644 --- a/Screenkey/screenkey.py +++ b/Screenkey/screenkey.py @@ -3,12 +3,12 @@ # 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 . @@ -57,7 +57,7 @@ class Screenkey(gtk.Window): MODE_NORMAL:_('Normal'), } - STATE_FILE = os.path.join(glib.get_user_cache_dir(), + STATE_FILE = os.path.join(glib.get_user_cache_dir(), 'screenkey.dat') def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): @@ -95,7 +95,7 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): self.pos_x = 0 self.pos_y = 0 - gobject.signal_new("text-changed", gtk.Label, + gobject.signal_new("text-changed", gtk.Label, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()) self.label = gtk.Label() self.label.set_justify(gtk.JUSTIFY_RIGHT) @@ -104,8 +104,8 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): self.label.show() self.add(self.label) - self.screen_width = gtk.gdk.screen_width() - self.screen_height = gtk.gdk.screen_height() + self.screen_width = gtk.gdk.screen_width() + self.screen_height = gtk.gdk.screen_height() self.set_gravity(gtk.gdk.GRAVITY_CENTER) @@ -118,12 +118,12 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): window_width, window_height = self.set_window_size_of_other_win( other_win_pos, self.options['size']) - self.set_xy_position_of_other_win(other_win_pos, self.options['position'], + self.set_xy_position_of_other_win(other_win_pos, self.options['position'], window_width, window_height) self.nosudo = nosudo - self.listenkbd = ListenKbd(self.label, logger=self.logger, + self.listenkbd = ListenKbd(self.label, logger=self.logger, mode=self.options['mode'], nosudo=self.nosudo) self.listenkbd.start() @@ -160,8 +160,8 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): try: import appindicator - self.systray = appindicator.Indicator(APP_NAME, - 'indicator-messages', + self.systray = appindicator.Indicator(APP_NAME, + 'indicator-messages', appindicator.CATEGORY_APPLICATION_STATUS) self.systray.set_status(appindicator.STATUS_ACTIVE) self.systray.set_attention_icon("indicator-messages-new") @@ -173,7 +173,7 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): self.systray = gtk.StatusIcon() self.systray.set_from_icon_name( "preferences-desktop-keyboard-shortcuts") - self.systray.connect("popup-menu", + self.systray.connect("popup-menu", self.on_statusicon_popup, menu) self.logger.debug("Using StatusIcon.") @@ -203,7 +203,7 @@ def load_state(self): except: f.close() except IOError: - self.logger.debug("file %s does not exists." % + self.logger.debug("file %s does not exists." % self.STATE_FILE) return options @@ -269,7 +269,7 @@ def set_xy_position_of_other_win(self, window_pos, setting, if setting == POS_CENTER: self.move(0, self.screen_height / 2) if setting == POS_BOTTOM: - self.move(window_pos['x']+window_pos['width']-window_width, + self.move(window_pos['x']+window_pos['width']-window_width, int(window_pos['y'] + window_pos['height'] - \ window_height*5 - window_pos['height']*0.05)) if setting == POS_KEEP: @@ -291,7 +291,7 @@ def on_statusicon_popup(self, widget, button, timestamp, data=None): if button == 3: if data: data.show() - data.popup(None, None, gtk.status_icon_position_menu, + data.popup(None, None, gtk.status_icon_position_menu, 3, timestamp, widget) def on_label_change(self, widget, data=None): @@ -316,14 +316,14 @@ def on_timeout(self): def on_change_mode(self, mode): self.listenkbd.stop() - self.listenkbd = ListenKbd(self.label, logger=self.logger, + self.listenkbd = ListenKbd(self.label, logger=self.logger, mode=mode, nosudo=self.nosudo) self.listenkbd.start() def on_show_keys(self, widget, data=None): if widget.get_active(): self.logger.debug("Screenkey enabled.") - self.listenkbd = ListenKbd(self.label, logger=self.logger, + self.listenkbd = ListenKbd(self.label, logger=self.logger, mode=self.options['mode'], nosudo=self.nosudo) self.listenkbd.start() @@ -354,8 +354,8 @@ def on_configure(self, _, event): self.label.set_attributes(attr) def on_preferences_dialog(self, widget, data=None): - prefs = gtk.Dialog(APP_NAME, None, - gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + prefs = gtk.Dialog(APP_NAME, None, + gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) def on_sb_time_changed(widget, data=None): @@ -401,11 +401,11 @@ def on_cbox_changed(widget, data=None): sb_time.set_update_policy(gtk.UPDATE_IF_VALID) sb_time.set_value(self.options['timeout']) sb_time.connect("value-changed", on_sb_time_changed) - hbox_time.pack_start(lbl_time1, expand=False, + hbox_time.pack_start(lbl_time1, expand=False, fill=False, padding=6) - hbox_time.pack_start(sb_time, expand=False, + hbox_time.pack_start(sb_time, expand=False, fill=False, padding=4) - hbox_time.pack_start(lbl_time2, expand=False, + hbox_time.pack_start(lbl_time2, expand=False, fill=False, padding=4) frm_time.add(hbox_time) frm_time.show_all() @@ -426,9 +426,9 @@ def on_cbox_changed(widget, data=None): cbox_positions.set_active(self.options['position']) cbox_positions.connect("changed", on_cbox_changed) - hbox1_aspect.pack_start(lbl_positions, expand=False, + hbox1_aspect.pack_start(lbl_positions, expand=False, fill=False, padding=6) - hbox1_aspect.pack_start(cbox_positions, expand=False, + hbox1_aspect.pack_start(cbox_positions, expand=False, fill=False, padding=4) hbox2_aspect = gtk.HBox() @@ -441,9 +441,9 @@ def on_cbox_changed(widget, data=None): cbox_sizes.set_active(self.options['size']) cbox_sizes.connect("changed", on_cbox_sizes_changed) - hbox2_aspect.pack_start(lbl_sizes, expand=False, + hbox2_aspect.pack_start(lbl_sizes, expand=False, fill=False, padding=6) - hbox2_aspect.pack_start(cbox_sizes, expand=False, + hbox2_aspect.pack_start(cbox_sizes, expand=False, fill=False, padding=4) vbox_aspect.pack_start(hbox1_aspect) @@ -462,9 +462,9 @@ def on_cbox_changed(widget, data=None): cbox_modes.insert_text(key, value) cbox_modes.set_active(self.options['mode']) cbox_modes.connect("changed", on_cbox_modes_changed) - hbox_kbd.pack_start(lbl_kbd, expand=False, + hbox_kbd.pack_start(lbl_kbd, expand=False, fill=False, padding=6) - hbox_kbd.pack_start(cbox_modes, expand=False, + hbox_kbd.pack_start(cbox_modes, expand=False, fill=False, padding=4) frm_kbd.add(hbox_kbd) diff --git a/screenkey b/screenkey index 8d3e2de..d27ce89 100755 --- a/screenkey +++ b/screenkey @@ -4,12 +4,12 @@ # 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 . @@ -33,10 +33,10 @@ gtk.gdk.threads_init() def Main(): parser = OptionParser(description=APP_DESC, version=VERSION) - parser.add_option("--no-detach", action="store_true", - dest="nodetach", default=False, + parser.add_option("--no-detach", action="store_true", + dest="nodetach", default=False, help=_("do not detach from the parent")) - parser.add_option("-d", "--debug", action="store_true", + parser.add_option("-d", "--debug", action="store_true", dest="debug", default=False, help=_("show debug information")) parser.add_option("-n", "--no-hide", action="store_true", dest="nohide", default=False, help=_("do not hide")) @@ -56,7 +56,7 @@ def Main(): # Send debug messages to standard output logfile = None else: - logfile = os.path.join(os.path.expanduser('~'), + logfile = os.path.join(os.path.expanduser('~'), '.screenkey.log') username = os.environ.get('SUDO_USER') if username: From 5d371c3cb8738d5c4b117e4f2fc7f482509bb065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denilson=20S=C3=A1?= Date: Thu, 2 Jul 2015 13:28:00 -0300 Subject: [PATCH 22/29] Using "is not None" instead of "not foo is None". https://www.python.org/dev/peps/pep-0008/#programming-recommendations --- Screenkey/listenkbd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 2401553..0d3951f 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -144,7 +144,7 @@ def replace_xk_key(self, key, keysym): def update_text(self, string=None): gtk.gdk.threads_enter() - if not string is None: + if string is not None: # TODO: make this configurable if string.strip() == 'Ctrl+F1': if self._disabled: From 108d720ae58a0a97031b3aff45baa7fbe4fd8aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denilson=20S=C3=A1?= Date: Thu, 2 Jul 2015 13:35:08 -0300 Subject: [PATCH 23/29] Fixing Unicode errors. First error happens upon launch. `unichr()` can't accept values for multimedia keys, as they are outside the Unicode range. Second error happens when trying to set a label to a string containing null characters. --- Screenkey/listenkbd.py | 6 +++--- Screenkey/modmap.py | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 0d3951f..758ea95 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -134,9 +134,9 @@ def lookup_keysym(self, keysym): return "" def replace_xk_key(self, key, keysym): - if key == '\x00': - return '' print(key, keysym) + if key == u'\x00' or key == '\x00': + return '' for name in dir(XK): if name[:3] == "XK_" and getattr(XK, name) == keysym: if name in REPLACE_KEYS: @@ -275,7 +275,7 @@ def key_normal_mode(self, event): key = u'\u2623' string = self.replace_xk_key(key, keysym) - if string: + if string is not None: key = string if mod != '': diff --git a/Screenkey/modmap.py b/Screenkey/modmap.py index efb887f..8d14cbd 100644 --- a/Screenkey/modmap.py +++ b/Screenkey/modmap.py @@ -72,7 +72,10 @@ def keysym_to_unicode(keysym): if keysym in mapping: return unichr(mapping[keysym]) else: - return unichr(keysym) + try: + return unichr(keysym) + except ValueError: + return u'\x00' mapping = { 0x01a1: 0x0104, From 7aff2028d1e5741b72d48000cbca9f8cad7568c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denilson=20S=C3=A1?= Date: Thu, 2 Jul 2015 14:04:51 -0300 Subject: [PATCH 24/29] Fixing most/all PEP-8 warnings. Mostly whitespace. Also moved GTK/GDK threading initialization to inside main(). --- Screenkey/__init__.py | 2 - Screenkey/listenkbd.py | 173 +++++++++++++++++++++-------------------- Screenkey/modmap.py | 10 ++- Screenkey/screenkey.py | 69 ++++++++-------- screenkey | 38 +++++---- 5 files changed, 152 insertions(+), 140 deletions(-) diff --git a/Screenkey/__init__.py b/Screenkey/__init__.py index c0c13e3..7097836 100644 --- a/Screenkey/__init__.py +++ b/Screenkey/__init__.py @@ -1,7 +1,5 @@ - APP_NAME = "Screenkey" APP_DESC = _("Screencast your keys") APP_URL = 'http://launchpad.net/screenkey' VERSION = '0.2' AUTHOR = 'Pablo Seminario' - diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 758ea95..1209359 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -26,53 +26,54 @@ MODE_NORMAL = 1 REPLACE_KEYS = { - 'XK_Escape':_('Esc'), - 'XK_Tab':u'\u21B9', - 'XK_Return':u'\u23CE', - 'XK_Space':u'', - 'XK_Caps_Lock':_('Caps'), - 'XK_F1':u'F1', - 'XK_F2':u'F2', - 'XK_F3':u'F3', - 'XK_F4':u'F4', - 'XK_F5':u'F5', - 'XK_F6':u'F6', - 'XK_F7':u'F7', - 'XK_F8':u'F8', - 'XK_F9':u'F9', - 'XK_F10':u'F10', - 'XK_F11':u'F11', - 'XK_F12':u'F12', - 'XK_Home':_('Home'), - 'XK_Up':u'\u2191', - 'XK_Page_Up':_('PgUp'), - 'XK_Left':u'\u2190', - 'XK_Right':u'\u2192', - 'XK_End':_('End'), - 'XK_Down':u'\u2193', - 'XK_Next':_('PgDn'), - 'XK_Insert':_('Ins'), - 'XK_BackSpace':_(u'\u21d0'), - 'XK_Delete':_('Del'), - 'XK_KP_Home':u'(7)', - 'XK_KP_Up':u'(8)', - 'XK_KP_Prior':u'(9)', - 'XK_KP_Left':u'(4)', - 'XK_KP_Right':u'(6)', - 'XK_KP_End':u'(1)', - 'XK_KP_Down':u'(2)', - 'XK_KP_Page_Down':u'(3)', - 'XK_KP_Begin':u'(5)', - 'XK_KP_Insert':u'(0)', - 'XK_KP_Delete':u'(.)', - 'XK_KP_Add':u'(+)', - 'XK_KP_Subtract':u'(-)', - 'XK_KP_Multiply':u'(*)', - 'XK_KP_Divide':u'(/)', - 'XK_Num_Lock':u'NumLock', - 'XK_KP_Enter':u'\u23CE', + 'XK_Escape': _('Esc'), + 'XK_Tab': u'\u21B9', + 'XK_Return': u'\u23CE', + 'XK_Space': u'', + 'XK_Caps_Lock': _('Caps'), + 'XK_F1': u'F1', + 'XK_F2': u'F2', + 'XK_F3': u'F3', + 'XK_F4': u'F4', + 'XK_F5': u'F5', + 'XK_F6': u'F6', + 'XK_F7': u'F7', + 'XK_F8': u'F8', + 'XK_F9': u'F9', + 'XK_F10': u'F10', + 'XK_F11': u'F11', + 'XK_F12': u'F12', + 'XK_Home': _('Home'), + 'XK_Up': u'\u2191', + 'XK_Page_Up': _('PgUp'), + 'XK_Left': u'\u2190', + 'XK_Right': u'\u2192', + 'XK_End': _('End'), + 'XK_Down': u'\u2193', + 'XK_Next': _('PgDn'), + 'XK_Insert': _('Ins'), + 'XK_BackSpace': _(u'\u21d0'), + 'XK_Delete': _('Del'), + 'XK_KP_Home': u'(7)', + 'XK_KP_Up': u'(8)', + 'XK_KP_Prior': u'(9)', + 'XK_KP_Left': u'(4)', + 'XK_KP_Right': u'(6)', + 'XK_KP_End': u'(1)', + 'XK_KP_Down': u'(2)', + 'XK_KP_Page_Down': u'(3)', + 'XK_KP_Begin': u'(5)', + 'XK_KP_Insert': u'(0)', + 'XK_KP_Delete': u'(.)', + 'XK_KP_Add': u'(+)', + 'XK_KP_Subtract': u'(-)', + 'XK_KP_Multiply': u'(*)', + 'XK_KP_Divide': u'(/)', + 'XK_Num_Lock': u'NumLock', + 'XK_KP_Enter': u'\u23CE', } + class ListenKbd(threading.Thread): # Add in a shortcut to disable _disabled = False @@ -93,7 +94,7 @@ def __init__(self, label, logger, mode, nosudo): 'alt': False, 'capslock': False, 'meta': False, - 'super':False + 'super': False } self.logger.debug("Thread created") @@ -109,19 +110,19 @@ def __init__(self, label, logger, mode, nosudo): sys.exit(1) self.ctx = self.record_dpy.record_create_context( - 0, - [record.AllClients], - [{ - 'core_requests': (0, 0), - 'core_replies': (0, 0), - 'ext_requests': (0, 0, 0, 0), - 'ext_replies': (0, 0, 0, 0), - 'delivered_events': (0, 0), - 'device_events': (X.KeyPress, X.KeyRelease), - 'errors': (0, 0), - 'client_started': False, - 'client_died': False, - }]) + 0, + [record.AllClients], + [{ + 'core_requests': (0, 0), + 'core_replies': (0, 0), + 'ext_requests': (0, 0, 0, 0), + 'ext_replies': (0, 0, 0, 0), + 'delivered_events': (0, 0), + 'device_events': (X.KeyPress, X.KeyRelease), + 'errors': (0, 0), + 'client_started': False, + 'client_died': False, + }]) def run(self): self.logger.debug("Thread started.") @@ -148,10 +149,10 @@ def update_text(self, string=None): # TODO: make this configurable if string.strip() == 'Ctrl+F1': if self._disabled: - self._disabled=False + self._disabled = False self.text = "[ENABLED]" else: - self._disabled=True + self._disabled = True self.text = "[DISABLED]" else: self.text = "%s%s" % (self.label.get_text(), string) @@ -171,8 +172,9 @@ def key_press(self, reply): # This is not the most efficient way to detect the # use of sudo/gksudo but it works. if not self.nosudo: - sudo_is_running = subprocess.call(['ps', '-C', 'sudo'], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + sudo_is_running = subprocess.call( + ['ps', '-C', 'sudo'], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if not sudo_is_running: return @@ -189,15 +191,15 @@ def key_press(self, reply): data = reply.data key = [] while len(data): - event, data = rq.EventField(None).parse_binary_value(data, - self.record_dpy.display, None, None) + event, data = rq.EventField(None).parse_binary_value( + data, self.record_dpy.display, None, None) if event.type in [X.KeyPress, X.KeyRelease]: if self.mode == MODE_NORMAL: key.append(self.key_normal_mode(event)) if self.mode == MODE_RAW: key.append(self.key_raw_mode(event)) if any(key): - self.update_text(''.join(k for k in key if k)) + self.update_text(''.join(k for k in key if k)) def key_normal_mode(self, event): key = '' @@ -205,33 +207,32 @@ def key_normal_mode(self, event): keysym = self.local_dpy.keycode_to_keysym(event.detail, 0) if event.detail in self.keymap: - key_normal_1, key_shift_1, \ - key_normal_2, key_shift_2, \ - key_dead, key_deadshift = self.keymap[event.detail] + (key_normal_1, key_shift_1, + key_normal_2, key_shift_2, + key_dead, key_deadshift) = self.keymap[event.detail] if event.state & 1 << 13 != 0: key_normal = key_normal_2 key_shift = key_shift_2 else: key_normal = key_normal_1 key_shift = key_shift_1 - self.logger.debug("Key %s(keycode) %s. Symbols %s" % - (event.detail, - event.type == X.KeyPress and "pressed" or "released", - self.keymap[event.detail]) - ) + self.logger.debug("Key %s(keycode) %s. Symbols %s" % ( + event.detail, + event.type == X.KeyPress and "pressed" or "released", + self.keymap[event.detail])) else: self.logger.debug('No mapping for scan_code %d' % event.detail) return masks = { - 1<<0: 'shift', - 1<<1: 'lock', - 1<<2: 'ctrl', - 1<<3: 'alt', - 1<<4: 'mod2', - 1<<5: 'mod3', - 1<<6: 'super', - 1<<7: 'meta' + 1 << 0: 'shift', + 1 << 1: 'lock', + 1 << 2: 'ctrl', + 1 << 3: 'alt', + 1 << 4: 'mod2', + 1 << 5: 'mod3', + 1 << 6: 'super', + 1 << 7: 'meta' } for k in masks: if k & event.state: @@ -251,11 +252,12 @@ def key_normal_mode(self, event): mod = mod + _("Super+") if self.cmd_keys['shift']: - if key_shift == key_normal or key_normal == u'\x00' or key_shift == u'\x00': + if (key_shift == key_normal or key_normal == u'\x00' + or key_shift == u'\x00'): mod = mod + _("Shift+") key = key_shift - if self.cmd_keys['capslock'] \ - and ord(key_normal) in range(97,123): + if (self.cmd_keys['capslock'] and + ord(key_normal) in range(97, 123)): key = key_shift if self.cmd_keys['meta']: key = key_dead @@ -314,4 +316,3 @@ def stop(self): self.local_dpy.flush() self.record_dpy.record_free_context(self.ctx) self.logger.debug("Thread stopped.") - diff --git a/Screenkey/modmap.py b/Screenkey/modmap.py index 8d14cbd..9940082 100644 --- a/Screenkey/modmap.py +++ b/Screenkey/modmap.py @@ -16,13 +16,16 @@ import re import subprocess + def cmd_keymap_table(): return subprocess.Popen( - ['xmodmap','-pk'], stdout=subprocess.PIPE).communicate()[0] + ['xmodmap', '-pk'], stdout=subprocess.PIPE).communicate()[0] + def cmd_modifier_map(): return subprocess.Popen( - ['xmodmap','-pm'], stdout=subprocess.PIPE).communicate()[0] + ['xmodmap', '-pm'], stdout=subprocess.PIPE).communicate()[0] + def get_keymap_table(): keymap = {} @@ -49,6 +52,7 @@ def get_keymap_table(): return keymap + def get_modifier_map(): modifiers = [] @@ -68,6 +72,7 @@ def get_modifier_map(): return modifiers + def keysym_to_unicode(keysym): if keysym in mapping: return unichr(mapping[keysym]) @@ -77,6 +82,7 @@ def keysym_to_unicode(keysym): except ValueError: return u'\x00' + mapping = { 0x01a1: 0x0104, 0x01a2: 0x02d8, diff --git a/Screenkey/screenkey.py b/Screenkey/screenkey.py index d2b0883..b927d4a 100644 --- a/Screenkey/screenkey.py +++ b/Screenkey/screenkey.py @@ -39,22 +39,23 @@ MODE_RAW = 0 MODE_NORMAL = 1 + class Screenkey(gtk.Window): POSITIONS = { - POS_TOP:_('Top'), - POS_CENTER:_('Center'), - POS_BOTTOM:_('Bottom'), - POS_KEEP:_('Keep'), + POS_TOP: _('Top'), + POS_CENTER: _('Center'), + POS_BOTTOM: _('Bottom'), + POS_KEEP: _('Keep'), } SIZES = { - SIZE_LARGE:_('Large'), - SIZE_MEDIUM:_('Medium'), - SIZE_SMALL:_('Small'), + SIZE_LARGE: _('Large'), + SIZE_MEDIUM: _('Medium'), + SIZE_SMALL: _('Small'), } MODES = { - MODE_RAW:_('Raw'), - MODE_NORMAL:_('Normal'), + MODE_RAW: _('Raw'), + MODE_NORMAL: _('Normal'), } STATE_FILE = os.path.join(glib.get_user_cache_dir(), @@ -96,7 +97,7 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): self.pos_y = 0 gobject.signal_new("text-changed", gtk.Label, - gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()) + gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()) self.label = gtk.Label() self.label.set_justify(gtk.JUSTIFY_RIGHT) self.label.set_ellipsize(pango.ELLIPSIZE_START) @@ -116,10 +117,11 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): other_win_pos = self.get_window_pos(window_id) window_width, window_height = self.set_window_size_of_other_win( - other_win_pos, self.options['size']) + other_win_pos, self.options['size']) - self.set_xy_position_of_other_win(other_win_pos, self.options['position'], - window_width, window_height) + self.set_xy_position_of_other_win( + other_win_pos, self.options['position'], + window_width, window_height) self.nosudo = nosudo @@ -128,7 +130,6 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): nosudo=self.nosudo) self.listenkbd.start() - menu = gtk.Menu() show_item = gtk.CheckMenuItem(_("Show keys")) @@ -142,7 +143,6 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): preferences_item.show() menu.append(preferences_item) - about_item = gtk.ImageMenuItem(gtk.STOCK_ABOUT) about_item.connect("activate", self.on_about_dialog) about_item.show() @@ -160,24 +160,23 @@ def __init__(self, logger, nodetach, nohide, bg, fg, nosudo, window_id): try: import appindicator - self.systray = appindicator.Indicator(APP_NAME, - 'indicator-messages', - appindicator.CATEGORY_APPLICATION_STATUS) + self.systray = appindicator.Indicator( + APP_NAME, + 'indicator-messages', + appindicator.CATEGORY_APPLICATION_STATUS) self.systray.set_status(appindicator.STATUS_ACTIVE) self.systray.set_attention_icon("indicator-messages-new") - self.systray.set_icon( - "preferences-desktop-keyboard-shortcuts") + self.systray.set_icon("preferences-desktop-keyboard-shortcuts") self.systray.set_menu(menu) self.logger.debug("Using AppIndicator.") - except(ImportError): + except ImportError: self.systray = gtk.StatusIcon() self.systray.set_from_icon_name( - "preferences-desktop-keyboard-shortcuts") + "preferences-desktop-keyboard-shortcuts") self.systray.connect("popup-menu", - self.on_statusicon_popup, menu) + self.on_statusicon_popup, menu) self.logger.debug("Using StatusIcon.") - self.connect("destroy-event", self.quit) self.connect("delete-event", self.quit) self.connect("configure-event", self.on_configure) @@ -252,7 +251,7 @@ def get_window_pos(self, win_id): """ get window position x,y and size width, height" """ p = Popen(["xwininfo", "-id", win_id], stdout=PIPE) out = p.communicate()[0] - #if p.returncode != 0: + # if p.returncode != 0: x = int(re.search("Absolute upper-left X:.*?(\d+)", out).groups()[0]) y = int(re.search("Absolute upper-left Y:.*?(\d+)", out).groups()[0]) width = int(re.search("Width:.*?(\d+)", out).groups()[0]) @@ -261,7 +260,7 @@ def get_window_pos(self, win_id): return {'x': x, 'y': y, 'width': width, 'height': height} def set_xy_position_of_other_win(self, window_pos, setting, - window_width, window_height): + window_width, window_height): """Set window position.""" if setting == POS_TOP: @@ -270,8 +269,8 @@ def set_xy_position_of_other_win(self, window_pos, setting, self.move(0, self.screen_height / 2) if setting == POS_BOTTOM: self.move(window_pos['x']+window_pos['width']-window_width, - int(window_pos['y'] + window_pos['height'] - \ - window_height*5 - window_pos['height']*0.05)) + int(window_pos['y'] + window_pos['height'] - + window_height*5 - window_pos['height']*0.05)) if setting == POS_KEEP: self.move(self.pos_x, self.pos_y) @@ -340,7 +339,8 @@ def on_configure(self, _, event): attr.change(pango.AttrWeight(pango.WEIGHT_BOLD, 0, -1)) fgcolor = gtk.gdk.color_parse(self.fg) - attr.change(pango.AttrForeground(fgcolor.red, fgcolor.green, fgcolor.blue, 0, -1)) + attr.change(pango.AttrForeground( + fgcolor.red, fgcolor.green, fgcolor.blue, 0, -1)) self.pos_x = event.x self.pos_y = event.y @@ -355,8 +355,8 @@ def on_configure(self, _, event): def on_preferences_dialog(self, widget, data=None): prefs = gtk.Dialog(APP_NAME, None, - gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) + gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) def on_sb_time_changed(widget, data=None): self.options['timeout'] = widget.get_value() @@ -491,13 +491,11 @@ def on_about_dialog(self, widget, data=None): about.set_copyright(u"2010 \u00a9 %s" % AUTHOR) about.set_comments(APP_DESC) about.set_documenters( - [u"Jos\xe9 Mar\xeda Quiroga "] + [u"Jos\xe9 Mar\xeda Quiroga "] ) about.set_website(APP_URL) about.set_icon_name('preferences-desktop-keyboard-shortcuts') - about.set_logo_icon_name( - 'preferences-desktop-keyboard-shortcuts' - ) + about.set_logo_icon_name('preferences-desktop-keyboard-shortcuts') about.run() about.destroy() @@ -508,4 +506,3 @@ def drop_tty(self): os._exit(0) os.setsid() - diff --git a/screenkey b/screenkey index d27ce89..4e8945e 100755 --- a/screenkey +++ b/screenkey @@ -29,25 +29,32 @@ gettext.install('screenkey', unicode=True) from Screenkey import APP_NAME, APP_DESC, VERSION from Screenkey.screenkey import Screenkey -gtk.gdk.threads_init() -def Main(): +def main(): parser = OptionParser(description=APP_DESC, version=VERSION) - parser.add_option("--no-detach", action="store_true", - dest="nodetach", default=False, + parser.add_option( + "--no-detach", action="store_true", + dest="nodetach", default=True, help=_("do not detach from the parent")) - parser.add_option("-d", "--debug", action="store_true", + parser.add_option( + "-d", "--debug", action="store_true", dest="debug", default=False, help=_("show debug information")) - parser.add_option("-n", "--no-hide", action="store_true", + parser.add_option( + "-n", "--no-hide", action="store_true", dest="nohide", default=False, help=_("do not hide")) - parser.add_option("--bg", action="store", - dest="bg", default="black", help=_("background color (in #RRGGBB format)")) - parser.add_option("--fg", action="store", + parser.add_option( + "--bg", action="store", + dest="bg", default="black", + help=_("background color (in #RRGGBB format)")) + parser.add_option( + "--fg", action="store", dest="fg", default="white", help=_("foreground color")) - parser.add_option("--no-sudo", action="store_true", + parser.add_option( + "--no-sudo", action="store_true", dest="nosudo", default="white", help=_("do not perform very expensive sudo running check")) - parser.add_option("-w", "--window", + parser.add_option( + "-w", "--window", dest="window_id", default=0, help=_("align to other window")) (options, args) = parser.parse_args() @@ -68,7 +75,11 @@ def Main(): logging.basicConfig(level=logging.INFO) logger = logging.getLogger(APP_NAME) - s = Screenkey(logger=logger, nodetach=options.nodetach, + gtk.gdk.threads_init() + + s = Screenkey( + logger=logger, + nodetach=options.nodetach, nohide=options.nohide, bg=options.bg, fg=options.fg, nosudo=options.nosudo, @@ -81,5 +92,4 @@ def Main(): os.system('kill %d' % (os.getpid())) if __name__ == "__main__": - Main() - + main() From 3a50f16e43fde0d127c6f22488d4d44de0ba7b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denilson=20S=C3=A1?= Date: Thu, 2 Jul 2015 14:13:20 -0300 Subject: [PATCH 25/29] Fixing "nosudo" code. --- Screenkey/listenkbd.py | 5 ++--- Screenkey/screenkey.py | 3 +-- screenkey | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 1209359..89b0973 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -167,15 +167,14 @@ def update_text(self, string=None): self.label.emit("text-changed") def key_press(self, reply): - # FIXME: # This is not the most efficient way to detect the # use of sudo/gksudo but it works. if not self.nosudo: - sudo_is_running = subprocess.call( + sudo_is_running = 0 == subprocess.call( ['ps', '-C', 'sudo'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if not sudo_is_running: + if sudo_is_running: return if reply.category != record.FromServer: diff --git a/Screenkey/screenkey.py b/Screenkey/screenkey.py index b927d4a..553d8a7 100644 --- a/Screenkey/screenkey.py +++ b/Screenkey/screenkey.py @@ -500,8 +500,7 @@ def on_about_dialog(self, widget, data=None): about.destroy() def drop_tty(self): - # We fork and setsid so that we drop the controlling - # tty. + # We fork and setsid so that we drop the controlling tty. if os.fork() != 0: os._exit(0) diff --git a/screenkey b/screenkey index 4e8945e..9be1311 100755 --- a/screenkey +++ b/screenkey @@ -51,7 +51,7 @@ def main(): dest="fg", default="white", help=_("foreground color")) parser.add_option( "--no-sudo", action="store_true", - dest="nosudo", default="white", + dest="nosudo", default=True, help=_("do not perform very expensive sudo running check")) parser.add_option( "-w", "--window", From a69a9dd40b2850738920edd96f4cf72ef0c88d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denilson=20S=C3=A1?= Date: Fri, 3 Jul 2015 18:23:28 -0300 Subject: [PATCH 26/29] Reverting --no-detach default value. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I accidentally switched the default in the previous commit(s). I'm reverting it. However, I'm somewhat inclined to remove this option… But not now. --- screenkey | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screenkey b/screenkey index 9be1311..f0c84a1 100755 --- a/screenkey +++ b/screenkey @@ -34,7 +34,7 @@ def main(): parser = OptionParser(description=APP_DESC, version=VERSION) parser.add_option( "--no-detach", action="store_true", - dest="nodetach", default=True, + dest="nodetach", default=False, help=_("do not detach from the parent")) parser.add_option( "-d", "--debug", action="store_true", From afdbe0d7018996918145d233af56c2aebfc26fd2 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Mon, 6 Jul 2015 15:07:09 +0600 Subject: [PATCH 27/29] change backspace symbol --- Screenkey/listenkbd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 5207646..6e47a5b 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -265,7 +265,7 @@ def key_normal_mode(self, event): if event.detail == 23: self.detached = True if event.detail == 22: - key = u'\u21D0' + key = u'\u232B' if event.detail == 65: key = u'\u2423' if event.detail == 66: From c0e93b5ef7f119403007cc4af24a5ef02f11b716 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Wed, 19 Aug 2015 15:25:33 +0600 Subject: [PATCH 28/29] keylogger mode --- Screenkey/listenkbd.py | 131 ++++++++++++++++++++++++++++++----------- screenkey | 36 +++++++---- 2 files changed, 119 insertions(+), 48 deletions(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 6e47a5b..48e1128 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -17,6 +17,7 @@ import subprocess import modmap import gtk +import time from Xlib import X, XK, display from Xlib.ext import record @@ -26,11 +27,11 @@ MODE_NORMAL = 1 REPLACE_KEYS = { - 'XK_Escape': _('Esc'), - 'XK_Tab': u'\u21B9', - 'XK_Return': u'\u23CE', - 'XK_Space': u'', - 'XK_Caps_Lock': _('Caps'), + 'XK_ESCAPE': _('Esc'), + 'XK_TAB': u'\u21B9', + 'XK_RETURN': u'\u23CE', + 'XK_SPACE': u'', + 'XK_CAPS_LOCK': _('Caps'), 'XK_F1': u'F1', 'XK_F2': u'F2', 'XK_F3': u'F3', @@ -43,34 +44,34 @@ 'XK_F10': u'F10', 'XK_F11': u'F11', 'XK_F12': u'F12', - 'XK_Home': _('Home'), - 'XK_Up': u'\u2191', - 'XK_Page_Up': _('PgUp'), - 'XK_Left': u'\u2190', - 'XK_Right': u'\u2192', - 'XK_End': _('End'), - 'XK_Down': u'\u2193', - 'XK_Next': _('PgDn'), - 'XK_Insert': _('Ins'), - 'XK_BackSpace': _(u'\u232B'), - 'XK_Delete': _('Del'), - 'XK_KP_Home': u'(7)', - 'XK_KP_Up': u'(8)', - 'XK_KP_Prior': u'(9)', - 'XK_KP_Left': u'(4)', - 'XK_KP_Right': u'(6)', - 'XK_KP_End': u'(1)', - 'XK_KP_Down': u'(2)', - 'XK_KP_Page_Down': u'(3)', - 'XK_KP_Begin': u'(5)', - 'XK_KP_Insert': u'(0)', - 'XK_KP_Delete': u'(.)', - 'XK_KP_Add': u'(+)', - 'XK_KP_Subtract': u'(-)', - 'XK_KP_Multiply': u'(*)', - 'XK_KP_Divide': u'(/)', - 'XK_Num_Lock': u'NumLock', - 'XK_KP_Enter': u'\u23CE', + 'XK_HOME': _('Home'), + 'XK_UP': u'\u2191', + 'XK_PAGE_UP': _('PgUp'), + 'XK_LEFT': u'\u2190', + 'XK_RIGHT': u'\u2192', + 'XK_END': _('End'), + 'XK_DOWN': u'\u2193', + 'XK_NEXT': _('PgDn'), + 'XK_INSERT': _('Ins'), + 'XK_BACKSPACE': _(u'\u232B'), + 'XK_DELETE': _('Del'), + 'XK_KP_HOME': u'(7)', + 'XK_KP_UP': u'(8)', + 'XK_KP_PRIOR': u'(9)', + 'XK_KP_LEFT': u'(4)', + 'XK_KP_RIGHT': u'(6)', + 'XK_KP_END': u'(1)', + 'XK_KP_DOWN': u'(2)', + 'XK_KP_PAGE_DOWN': u'(3)', + 'XK_KP_BEGIN': u'(5)', + 'XK_KP_INSERT': u'(0)', + 'XK_KP_DELETE': u'(.)', + 'XK_KP_ADD': u'(+)', + 'XK_KP_SUBTRACT': u'(-)', + 'XK_KP_MULTIPLY': u'(*)', + 'XK_KP_DIVIDE': u'(/)', + 'XK_NUM_LOCK': u'NumLock', + 'XK_KP_ENTER': u'\u23CE', } @@ -135,12 +136,11 @@ def lookup_keysym(self, keysym): return "" def replace_xk_key(self, key, keysym): - print(key, keysym) if key == u'\x00' or key == '\x00': return '' for name in dir(XK): if name[:3] == "XK_" and getattr(XK, name) == keysym: - if name in REPLACE_KEYS: + if name.upper() in REPLACE_KEYS: return REPLACE_KEYS[name] def update_text(self, string=None): @@ -276,6 +276,7 @@ def key_normal_mode(self, event): key = u'\u2623' string = self.replace_xk_key(key, keysym) + if string is not None: key = string @@ -315,3 +316,63 @@ def stop(self): self.local_dpy.flush() self.record_dpy.record_free_context(self.ctx) self.logger.debug("Thread stopped.") + + +class ListenKbd_Logger(ListenKbd): + def __init__(self, logger, mode, nosudo): + self.mode = mode + self.logger = logger + self.text = "" + self.command = None + self.shift = None + self.detached = False + self.nosudo = nosudo + self.cmd_keys = { + 'shift': False, + 'ctrl': False, + 'alt': False, + 'capslock': False, + 'meta': False, + 'super': False + } + + + self.keymap = modmap.get_keymap_table() + self.modifiers = modmap.get_modifier_map() + + self.local_dpy = display.Display() + self.record_dpy = display.Display() + + if not self.record_dpy.has_extension("RECORD"): + self.logger.error("RECORD extension not found.") + print "RECORD extension not found" + sys.exit(1) + + self.ctx = self.record_dpy.record_create_context( + 0, + [record.AllClients], + [{ + 'core_requests': (0, 0), + 'core_replies': (0, 0), + 'ext_requests': (0, 0, 0, 0), + 'ext_replies': (0, 0, 0, 0), + 'delivered_events': (0, 0), + 'device_events': (X.KeyPress, X.KeyRelease), + 'errors': (0, 0), + 'client_started': False, + 'client_died': False, + }]) + + def start(self): + self.record_dpy.record_enable_context(self.ctx, self.key_press) + + def update_text(self, string): + print("{:<013}\t{}".format(time.time(), string.strip())) + + def replace_xk_key(self, key, keysym): + if key == u'\x00' or key == '\x00': + return '' + for name in dir(XK): + if name[:3] == "XK_" and getattr(XK, name) == keysym: + if name.upper() in REPLACE_KEYS: + return name[3:] diff --git a/screenkey b/screenkey index f0c84a1..912a894 100755 --- a/screenkey +++ b/screenkey @@ -28,6 +28,7 @@ gettext.install('screenkey', unicode=True) from Screenkey import APP_NAME, APP_DESC, VERSION from Screenkey.screenkey import Screenkey +from Screenkey.listenkbd import ListenKbd_Logger, MODE_NORMAL def main(): @@ -56,6 +57,9 @@ def main(): parser.add_option( "-w", "--window", dest="window_id", default=0, help=_("align to other window")) + parser.add_option( + "--key-logger", action="store_true", + default=False, help=_("keylogger mode: windowless, print to stdout")) (options, args) = parser.parse_args() if options.debug: @@ -77,19 +81,25 @@ def main(): gtk.gdk.threads_init() - s = Screenkey( - logger=logger, - nodetach=options.nodetach, - nohide=options.nohide, - bg=options.bg, fg=options.fg, - nosudo=options.nosudo, - window_id=options.window_id) - try: - gtk.main() - finally: - # listenkbd thread do not terminate due to bug in Xlib implementation - if threading.active_count() > 1: - os.system('kill %d' % (os.getpid())) + if options.key_logger: + listenkbd = ListenKbd_Logger(logger=logger, + mode=MODE_NORMAL, + nosudo=options.nosudo) + listenkbd.start() + else: + Screenkey( + logger=logger, + nodetach=options.nodetach, + nohide=options.nohide, + bg=options.bg, fg=options.fg, + nosudo=options.nosudo, + window_id=options.window_id) + try: + gtk.main() + finally: + # listenkbd thread do not terminate due to bug in Xlib implementation + if threading.active_count() > 1: + os.system('kill %d' % (os.getpid())) if __name__ == "__main__": main() From 420fcb8f3494b1cb78e9514d4af45c1416ff38e3 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Wed, 19 Aug 2015 16:43:51 +0600 Subject: [PATCH 29/29] better time format, keypresses/releases --- Screenkey/listenkbd.py | 147 +++++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 66 deletions(-) diff --git a/Screenkey/listenkbd.py b/Screenkey/listenkbd.py index 48e1128..a9bf7a7 100644 --- a/Screenkey/listenkbd.py +++ b/Screenkey/listenkbd.py @@ -17,7 +17,7 @@ import subprocess import modmap import gtk -import time +import datetime from Xlib import X, XK, display from Xlib.ext import record @@ -143,7 +143,9 @@ def replace_xk_key(self, key, keysym): if name.upper() in REPLACE_KEYS: return REPLACE_KEYS[name] - def update_text(self, string=None): + def update_text(self, string=None, event=None): + if event.type == X.KeyRelease: + return gtk.gdk.threads_enter() if string is not None: # TODO: make this configurable @@ -194,11 +196,11 @@ def key_press(self, reply): data, self.record_dpy.display, None, None) if event.type in [X.KeyPress, X.KeyRelease]: if self.mode == MODE_NORMAL: - key.append(self.key_normal_mode(event)) + key = self.key_normal_mode(event) if self.mode == MODE_RAW: - key.append(self.key_raw_mode(event)) - if any(key): - self.update_text(''.join(k for k in key if k)) + key = self.key_raw_mode(event) + if key: + self.update_text(key, event) def key_normal_mode(self, event): key = '' @@ -241,64 +243,61 @@ def key_normal_mode(self, event): if event.detail in self.modifiers: return else: - if event.type == X.KeyPress: - key = key_normal - if self.cmd_keys['ctrl']: - mod = mod + _("Ctrl+") - if self.cmd_keys['alt']: - mod = mod + _("Alt+") - if self.cmd_keys['super']: - mod = mod + _("Super+") - - if self.cmd_keys['shift']: - if (key_shift == key_normal or key_normal == u'\x00' - or key_shift == u'\x00'): - mod = mod + _("Shift+") - key = key_shift - if (self.cmd_keys['capslock'] and - ord(key_normal) in range(97, 123)): - key = key_shift - if self.cmd_keys['meta']: - key = key_dead - if self.cmd_keys['shift'] and self.cmd_keys['meta']: - key = key_deadshift - if event.detail == 23: - self.detached = True - if event.detail == 22: - key = u'\u232B' - if event.detail == 65: - key = u'\u2423' - if event.detail == 66: - self.detached = True - key = u'\u2328' - if event.detail == 108: - self.detached = True - key = u'\u2623' - - string = self.replace_xk_key(key, keysym) - - if string is not None: - key = string - - if mod != '': - key = mod + key - - detached = self.detached - self.detached = False - if len(key) > 1: - self.detached = True - if event.detail == 66: - self.detached = True - if event.detail == 23: - self.detached = True - - if detached or len(key) > 1: - key = " " + key - - if event.detail == 65: - key += u'\u200A' - else: - return + key = key_normal + if self.cmd_keys['ctrl']: + mod = mod + _("Ctrl+") + if self.cmd_keys['alt']: + mod = mod + _("Alt+") + if self.cmd_keys['super']: + mod = mod + _("Super+") + + if self.cmd_keys['shift']: + if (key_shift == key_normal or key_normal == u'\x00' + or key_shift == u'\x00'): + mod = mod + _("Shift+") + key = key_shift + if (self.cmd_keys['capslock'] and + ord(key_normal) in range(97, 123)): + key = key_shift + if self.cmd_keys['meta']: + key = key_dead + if self.cmd_keys['shift'] and self.cmd_keys['meta']: + key = key_deadshift + if event.detail == 23: + self.detached = True + if event.detail == 22: + key = u'\u232B' + if event.detail == 65: + key = u'\u2423' + if event.detail == 66: + self.detached = True + key = u'\u2328' + if event.detail == 108: + self.detached = True + key = u'\u2623' + + string = self.replace_xk_key(key, keysym) + + if string is not None: + key = string + + if mod != '': + key = mod + key + + detached = self.detached + self.detached = False + if len(key) > 1: + self.detached = True + if event.detail == 66: + self.detached = True + if event.detail == 23: + self.detached = True + + if detached or len(key) > 1: + key = " " + key + + if event.detail == 65: + key += u'\u200A' return key @@ -366,10 +365,26 @@ def __init__(self, logger, mode, nosudo): def start(self): self.record_dpy.record_enable_context(self.ctx, self.key_press) - def update_text(self, string): - print("{:<013}\t{}".format(time.time(), string.strip())) + def update_text(self, string, event): + if event.sequence_number == 1: + return + if event.type == X.KeyRelease: + event_type = 'release' + else: + event_type = 'press' + print( + "{}\t{}\t{}".format( + datetime.datetime.now().strftime("%d/%m/%YT%H:%M:%S.%f"), + event_type, + string.strip(), + ) + ) def replace_xk_key(self, key, keysym): + if key == u'\u2623': + return 'Lvl5' + if key == u'\uff7e': + return 'Layout' if key == u'\x00' or key == '\x00': return '' for name in dir(XK):