diff options
-rw-r--r-- | README | 6 | ||||
-rw-r--r-- | src/caribou.py | 147 | ||||
-rw-r--r-- | src/keyboard.py | 143 |
3 files changed, 296 insertions, 0 deletions
@@ -0,0 +1,6 @@ +Running Caribou +=============== + + +cd src +python ./caribou.py diff --git a/src/caribou.py b/src/caribou.py new file mode 100644 index 0000000..4db7a17 --- /dev/null +++ b/src/caribou.py @@ -0,0 +1,147 @@ +import pyatspi +import gtk +import keyboard +import gettext +import getopt +import sys + +_ = gettext.gettext + +debug = False + +class Test: + def __init__(self): + self.__current_acc = None + + def on_text_caret_moved(self, event): + if self.__current_acc == event.source: + self.__set_location(event.source) + if debug == True: + print "object:text-caret-moved in", event.host_application.name, event.detail1, event.source.description + + def __set_text_location(self, acc): + text = acc.queryText() + [x, y, width, height] = text.getCharacterExtents(text.caretOffset, pyatspi.DESKTOP_COORDS) + cp.set_cursor_location(x, y + height) + cp.show_all() + + def __set_entry_location(self, acc): + text = acc.queryText() + [x, y, width, height] = text.getCharacterExtents(text.caretOffset, pyatspi.DESKTOP_COORDS) + if x == 0 and y == 0 and width == 0 and height == 0: + component = acc.queryComponent() + bb = component.getExtents(pyatspi.DESKTOP_COORDS) + cp.set_cursor_location(bb.x, bb.y + bb.height) + else: + cp.set_cursor_location(x, y + height) + cp.show_all() + + def on_state_changed_focused(self, event): + acc = event.source + if pyatspi.STATE_EDITABLE in acc.getState().getStates(): + if event.source_role == pyatspi.ROLE_TEXT: + if event.detail1 == 1: + self.__set_text_location(acc) + self.__current_acc = event.source + self.__set_location = self.__set_text_location + if debug == True: + print "enter text widget in", event.host_application.name + elif event.detail1 == 0: + cp.hide_all() + self.__current_acc = None + self.__set_location = None + if debug == True: + print "leave text widget in", event.host_application.name + + elif event.source_role == pyatspi.ROLE_ENTRY: + if event.detail1 == 1: + self.__set_entry_location(acc) + self.__current_acc = event.source + self.__set_location = self.__set_entry_location + if debug == True: + print "enter entry widget in", event.host_application.name + elif event.detail1 == 0: + cp.hide_all() + self.__current_acc = None + self.__set_location = None + if debug == True: + print "leave entry widget in", event.host_application.name + else: + print _("WARNING - Caribou: unhandled editable widget:"), event.source + + # Firefox does report leave entry widget events. + # This could be a way to get the entry widget leave events. + #else: + # if event.detail1 == 1: + # cp.hide_all() + # print "--> LEAVE EDITABLE TEXT <--" + + def on_key_down(self, event): + # key binding for controling the row column scanning + # TODO: needs implementing + if event.event_string == "Control_R": + pass + elif event.event_string == "Shift_R": + if debug == True: + print "quitting ..." + # TODO: use for loop here? see below + result = pyatspi.Registry.deregisterEventListener(self.on_text_caret_moved, "object:text-caret-moved") + if debug == True: + print "deregisterEventListener - object:text-caret-moved ...", + if result == False: + print "OK" + else: + print "FAIL" + result = pyatspi.Registry.deregisterEventListener(self.on_state_changed_focused, "object:state-changed:focused") + if debug == True: + print "deregisterEventListener - object:state-changed:focused ...", + if result == False: + print "OK" + else: + print "FAIL" + result = pyatspi.Registry.deregisterKeystrokeListener(self.on_key_down, mask = None, kind = (pyatspi.KEY_PRESSED_EVENT,)) + if debug == True: + print "deregisterKeystrokeListener" + gtk.main_quit() + + +def usage(): + """Prints out usage information.""" + print _("Usage:") + print " " + sys.arg[0] + _(" [OPTION...]") + print + print _("Help Options:") + print " -h, --help " + _("Show this help message") + print " -d, --debug " + _("Print debug messages on stdout") + +if __name__ == "__main__": + + try: + options, xargs = getopt.getopt(sys.argv[1:], "d", ["debug"]) + except getopt.GetoptError, e: + print "Error: " + e.__str__() + "\n" + usage() + sys.exit(1) + + for opt, val in options: + if opt in ("-h", "--help"): + usage() + sys.exit(0) + + if opt in ("-d", "--debug"): + debug = True + + test = Test() + # TODO: make a for loop + #EVENTS = ["object:state-changed:focused", "object:text-caret-moved"] + #for f in dir(test): + # print f, isinstance(f, str) + pyatspi.Registry.registerEventListener(test.on_state_changed_focused, "object:state-changed:focused") + pyatspi.Registry.registerEventListener(test.on_text_caret_moved, "object:text-caret-moved") + pyatspi.Registry.registerKeystrokeListener(test.on_key_down, mask = None, kind = (pyatspi.KEY_PRESSED_EVENT,)) + + # TODO: move text entry detection to its own file + cp = keyboard.CaribouKeyboard() + cp.hide_all() + + gtk.main() diff --git a/src/keyboard.py b/src/keyboard.py new file mode 100644 index 0000000..7474334 --- /dev/null +++ b/src/keyboard.py @@ -0,0 +1,143 @@ +# -*- coding: UTF-8 -*- + +import candidatepanel +import gtk +import gobject +import gtk.gdk as gdk +import pango +import virtkey + + +# from OnBoard.utils.py +keysyms = {"space" : 0xff80, + "insert" : 0xff9e, + "home" : 0xff50, + "page_up" : 0xff55, + "page_down" : 0xff56, + "end" : 0xff57, + "delete" : 0xff9f, + "return" : 0xff0d, + "backspace" : 0xff08, + "left" : 0xff51, + "up" : 0xff52, + "right" : 0xff53, + "down" : 0xff54,} + +# TODO add horizonal keysize - will be able to specify a mulitplier +# TODO add key colour + +backspace = ("⌫", keysyms["backspace"]) +control = (".?12", keysyms["space"]) #TODO not implemented + +# key format ("label", keysym, size) +layout = ( ("q", "w", "e", "r", "t", "y", "u", "i", "o", "p"), + ("a", "s", "d", "f", "g", "h", "j", "k", "l", backspace ), + ("z", "x", "c", "v", "b", "n", "m", " ", control, "⇧") ) + +# TODO add keyboard layers +#layout2 = ( ("Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"), +# ("A", "S", "D", "F", "G", "H", "J", "K", "L", backspace ), +# ("Z", "X", "C", "V", "B", "N", "M", " ", " ", "⇧") ) + +class CaribouPredicitionArea(gtk.HBox): + pass + +class CaribouKey(gtk.Button): + __gtype_name__ = "CaribouKey" + + def __init__(self, key, vk): + if isinstance(key, tuple): + label = key[0] + keysym = key[1] + self.__press_key = vk.press_keysym + self.__release_key = vk.release_keysym + elif isinstance(key, str): + label = key + keysym = ord(key.decode('utf-8')) + self.__press_key = vk.press_unicode + self.__release_key = vk.release_unicode + else: + pass #TODO throw error here + gtk.Button.__init__(self, label) + #self.set_relief(gtk.RELIEF_NONE) + self.connect("clicked", self.__on_click, keysym) + + def __on_click(self, widget, data): + self.__press_key(data) + self.__release_key(data) + +gobject.type_register(CaribouKey) + +class CaribouKeyboard(gtk.VBox): + __gtype_name__ = "CaribouKeyboard" + + def __init__(self): + super(CaribouKeyboard, self).__init__() + self.set_name("CaribouKeyboard") + + self.__toplevel = gtk.Window(gtk.WINDOW_POPUP) + self.__toplevel.add(self) + self.__toplevel.connect("size-allocate", lambda w, a: self.__check_position()) + + self.__cursor_location = (0, 0) + + self.__build_keyboard() + + + #TODO: validate keyboard + def __build_keyboard(self): + vk = virtkey.virtkey() + for row in layout: + rowhbox = gtk.HBox(homogeneous=True) + for keylabel in row: + key = CaribouKey(keylabel, vk) + rowhbox.pack_start(key, expand=False, fill=True) + self.pack_start(rowhbox, expand=False, fill=False) + + #TODO understand + def set_cursor_location(self, x, y): + #print "----> SET CURSOR LOCATION" + self.__cursor_location = (x, y) + self.__check_position() + + def do_size_request(self, requisition): + #print "---->> DO SIZE REQUEST" + gtk.VBox.do_size_request(self, requisition) + self.__toplevel.resize(1, 1) + + def __check_position(self): + #print "---->>> CHECK POSITION" + bx = self.__cursor_location[0] + self.__toplevel.allocation.width + by = self.__cursor_location[1] + self.__toplevel.allocation.height + + root_window = gdk.get_default_root_window() + sx, sy = root_window.get_size() + + if bx > sx: + x = sx - self.__toplevel.allocation.width + else: + x = self.__cursor_location[0] + + if by > sy: + y = sy - self.__toplevel.allocation.height + else: + y = self.__cursor_location[1] + + self.move(x, y) + + def show_all(self): + gtk.VBox.show_all(self) + self.__toplevel.show_all() + + def hide_all(self): + gtk.VBox.hide_all(self) + self.__toplevel.hide_all() + + def move(self, x, y): + self.__toplevel.move(x, y) + +if __name__ == "__main__": + ckbd = CaribouKeyboard() + ckbd.show_all() + gtk.main() + |