diff options
Diffstat (limited to 'daemon/daemon.vala')
-rw-r--r-- | daemon/daemon.vala | 196 |
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; +} |