summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Konrath <ben@bagu.org>2009-11-14 22:09:34 -0600
committerBen Konrath <ben@bagu.org>2009-11-14 22:09:34 -0600
commitc56a984f7c45b1594cc2b6507678c4cab942f852 (patch)
treed64245434ccb00ae9f90696c31f167764bedda78
downloadcaribou-c56a984f7c45b1594cc2b6507678c4cab942f852.tar.gz
initial commit
-rw-r--r--README6
-rw-r--r--src/caribou.py147
-rw-r--r--src/keyboard.py143
3 files changed, 296 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..37cd05a
--- /dev/null
+++ b/README
@@ -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()
+