diff options
author | Daiki Ueno <ueno@unixuser.org> | 2012-11-12 13:06:06 +0900 |
---|---|---|
committer | Daiki Ueno <ueno@unixuser.org> | 2013-02-17 10:28:20 +0900 |
commit | 1b7a0c6c7b855e6204830656a699483898329bc1 (patch) | |
tree | 93f6124eaa7ca2a5db0d12cca7dff7cd7fea25b7 | |
parent | 4000c25a71121d83744fc50072f2c9034e2456ab (diff) | |
download | caribou-1b7a0c6c7b855e6204830656a699483898329bc1.tar.gz |
Port daemon from Python to Vala
This eliminates the need of the shell script wrapper.
https://bugzilla.gnome.org/show_bug.cgi?id=688218
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | bin/Makefile.am | 2 | ||||
-rwxr-xr-x | bin/caribou.in | 41 | ||||
-rw-r--r-- | caribou/Makefile.am | 3 | ||||
-rw-r--r-- | caribou/daemon/Makefile.am | 7 | ||||
-rw-r--r-- | caribou/daemon/__init__.py | 1 | ||||
-rw-r--r-- | caribou/daemon/main.py | 123 | ||||
-rw-r--r-- | configure.ac | 8 | ||||
-rw-r--r-- | daemon/Makefile.am | 23 | ||||
-rw-r--r-- | daemon/daemon.vala | 196 |
10 files changed, 227 insertions, 179 deletions
diff --git a/Makefile.am b/Makefile.am index 07a7a55..c23aab2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} -SUBDIRS = caribou bin data po libcaribou modules tools vapi +SUBDIRS = caribou bin data po libcaribou modules tools vapi daemon if HAVE_VALADOC SUBDIRS += docs diff --git a/bin/Makefile.am b/bin/Makefile.am index b001b10..2b1839a 100644 --- a/bin/Makefile.am +++ b/bin/Makefile.am @@ -1,4 +1,4 @@ -bin_SCRIPTS = caribou caribou-preferences +bin_SCRIPTS = caribou-preferences libexec_SCRIPTS = antler-keyboard DISTCLEANFILES = $(bin_SCRIPTS) $(libexec_SCRIPTS) diff --git a/bin/caribou.in b/bin/caribou.in deleted file mode 100755 index f2cdc2f..0000000 --- a/bin/caribou.in +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# -# Caribou - text entry and UI navigation application -# -# Copyright (C) 2009 Adaptive Technology Resource Centre -# * Contributor: Ben Konrath <ben@bagu.org> -# Copyright (C) 2009 Eitan Isaacson <eitan@monotonous.org> -# Copyright (C) 2009 Sun Microsystems, Inc. -# * Contributor: Willie Walker <william.walker@sun.com> -# Copyright (C) 2009 Flavio Percoco <flaper87@flaper87.org> -# * Contributor: Flavio Percoco <flaper87@flaper87.org> -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation; either version 2.1 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 Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser 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 - -script_dir="$(dirname "$(readlink -f ${0})")" - -prefix=@prefix@ -exec_prefix=@exec_prefix@ - -if [ $script_dir = "@bindir@" ] -then - export PYTHONPATH="@prefix@/lib/python@PYTHON_VERSION@/site-packages${PYTHONPATH:+:$PYTHONPATH}" - export GI_TYPELIB_PATH="@libdir@/girepository-1.0${GI_TYPELIB_PATH:+:$GI_TYPELIB_PATH}" -else - export PYTHONPATH="$(dirname $script_dir)${PYTHONPATH:+:$PYTHONPATH}" - export GI_TYPELIB_PATH="$(dirname $script_dir)/libcaribou${GI_TYPELIB_PATH:+:$GI_TYPELIB_PATH}" -fi - -@PYTHON@ -c "from caribou.daemon.main import CaribouDaemon; CaribouDaemon().run()" diff --git a/caribou/Makefile.am b/caribou/Makefile.am index cdb7045..2d7fd50 100644 --- a/caribou/Makefile.am +++ b/caribou/Makefile.am @@ -6,8 +6,7 @@ caribou_PYTHON = \ SUBDIRS = \ antler/ \ - settings/ \ - daemon/ + settings/ DISTCLEANFILES = i18n.py diff --git a/caribou/daemon/Makefile.am b/caribou/daemon/Makefile.am deleted file mode 100644 index 0bf6268..0000000 --- a/caribou/daemon/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -caribou_daemondir = $(pkgpythondir)/daemon/ - -caribou_daemon_PYTHON = \ - __init__.py \ - main.py - --include $(top_srcdir)/git.mk diff --git a/caribou/daemon/__init__.py b/caribou/daemon/__init__.py deleted file mode 100644 index fd9812d..0000000 --- a/caribou/daemon/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from main import CaribouDaemon diff --git a/caribou/daemon/main.py b/caribou/daemon/main.py deleted file mode 100644 index dded0e5..0000000 --- a/caribou/daemon/main.py +++ /dev/null @@ -1,123 +0,0 @@ -import pyatspi -from gi.repository import GLib -from gi.repository import Gio -from gi.repository import GdkX11 - -from string import Template - -from caribou.i18n import _ -from caribou import APP_NAME - -debug = False - -class CaribouDaemon: - def __init__(self): - try: - self.keyboard_proxy = Gio.DBusProxy.new_for_bus_sync( - Gio.BusType.SESSION, - Gio.DBusProxyFlags.NONE, - None, - "org.gnome.Caribou.Keyboard", - "/org/gnome/Caribou/Keyboard", - "org.gnome.Caribou.Keyboard", - None) - except GLib.GError, e: - self._show_error_dialog(e.message) - self._current_acc = None - self._x11_display = GdkX11.X11Display.get_default() - self._register_event_listeners() - - def _show_error_dialog(self, message): - from gi.repository import Gtk - msgdialog = Gtk.MessageDialog(None, - Gtk.DialogFlags.MODAL, - Gtk.MessageType.ERROR, - Gtk.ButtonsType.CLOSE, - _("Error starting %s") % APP_NAME) - msgdialog.format_secondary_text(message) - msgdialog.run() - quit() - - def _register_event_listeners(self): - pyatspi.Registry.registerEventListener( - self.on_focus, "object:state-changed:focused") - pyatspi.Registry.registerEventListener(self.on_focus, "focus") - pyatspi.Registry.registerEventListener( - self.on_text_caret_moved, "object:text-caret-moved") - - def _deregister_event_listeners(self): - pyatspi.Registry.deregisterEventListener( - self.on_focus, "object:state-changed:focused") - pyatspi.Registry.deregisterEventListener(self.on_focus, "focus") - pyatspi.Registry.deregisterEventListener( - self.on_text_caret_moved, "object:text-caret-moved") - - def on_text_caret_moved(self, event): - if self._current_acc == event.source: - text = self._current_acc.queryText() - x, y, w, h = text.getCharacterExtents(text.caretOffset, - pyatspi.DESKTOP_COORDS) - if (x, y, w, h) == (0, 0, 0, 0): - component = self._current_acc.queryComponent() - bb = component.getExtents(pyatspi.DESKTOP_COORDS) - x, y, w, h = bb.x, bb.y, bb.width, bb.height - - self.keyboard_proxy.SetCursorLocation('(iiii)', x, y, w, h) - if debug == True: - print "object:text-caret-moved in", event.host_application.name, - print event.detail1, event.source.description - - def _set_entry_location(self, acc): - text = acc.queryText() - bx, by, bw, bh = text.getCharacterExtents(text.caretOffset, - pyatspi.DESKTOP_COORDS) - - component = acc.queryComponent() - entry_bb = component.getExtents(pyatspi.DESKTOP_COORDS) - - if (bx, by, bw, bh) == (0, 0, 0, 0): - bx, by, bw, bh = entry_bb.x, entry_bb.y, entry_bb.width, entry_bb.height - - self.keyboard_proxy.SetCursorLocation('(iiii)', bx, by, bw, bh) - - self.keyboard_proxy.SetEntryLocation('(iiii)', entry_bb.x, entry_bb.y, - entry_bb.width, entry_bb.height) - - self.keyboard_proxy.Show('(u)', self._x11_display.get_user_time()) - - def on_focus(self, event): - acc = event.source - source_role = acc.getRole() - if acc.getState().contains(pyatspi.STATE_EDITABLE) or \ - source_role == pyatspi.ROLE_TERMINAL: - if source_role in (pyatspi.ROLE_TEXT, - pyatspi.ROLE_PARAGRAPH, - pyatspi.ROLE_PASSWORD_TEXT, - pyatspi.ROLE_TERMINAL, - pyatspi.ROLE_ENTRY): - if event.type.startswith("focus") or event.detail1 == 1: - self._set_entry_location(acc) - self._current_acc = event.source - if debug == True: - print "enter text widget in", event.host_application.name - elif event.detail1 == 0 and acc == self._current_acc: - self.keyboard_proxy.Hide('(u)', - self._x11_display.get_user_time()) - self._current_acc = None - if debug == True: - print "leave text widget in", event.host_application.name - else: - if debug == True: - print _("WARNING - Caribou: unhandled editable widget:"), \ - event.source - - def clean_exit(self): - self.keyboard_proxy.Hide('(u)', self._x11_display.get_user_time()) - self._deregister_event_listeners() - - def run(self): - try: - pyatspi.Registry.start() - except KeyboardInterrupt: - self.clean_exit() - pyatspi.Registry.stop() diff --git a/configure.ac b/configure.ac index be48c44..228d094 100644 --- a/configure.ac +++ b/configure.ac @@ -32,7 +32,10 @@ VALADOC_REQUIRED=0.3.1 PKG_CHECK_MODULES(CARIBOU, [ pygobject-3.0 >= $PYGOBJECT_REQUIRED, gtk+-3.0 >= $GTK_REQUIRED, - clutter-1.0 >= $CLUTTER_REQUIRED + clutter-1.0 >= $CLUTTER_REQUIRED, + gdk-3.0 >= $GDK_REQUIRED, + x11, + atspi-2 ]) AC_SUBST(CARIBOU_CFLAGS) AC_SUBST(CARIBOU_LIBS) @@ -132,9 +135,7 @@ caribou/Makefile caribou/i18n.py caribou/antler/Makefile caribou/settings/Makefile -caribou/daemon/Makefile bin/Makefile -bin/caribou bin/caribou-preferences bin/antler-keyboard data/Makefile @@ -151,4 +152,5 @@ modules/gtk2/Makefile tools/Makefile docs/Makefile vapi/Makefile +daemon/Makefile ]) diff --git a/daemon/Makefile.am b/daemon/Makefile.am new file mode 100644 index 0000000..13bca11 --- /dev/null +++ b/daemon/Makefile.am @@ -0,0 +1,23 @@ +bin_PROGRAMS = caribou + +caribou_VALAFLAGS = \ + --vapidir=$(top_srcdir)/vapi \ + --pkg config \ + --pkg gtk+-3.0 \ + --pkg gdk-x11-3.0 \ + --pkg atspi-2 \ + --pkg x11 \ + --pkg posix \ + --pkg libxklavier \ + $(VALAFLAGS) + +caribou_CFLAGS = \ + $(CARIBOU_CFLAGS) \ + -DLOCALEDIR=\"$(datadir)/locale\" + +caribou_LDADD = \ + $(CARIBOU_LIBS) + +caribou_SOURCES = daemon.vala + +-include $(top_srcdir)/git.mk diff --git a/daemon/daemon.vala b/daemon/daemon.vala new file mode 100644 index 0000000..2f5feee --- /dev/null +++ b/daemon/daemon.vala @@ -0,0 +1,196 @@ +namespace Caribou { + // We can't use the name "Keyboard" here since caribou-gtk-module + // might register the name first. + [DBus (name = "org.gnome.Caribou.Keyboard")] + interface _Keyboard : Object { + public abstract void set_cursor_location (int x, int y, int w, int h) + throws IOError; + public abstract void set_entry_location (int x, int y, int w, int h) + throws IOError; + public abstract void show (uint32 timestamp) throws IOError; + public abstract void hide (uint32 timestamp) throws IOError; + } + + class Daemon : Object { + _Keyboard keyboard; + Atspi.Accessible current_acc; + unowned Gdk.Display display; + + public Daemon () { + display = Gdk.Display.get_default (); + } + + void on_get_proxy_ready (GLib.Object? obj, GLib.AsyncResult res) { + try { + keyboard = Bus.get_proxy.end (res); + } catch (Error e) { + error ("%s\n".printf (e.message)); + } + + try { + register_event_listeners (); + } catch (Error e) { + warning ("can't register event listeners: %s", e.message); + } + } + + uint32 get_timestamp () { + return Gdk.X11Display.get_user_time (display); + } + + void set_entry_location (Atspi.Accessible acc) throws Error { + var text = acc.get_text (); + var rect = text.get_character_extents (text.get_caret_offset (), + Atspi.CoordType.SCREEN); + var component = acc.get_component (); + var entry_rect = component.get_extents (Atspi.CoordType.SCREEN); + if (rect.x == 0 && rect.y == 0 && + rect.width == 0 && rect.height == 0) { + rect = entry_rect; + } + + keyboard.set_cursor_location (rect.x, rect.y, + rect.width, rect.height); + + keyboard.set_entry_location (entry_rect.x, entry_rect.y, + entry_rect.width, entry_rect.height); + + keyboard.show (get_timestamp ()); + } + + void on_focus (owned Atspi.Event event) throws Error { + var acc = event.source; + var source_role = acc.get_role (); + if (acc.get_state_set ().contains (Atspi.StateType.EDITABLE) || + source_role == Atspi.Role.TERMINAL) { + switch (source_role) { + case Atspi.Role.TEXT: + case Atspi.Role.PARAGRAPH: + case Atspi.Role.PASSWORD_TEXT: + case Atspi.Role.TERMINAL: + case Atspi.Role.ENTRY: + if (event.type.has_prefix ("focus") || event.detail1 == 1) { + set_entry_location (acc); + current_acc = event.source; + debug ("enter text widget in %s", + event.source.get_application ().name); + } else if (event.detail1 == 0 && acc == current_acc) { + keyboard.hide (get_timestamp ()); + current_acc = null; + debug ("leave text widget in %s", + event.source.get_application ().name); + } else { + warning ("unhandled editable widget: %s", + event.source.name); + } + break; + default: + break; + } + } + } + + void on_focus_ignore_error (owned Atspi.Event event) { + try { + on_focus (event); + } catch (Error e) { + warning ("error in focus handler: %s", e.message); + } + } + + void on_text_caret_moved (owned Atspi.Event event) throws Error { + if (current_acc == event.source) { + var text = current_acc.get_text (); + var rect = text.get_character_extents (text.get_caret_offset (), + Atspi.CoordType.SCREEN); + if (rect.x == 0 && rect.y == 0 && + rect.width == 0 && rect.height == 0) { + var component = current_acc.get_component (); + rect = component.get_extents (Atspi.CoordType.SCREEN); + } + + keyboard.set_cursor_location (rect.x, rect.y, + rect.width, rect.height); + debug ("object:text-caret-moved in %s: %d %s", + event.source.get_application ().name, + event.detail1, event.source.description); + } + } + + void on_text_caret_moved_ignore_error (owned Atspi.Event event) { + try { + on_text_caret_moved (event); + } catch (Error e) { + warning ("error in text caret movement handler: %s", e.message); + } + } + + void register_event_listeners () throws Error { + Atspi.EventListener.register_from_callback ( + on_focus_ignore_error, "object:state-changed:focused"); + Atspi.EventListener.register_from_callback ( + on_focus_ignore_error, "focus:"); + Atspi.EventListener.register_from_callback ( + on_text_caret_moved_ignore_error, "object:text-caret-moved"); + } + + void deregister_event_listeners () throws Error { + Atspi.EventListener.deregister_from_callback ( + on_focus_ignore_error, "object:state-changed:focused"); + Atspi.EventListener.deregister_from_callback ( + on_focus_ignore_error, "focus:"); + Atspi.EventListener.deregister_from_callback ( + on_text_caret_moved_ignore_error, "object:text-caret-moved"); + } + + public void run () { + Bus.get_proxy.begin<_Keyboard> (BusType.SESSION, + "org.gnome.Caribou.Keyboard", + "/org/gnome/Caribou/Keyboard", + 0, + null, + on_get_proxy_ready); + Gtk.main (); + } + + public void quit () { + if (keyboard != null) { + try { + keyboard.hide (get_timestamp ()); + } catch (IOError e) { + warning ("can't hide keyboard: %s", e.message); + } + + try { + deregister_event_listeners (); + } catch (Error e) { + warning ("can't deregister event listeners: %s", e.message); + } + keyboard = null; + } + + if (Gtk.main_level () > 0) + Gtk.main_quit (); + } + } +} + +static int main (string[] args) { + Gtk.init (ref args); + + Intl.setlocale (LocaleCategory.ALL, ""); + Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR); + Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8"); + Intl.textdomain (Config.GETTEXT_PACKAGE); + + Atspi.init (); + + var daemon = new Caribou.Daemon (); + Unix.signal_add (Posix.SIGINT, () => { + daemon.quit (); + return false; + }); + daemon.run (); + + return 0; +} |