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
diff --git a/README.md b/README.md
index c9d77d4..1a043c0 100644
--- a/README.md
+++ b/README.md
@@ -1,45 +1,48 @@
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
+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` 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;
+* 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`.
+
+Preview
+=======
+Some awkward highlights of this features shown here:
+
-Author
-======
+Original author
+===============
Pablo Seminario
Thanks to
@@ -64,5 +67,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/__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 6ca9ae9..a9bf7a7 100644
--- a/Screenkey/listenkbd.py
+++ b/Screenkey/listenkbd.py
@@ -17,6 +17,7 @@
import subprocess
import modmap
import gtk
+import datetime
from Xlib import X, XK, display
from Xlib.ext import record
@@ -26,57 +27,59 @@
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_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'\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',
}
+
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
@@ -84,13 +87,15 @@ def __init__(self, label, logger, mode):
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
+ 'super': False
}
self.logger.debug("Thread created")
@@ -106,19 +111,19 @@ def __init__(self, label, logger, mode):
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.")
@@ -130,22 +135,26 @@ def lookup_keysym(self, keysym):
return name[3:]
return ""
- def replace_key(self, key, keysym):
+ 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 in REPLACE_KEYS:
+ 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 not string is None:
+ if string is not None:
# TODO: make this configurable
- if string == 'Ctrl+F1 ':
+ 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)
@@ -160,14 +169,15 @@ 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.
- 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 = 0 == subprocess.call(
+ ['ps', '-C', 'sudo'],
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ if sudo_is_running:
+ return
if reply.category != record.FromServer:
return
@@ -182,15 +192,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))
+ 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 = ''
@@ -198,106 +208,96 @@ 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]
- self.logger.debug("Key %s(keycode) %s. Symbols %s" %
- (event.detail,
- event.type == X.KeyPress and "pressed" or "released",
- 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]))
else:
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
- 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
+ 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['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
- 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']:
+ 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
-
- string = self.replace_key(key, keysym)
- if string:
- key = string
-
- if mod != '':
- key = "%s%s " % (mod, key)
- else:
- key = "%s%s" % (mod, key)
- else:
- return
+ 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
@@ -316,3 +316,78 @@ def stop(self):
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, 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):
+ if name[:3] == "XK_" and getattr(XK, name) == keysym:
+ if name.upper() in REPLACE_KEYS:
+ return name[3:]
diff --git a/Screenkey/modmap.py b/Screenkey/modmap.py
index 4e1cad5..9940082 100644
--- a/Screenkey/modmap.py
+++ b/Screenkey/modmap.py
@@ -1,15 +1,14 @@
-#!/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
# 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 .
@@ -17,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 = {}
@@ -38,53 +40,21 @@ 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
- keymap[keycode] = 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
+
def get_modifier_map():
- modifiers = {}
+ modifiers = []
modifier_map = cmd_modifier_map()
@@ -97,9 +67,779 @@ 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
+
+def keysym_to_unicode(keysym):
+ if keysym in mapping:
+ return unichr(mapping[keysym])
+ else:
+ try:
+ return unichr(keysym)
+ except ValueError:
+ return u'\x00'
+
+
+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,
+}
diff --git a/Screenkey/screenkey.py b/Screenkey/screenkey.py
index a292ed9..553d8a7 100644
--- a/Screenkey/screenkey.py
+++ b/Screenkey/screenkey.py
@@ -3,16 +3,17 @@
# 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 .
import os
+import re
import pygtk
pygtk.require('2.0')
import gtk
@@ -24,10 +25,12 @@
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
POS_BOTTOM = 2
+POS_KEEP = 3
SIZE_LARGE = 0
SIZE_MEDIUM = 1
@@ -36,29 +39,30 @@
MODE_RAW = 0
MODE_NORMAL = 1
+
class Screenkey(gtk.Window):
POSITIONS = {
- POS_TOP:_('Top'),
- POS_CENTER:_('Center'),
- POS_BOTTOM:_('Bottom'),
+ 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(),
+ 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, nosudo, window_id):
gtk.Window.__init__(self)
-
self.timer = None
self.logger = logger
@@ -67,7 +71,7 @@ def __init__(self, logger, nodetach):
self.options = {
'timeout': 2.5,
'position': POS_BOTTOM,
- 'size': SIZE_MEDIUM,
+ 'size': SIZE_SMALL,
'mode': MODE_NORMAL,
}
@@ -83,12 +87,17 @@ 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)
- gobject.signal_new("text-changed", gtk.Label,
- gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())
+ 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()
self.label.set_justify(gtk.JUSTIFY_RIGHT)
self.label.set_ellipsize(pango.ELLIPSIZE_START)
@@ -96,17 +105,30 @@ def __init__(self, logger, nodetach):
self.label.show()
self.add(self.label)
- self.screen_width = gtk.gdk.screen_width()
- self.screen_height = gtk.gdk.screen_height()
- self.set_window_size(self.options['size'])
+ self.screen_width = gtk.gdk.screen_width()
+ self.screen_height = gtk.gdk.screen_height()
self.set_gravity(gtk.gdk.GRAVITY_CENTER)
- self.set_xy_position(self.options['position'])
- self.listenkbd = ListenKbd(self.label, logger=self.logger,
- mode=self.options['mode'])
- self.listenkbd.start()
+ 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
+
+ self.listenkbd = ListenKbd(self.label, logger=self.logger,
+ mode=self.options['mode'],
+ nosudo=self.nosudo)
+ self.listenkbd.start()
menu = gtk.Menu()
@@ -121,7 +143,6 @@ def __init__(self, logger, nodetach):
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()
@@ -139,29 +160,36 @@ def __init__(self, logger, nodetach):
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")
- self.systray.connect("popup-menu",
- self.on_statusicon_popup, menu)
+ "preferences-desktop-keyboard-shortcuts")
+ self.systray.connect("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)
+
+ 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"""
@@ -174,7 +202,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
@@ -190,6 +218,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 = 3 * 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
@@ -202,16 +245,35 @@ 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 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()
@@ -221,12 +283,14 @@ 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:
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):
@@ -244,30 +308,55 @@ 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()
def on_change_mode(self, mode):
self.listenkbd.stop()
- self.listenkbd = ListenKbd(self.label, logger=self.logger,
- mode=mode)
+ 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,
- mode=self.options['mode'])
+ self.listenkbd = ListenKbd(self.label, logger=self.logger,
+ mode=self.options['mode'],
+ nosudo=self.nosudo)
self.listenkbd.start()
else:
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("DejaVu 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,
- (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
+ 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):
self.options['timeout'] = widget.get_value()
@@ -312,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()
@@ -337,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()
@@ -352,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)
@@ -373,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)
@@ -402,21 +491,17 @@ 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()
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)
os.setsid()
-
diff --git a/screenkey b/screenkey
index 46674cb..912a894 100755
--- a/screenkey
+++ b/screenkey
@@ -1,15 +1,15 @@
-#!/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
# 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 .
@@ -21,22 +21,45 @@ pygtk.require('2.0')
import gtk
import gettext
import locale
+import threading
locale.setlocale(locale.LC_ALL, '')
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
-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=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"))
+ 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",
+ dest="nosudo", default=True,
+ help=_("do not perform very expensive sudo running check"))
+ 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:
@@ -44,7 +67,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:
@@ -56,10 +79,27 @@ def Main():
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(APP_NAME)
- s = Screenkey(logger=logger, nodetach=options.nodetach)
- gtk.main()
+ gtk.gdk.threads_init()
+ 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()
-
+ main()