From 9d46016f5e92a5574a891e491d2968482b8096b8 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sat, 21 Apr 2012 20:38:56 +1000 Subject: [PATCH] Add fakeinput.py '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 --- lib/fakeinput.py | 179 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 lib/fakeinput.py diff --git a/lib/fakeinput.py b/lib/fakeinput.py new file mode 100644 index 0000000..4de5075 --- /dev/null +++ b/lib/fakeinput.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python + +# Copyright (C) 2011-2012 Neil Brown +# +# 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]) -- 2.39.5