]> git.neil.brown.name Git - plato.git/commitdiff
Add fakeinput.py
authorNeilBrown <neilb@suse.de>
Sat, 21 Apr 2012 10:38:56 +0000 (20:38 +1000)
committerNeilBrown <neilb@suse.de>
Sat, 21 Apr 2012 10:38:56 +0000 (20:38 +1000)
'fakeinput' is a python library which makes characters appeared
to be typing at the X11 focus.
This is useful for making an on-screen keyboard for X

Signed-off-by: NeilBrown <neilb@suse.de>
lib/fakeinput.py [new file with mode: 0644]

diff --git a/lib/fakeinput.py b/lib/fakeinput.py
new file mode 100644 (file)
index 0000000..4de5075
--- /dev/null
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2011-2012 Neil Brown <neilb@suse.de>
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 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, write to the Free Software Foundation, Inc.,
+#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# fakeinput.py
+# based on pykey, see also "crikey"
+#    and http://shallowsky.com/blog/tags/crikey/
+#
+# a char or a string can be sent as typed input to the
+# 'focus' window in X11.
+# Strangely, '<' cannot be marked as shifted, so it is entered
+# as an unshifted '<'.
+
+import Xlib.display
+import Xlib.X
+import Xlib.XK
+import Xlib.protocol.event
+
+UseXTest = True
+
+try :
+    import Xlib.ext.xtest
+except ImportError:
+    UseXTest = False
+    print "no XTest extension; using XSendEvent"
+
+import sys, time
+
+special_X_keysyms = {
+    '\b': "BackSpace",
+    ' ' : "space",
+    '\t' : "Tab",
+    '\n' : "Return",  # for some reason this needs to be cr, not lf
+    '\r' : "Return",
+    '\033' : "Escape",
+    '!' : "exclam",
+    '#' : "numbersign",
+    '%' : "percent",
+    '$' : "dollar",
+    '&' : "ampersand",
+    '"' : "quotedbl",
+    '\'' : "apostrophe",
+    '(' : "parenleft",
+    ')' : "parenright",
+    '*' : "asterisk",
+    '=' : "equal",
+    '+' : "plus",
+    ',' : "comma",
+    '-' : "minus",
+    '.' : "period",
+    '/' : "slash",
+    ':' : "colon",
+    ';' : "semicolon",
+    '<' : "less",
+    '>' : "greater",
+    '?' : "question",
+    '@' : "at",
+    '[' : "bracketleft",
+    ']' : "bracketright",
+    '\\' : "backslash",
+    '^' : "asciicircum",
+    '_' : "underscore",
+    '`' : "grave",
+    '{' : "braceleft",
+    '|' : "bar",
+    '}' : "braceright",
+    '~' : "asciitilde"
+    }
+
+def get_keysym(ch) :
+    keysym = Xlib.XK.string_to_keysym(ch)
+    if keysym == 0 :
+        # Unfortunately, although this works to get the correct keysym
+        # i.e. keysym for '#' is returned as "numbersign"
+        # the subsequent display.keysym_to_keycode("numbersign") is 0.
+        keysym = Xlib.XK.string_to_keysym(special_X_keysyms[ch])
+    return keysym
+
+def is_shifted(ch) :
+    if ch.isupper() :
+        return True
+    if "~!@#$%^&*()_+{}|:\">?".find(ch) >= 0 :
+        return True
+    return False
+
+class fakeinput:
+    def __init__(self, display = None):
+        global UseXTest
+        if display:
+            self.display = display
+        else:
+            self.display = Xlib.display.Display()
+
+        self.UseXTest = UseXTest
+        
+        if UseXTest and not self.display.query_extension("XTEST") :
+            self.UseXTest = False
+
+        self.new_window()
+
+    def new_window(self):
+        if not self.UseXTest:
+            self.window = self.display.get_input_focus()._data["focus"];
+
+    def char_to_keycode(self, ch):
+        keysym = get_keysym(ch)
+        keycode = self.display.keysym_to_keycode(keysym)
+        if keycode == 0 :
+            print "fakeinput: Sorry, can't map", ch
+
+        if (is_shifted(ch)) :
+            shift_mask = Xlib.X.ShiftMask
+        else :
+            shift_mask = 0
+
+        return keycode, shift_mask
+
+    def send_char(self, ch, dosync = True) :
+        keycode, shift_mask = self.char_to_keycode(ch)
+        if (self.UseXTest) :
+            if shift_mask != 0 :
+                Xlib.ext.xtest.fake_input(self.display, Xlib.X.KeyPress, 50)
+            Xlib.ext.xtest.fake_input(self.display, Xlib.X.KeyPress, keycode)
+            Xlib.ext.xtest.fake_input(self.display, Xlib.X.KeyRelease, keycode)
+            if shift_mask != 0 :
+                Xlib.ext.xtest.fake_input(self.display, Xlib.X.KeyRelease, 50)
+        else :
+            event = Xlib.protocol.event.KeyPress(
+                time = int(time.time()),
+                root = self.display.screen().root,
+                window = self.window,
+                same_screen = 0, child = Xlib.X.NONE,
+                root_x = 0, root_y = 0, event_x = 0, event_y = 0,
+                state = shift_mask,
+                detail = keycode
+                )
+            self.window.send_event(event, propagate = True)
+            event = Xlib.protocol.event.KeyRelease(
+                time = int(time.time()),
+                root = self.display.screen().root,
+                window = self.window,
+                same_screen = 0, child = Xlib.X.NONE,
+                root_x = 0, root_y = 0, event_x = 0, event_y = 0,
+                state = shift_mask,
+                detail = keycode
+                )
+            self.window.send_event(event, propagate = True)
+        if dosync:
+            self.display.sync()
+
+    def send_str(self, str):
+        for ch in str:
+            self.send_char(ch, dosync = False)
+        self.display.sync()
+
+
+if __name__ == "__main__":
+    import sys, time
+    if len(sys.argv) != 2:
+        print "Please provide an arg to be typed"
+        sys.exit(0)
+    print "Please select the target window in 3 seconds"
+    f = fakeinput()
+    time.sleep(3)
+    f.send_str(sys.argv[1])