summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@unixuser.org>2012-11-12 13:06:06 +0900
committerDaiki Ueno <ueno@unixuser.org>2013-02-17 10:28:20 +0900
commit1b7a0c6c7b855e6204830656a699483898329bc1 (patch)
tree93f6124eaa7ca2a5db0d12cca7dff7cd7fea25b7
parent4000c25a71121d83744fc50072f2c9034e2456ab (diff)
downloadcaribou-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.am2
-rw-r--r--bin/Makefile.am2
-rwxr-xr-xbin/caribou.in41
-rw-r--r--caribou/Makefile.am3
-rw-r--r--caribou/daemon/Makefile.am7
-rw-r--r--caribou/daemon/__init__.py1
-rw-r--r--caribou/daemon/main.py123
-rw-r--r--configure.ac8
-rw-r--r--daemon/Makefile.am23
-rw-r--r--daemon/daemon.vala196
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;
+}