summaryrefslogtreecommitdiff
path: root/daemon/daemon.vala
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/daemon.vala')
-rw-r--r--daemon/daemon.vala196
1 files changed, 196 insertions, 0 deletions
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;
+}