123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "ApplicationAccessibleWrap.h"
- #include "mozilla/Likely.h"
- #include "nsAccessibilityService.h"
- #include "nsMai.h"
- #include <atk/atk.h>
- #include <gtk/gtk.h>
- #include <string.h>
- using namespace mozilla;
- using namespace mozilla::a11y;
- typedef AtkUtil MaiUtil;
- typedef AtkUtilClass MaiUtilClass;
- #define MAI_VERSION MOZILLA_VERSION
- #define MAI_NAME "Gecko"
- extern "C" {
- static guint (*gail_add_global_event_listener)(GSignalEmissionHook listener,
- const gchar* event_type);
- static void (*gail_remove_global_event_listener) (guint remove_listener);
- static void (*gail_remove_key_event_listener) (guint remove_listener);
- static AtkObject* (*gail_get_root)();
- }
- struct MaiUtilListenerInfo
- {
- gint key;
- guint signal_id;
- gulong hook_id;
- // For window create/destory/minimize/maximize/restore/activate/deactivate
- // events, we'll chain gail_util's add/remove_global_event_listener.
- // So we store the listenerid returned by gail's add_global_event_listener
- // in this structure to call gail's remove_global_event_listener later.
- guint gail_listenerid;
- };
- static GHashTable* sListener_list = nullptr;
- static gint sListener_idx = 1;
- extern "C" {
- static guint
- add_listener (GSignalEmissionHook listener,
- const gchar *object_type,
- const gchar *signal,
- const gchar *hook_data,
- guint gail_listenerid = 0)
- {
- GType type;
- guint signal_id;
- gint rc = 0;
- type = g_type_from_name(object_type);
- if (type) {
- signal_id = g_signal_lookup(signal, type);
- if (signal_id > 0) {
- MaiUtilListenerInfo *listener_info;
- rc = sListener_idx;
- listener_info = (MaiUtilListenerInfo *)
- g_malloc(sizeof(MaiUtilListenerInfo));
- listener_info->key = sListener_idx;
- listener_info->hook_id =
- g_signal_add_emission_hook(signal_id, 0, listener,
- g_strdup(hook_data),
- (GDestroyNotify)g_free);
- listener_info->signal_id = signal_id;
- listener_info->gail_listenerid = gail_listenerid;
- g_hash_table_insert(sListener_list, &(listener_info->key),
- listener_info);
- sListener_idx++;
- }
- else {
- g_warning("Invalid signal type %s\n", signal);
- }
- }
- else {
- g_warning("Invalid object type %s\n", object_type);
- }
- return rc;
- }
- static guint
- mai_util_add_global_event_listener(GSignalEmissionHook listener,
- const gchar *event_type)
- {
- guint rc = 0;
- gchar **split_string;
- split_string = g_strsplit (event_type, ":", 3);
- if (split_string) {
- if (!strcmp ("window", split_string[0])) {
- guint gail_listenerid = 0;
- if (gail_add_global_event_listener) {
- // call gail's function to track gtk native window events
- gail_listenerid =
- gail_add_global_event_listener(listener, event_type);
- }
- rc = add_listener (listener, "MaiAtkObject", split_string[1],
- event_type, gail_listenerid);
- }
- else {
- rc = add_listener (listener, split_string[1], split_string[2],
- event_type);
- }
- g_strfreev(split_string);
- }
- return rc;
- }
- static void
- mai_util_remove_global_event_listener(guint remove_listener)
- {
- if (remove_listener > 0) {
- MaiUtilListenerInfo *listener_info;
- gint tmp_idx = remove_listener;
- listener_info = (MaiUtilListenerInfo *)
- g_hash_table_lookup(sListener_list, &tmp_idx);
- if (listener_info != nullptr) {
- if (gail_remove_global_event_listener &&
- listener_info->gail_listenerid) {
- gail_remove_global_event_listener(listener_info->gail_listenerid);
- }
- /* Hook id of 0 and signal id of 0 are invalid */
- if (listener_info->hook_id != 0 && listener_info->signal_id != 0) {
- /* Remove the emission hook */
- g_signal_remove_emission_hook(listener_info->signal_id,
- listener_info->hook_id);
- /* Remove the element from the hash */
- g_hash_table_remove(sListener_list, &tmp_idx);
- }
- else {
- g_warning("Invalid listener hook_id %ld or signal_id %d\n",
- listener_info->hook_id, listener_info->signal_id);
- }
- }
- else {
- // atk-bridge is initialized with gail (e.g. yelp)
- // try gail_remove_global_event_listener
- if (gail_remove_global_event_listener) {
- return gail_remove_global_event_listener(remove_listener);
- }
- g_warning("No listener with the specified listener id %d",
- remove_listener);
- }
- }
- else {
- g_warning("Invalid listener_id %d", remove_listener);
- }
- }
- static AtkKeyEventStruct *
- atk_key_event_from_gdk_event_key (GdkEventKey *key)
- {
- AtkKeyEventStruct *event = g_new0(AtkKeyEventStruct, 1);
- switch (key->type) {
- case GDK_KEY_PRESS:
- event->type = ATK_KEY_EVENT_PRESS;
- break;
- case GDK_KEY_RELEASE:
- event->type = ATK_KEY_EVENT_RELEASE;
- break;
- default:
- g_assert_not_reached ();
- return nullptr;
- }
- event->state = key->state;
- event->keyval = key->keyval;
- event->length = key->length;
- if (key->string && key->string [0] &&
- (key->state & GDK_CONTROL_MASK ||
- g_unichar_isgraph (g_utf8_get_char (key->string)))) {
- event->string = key->string;
- }
- else if (key->type == GDK_KEY_PRESS ||
- key->type == GDK_KEY_RELEASE) {
- event->string = gdk_keyval_name (key->keyval);
- }
- event->keycode = key->hardware_keycode;
- event->timestamp = key->time;
- return event;
- }
- struct MaiKeyEventInfo
- {
- AtkKeyEventStruct *key_event;
- gpointer func_data;
- };
- union AtkKeySnoopFuncPointer
- {
- AtkKeySnoopFunc func_ptr;
- gpointer data;
- };
- static gboolean
- notify_hf(gpointer key, gpointer value, gpointer data)
- {
- MaiKeyEventInfo *info = (MaiKeyEventInfo *)data;
- AtkKeySnoopFuncPointer atkKeySnoop;
- atkKeySnoop.data = value;
- return (atkKeySnoop.func_ptr)(info->key_event, info->func_data) ? TRUE : FALSE;
- }
- static void
- insert_hf(gpointer key, gpointer value, gpointer data)
- {
- GHashTable *new_table = (GHashTable *) data;
- g_hash_table_insert (new_table, key, value);
- }
- static GHashTable* sKey_listener_list = nullptr;
- static gint
- mai_key_snooper(GtkWidget *the_widget, GdkEventKey *event, gpointer func_data)
- {
- /* notify each AtkKeySnoopFunc in turn... */
- MaiKeyEventInfo *info = g_new0(MaiKeyEventInfo, 1);
- gint consumed = 0;
- if (sKey_listener_list) {
- GHashTable *new_hash = g_hash_table_new(nullptr, nullptr);
- g_hash_table_foreach (sKey_listener_list, insert_hf, new_hash);
- info->key_event = atk_key_event_from_gdk_event_key (event);
- info->func_data = func_data;
- consumed = g_hash_table_foreach_steal (new_hash, notify_hf, info);
- g_hash_table_destroy (new_hash);
- g_free(info->key_event);
- }
- g_free(info);
- return (consumed ? 1 : 0);
- }
- static guint sKey_snooper_id = 0;
- static guint
- mai_util_add_key_event_listener(AtkKeySnoopFunc listener, gpointer data)
- {
- if (MOZ_UNLIKELY(!listener)) {
- return 0;
- }
- static guint key = 0;
- if (!sKey_listener_list) {
- sKey_listener_list = g_hash_table_new(nullptr, nullptr);
- }
- // If we have no registered event listeners then we need to (re)install the
- // key event snooper.
- if (g_hash_table_size(sKey_listener_list) == 0) {
- sKey_snooper_id = gtk_key_snooper_install(mai_key_snooper, data);
- }
- AtkKeySnoopFuncPointer atkKeySnoop;
- atkKeySnoop.func_ptr = listener;
- key++;
- g_hash_table_insert(sKey_listener_list, GUINT_TO_POINTER(key),
- atkKeySnoop.data);
- return key;
- }
- static void
- mai_util_remove_key_event_listener (guint remove_listener)
- {
- if (!sKey_listener_list) {
- // atk-bridge is initialized with gail (e.g. yelp)
- // try gail_remove_key_event_listener
- return gail_remove_key_event_listener(remove_listener);
- }
- g_hash_table_remove(sKey_listener_list, GUINT_TO_POINTER (remove_listener));
- if (g_hash_table_size(sKey_listener_list) == 0) {
- gtk_key_snooper_remove(sKey_snooper_id);
- }
- }
- static AtkObject*
- mai_util_get_root()
- {
- ApplicationAccessible* app = ApplicationAcc();
- if (app)
- return app->GetAtkObject();
- // We've shutdown, try to use gail instead
- // (to avoid assert in spi_atk_tidy_windows())
- // XXX tbsaunde then why didn't we replace the gail atk_util impl?
- if (gail_get_root)
- return gail_get_root();
- return nullptr;
- }
- static const gchar*
- mai_util_get_toolkit_name()
- {
- return MAI_NAME;
- }
- static const gchar*
- mai_util_get_toolkit_version()
- {
- return MAI_VERSION;
- }
- static void
- _listener_info_destroy(gpointer data)
- {
- g_free(data);
- }
- static void
- window_added (AtkObject *atk_obj,
- guint index,
- AtkObject *child)
- {
- if (!IS_MAI_OBJECT(child))
- return;
- static guint id = g_signal_lookup ("create", MAI_TYPE_ATK_OBJECT);
- g_signal_emit (child, id, 0);
- }
- static void
- window_removed (AtkObject *atk_obj,
- guint index,
- AtkObject *child)
- {
- if (!IS_MAI_OBJECT(child))
- return;
- static guint id = g_signal_lookup ("destroy", MAI_TYPE_ATK_OBJECT);
- g_signal_emit (child, id, 0);
- }
- static void
- UtilInterfaceInit(MaiUtilClass* klass)
- {
- AtkUtilClass *atk_class;
- gpointer data;
- data = g_type_class_peek(ATK_TYPE_UTIL);
- atk_class = ATK_UTIL_CLASS(data);
- // save gail function pointer
- gail_add_global_event_listener = atk_class->add_global_event_listener;
- gail_remove_global_event_listener = atk_class->remove_global_event_listener;
- gail_remove_key_event_listener = atk_class->remove_key_event_listener;
- gail_get_root = atk_class->get_root;
- atk_class->add_global_event_listener =
- mai_util_add_global_event_listener;
- atk_class->remove_global_event_listener =
- mai_util_remove_global_event_listener;
- atk_class->add_key_event_listener = mai_util_add_key_event_listener;
- atk_class->remove_key_event_listener = mai_util_remove_key_event_listener;
- atk_class->get_root = mai_util_get_root;
- atk_class->get_toolkit_name = mai_util_get_toolkit_name;
- atk_class->get_toolkit_version = mai_util_get_toolkit_version;
- sListener_list = g_hash_table_new_full(g_int_hash, g_int_equal, nullptr,
- _listener_info_destroy);
- // Keep track of added/removed windows.
- AtkObject *root = atk_get_root ();
- g_signal_connect (root, "children-changed::add", (GCallback) window_added, nullptr);
- g_signal_connect (root, "children-changed::remove", (GCallback) window_removed, nullptr);
- }
- }
- GType
- mai_util_get_type()
- {
- static GType type = 0;
- if (!type) {
- static const GTypeInfo tinfo = {
- sizeof(MaiUtilClass),
- (GBaseInitFunc) nullptr, /* base init */
- (GBaseFinalizeFunc) nullptr, /* base finalize */
- (GClassInitFunc) UtilInterfaceInit, /* class init */
- (GClassFinalizeFunc) nullptr, /* class finalize */
- nullptr, /* class data */
- sizeof(MaiUtil), /* instance size */
- 0, /* nb preallocs */
- (GInstanceInitFunc) nullptr, /* instance init */
- nullptr /* value table */
- };
- type = g_type_register_static(ATK_TYPE_UTIL,
- "MaiUtil", &tinfo, GTypeFlags(0));
- }
- return type;
- }
|