Bumping manifests a=b2g-bump
[gecko.git] / accessible / atk / UtilInterface.cpp
blob7dade78dfb745e4831424e8f6d3c708c403fbabf
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ApplicationAccessibleWrap.h"
8 #include "mozilla/Likely.h"
9 #include "nsAccessibilityService.h"
10 #include "nsMai.h"
12 #include <atk/atk.h>
13 #include <gtk/gtk.h>
14 #include <string.h>
16 using namespace mozilla;
17 using namespace mozilla::a11y;
19 typedef AtkUtil MaiUtil;
20 typedef AtkUtilClass MaiUtilClass;
22 #define MAI_VERSION MOZILLA_VERSION
23 #define MAI_NAME "Gecko"
25 extern "C" {
26 static guint (*gail_add_global_event_listener)(GSignalEmissionHook listener,
27 const gchar* event_type);
28 static void (*gail_remove_global_event_listener) (guint remove_listener);
29 static void (*gail_remove_key_event_listener) (guint remove_listener);
30 static AtkObject* (*gail_get_root)();
33 struct MaiUtilListenerInfo
35 gint key;
36 guint signal_id;
37 gulong hook_id;
38 // For window create/destory/minimize/maximize/restore/activate/deactivate
39 // events, we'll chain gail_util's add/remove_global_event_listener.
40 // So we store the listenerid returned by gail's add_global_event_listener
41 // in this structure to call gail's remove_global_event_listener later.
42 guint gail_listenerid;
45 static GHashTable* sListener_list = nullptr;
46 static gint sListener_idx = 1;
48 extern "C" {
49 static guint
50 add_listener (GSignalEmissionHook listener,
51 const gchar *object_type,
52 const gchar *signal,
53 const gchar *hook_data,
54 guint gail_listenerid = 0)
56 GType type;
57 guint signal_id;
58 gint rc = 0;
60 type = g_type_from_name(object_type);
61 if (type) {
62 signal_id = g_signal_lookup(signal, type);
63 if (signal_id > 0) {
64 MaiUtilListenerInfo *listener_info;
66 rc = sListener_idx;
68 listener_info = (MaiUtilListenerInfo *)
69 g_malloc(sizeof(MaiUtilListenerInfo));
70 listener_info->key = sListener_idx;
71 listener_info->hook_id =
72 g_signal_add_emission_hook(signal_id, 0, listener,
73 g_strdup(hook_data),
74 (GDestroyNotify)g_free);
75 listener_info->signal_id = signal_id;
76 listener_info->gail_listenerid = gail_listenerid;
78 g_hash_table_insert(sListener_list, &(listener_info->key),
79 listener_info);
80 sListener_idx++;
82 else {
83 g_warning("Invalid signal type %s\n", signal);
86 else {
87 g_warning("Invalid object type %s\n", object_type);
89 return rc;
92 static guint
93 mai_util_add_global_event_listener(GSignalEmissionHook listener,
94 const gchar *event_type)
96 guint rc = 0;
97 gchar **split_string;
99 split_string = g_strsplit (event_type, ":", 3);
101 if (split_string) {
102 if (!strcmp ("window", split_string[0])) {
103 guint gail_listenerid = 0;
104 if (gail_add_global_event_listener) {
105 // call gail's function to track gtk native window events
106 gail_listenerid =
107 gail_add_global_event_listener(listener, event_type);
110 rc = add_listener (listener, "MaiAtkObject", split_string[1],
111 event_type, gail_listenerid);
113 else {
114 rc = add_listener (listener, split_string[1], split_string[2],
115 event_type);
117 g_strfreev(split_string);
119 return rc;
122 static void
123 mai_util_remove_global_event_listener(guint remove_listener)
125 if (remove_listener > 0) {
126 MaiUtilListenerInfo *listener_info;
127 gint tmp_idx = remove_listener;
129 listener_info = (MaiUtilListenerInfo *)
130 g_hash_table_lookup(sListener_list, &tmp_idx);
132 if (listener_info != nullptr) {
133 if (gail_remove_global_event_listener &&
134 listener_info->gail_listenerid) {
135 gail_remove_global_event_listener(listener_info->gail_listenerid);
138 /* Hook id of 0 and signal id of 0 are invalid */
139 if (listener_info->hook_id != 0 && listener_info->signal_id != 0) {
140 /* Remove the emission hook */
141 g_signal_remove_emission_hook(listener_info->signal_id,
142 listener_info->hook_id);
144 /* Remove the element from the hash */
145 g_hash_table_remove(sListener_list, &tmp_idx);
147 else {
148 g_warning("Invalid listener hook_id %ld or signal_id %d\n",
149 listener_info->hook_id, listener_info->signal_id);
152 else {
153 // atk-bridge is initialized with gail (e.g. yelp)
154 // try gail_remove_global_event_listener
155 if (gail_remove_global_event_listener) {
156 return gail_remove_global_event_listener(remove_listener);
159 g_warning("No listener with the specified listener id %d",
160 remove_listener);
163 else {
164 g_warning("Invalid listener_id %d", remove_listener);
168 static AtkKeyEventStruct *
169 atk_key_event_from_gdk_event_key (GdkEventKey *key)
171 AtkKeyEventStruct *event = g_new0(AtkKeyEventStruct, 1);
172 switch (key->type) {
173 case GDK_KEY_PRESS:
174 event->type = ATK_KEY_EVENT_PRESS;
175 break;
176 case GDK_KEY_RELEASE:
177 event->type = ATK_KEY_EVENT_RELEASE;
178 break;
179 default:
180 g_assert_not_reached ();
181 return nullptr;
183 event->state = key->state;
184 event->keyval = key->keyval;
185 event->length = key->length;
186 if (key->string && key->string [0] &&
187 (key->state & GDK_CONTROL_MASK ||
188 g_unichar_isgraph (g_utf8_get_char (key->string)))) {
189 event->string = key->string;
191 else if (key->type == GDK_KEY_PRESS ||
192 key->type == GDK_KEY_RELEASE) {
193 event->string = gdk_keyval_name (key->keyval);
195 event->keycode = key->hardware_keycode;
196 event->timestamp = key->time;
198 return event;
201 struct MaiKeyEventInfo
203 AtkKeyEventStruct *key_event;
204 gpointer func_data;
207 union AtkKeySnoopFuncPointer
209 AtkKeySnoopFunc func_ptr;
210 gpointer data;
213 static gboolean
214 notify_hf(gpointer key, gpointer value, gpointer data)
216 MaiKeyEventInfo *info = (MaiKeyEventInfo *)data;
217 AtkKeySnoopFuncPointer atkKeySnoop;
218 atkKeySnoop.data = value;
219 return (atkKeySnoop.func_ptr)(info->key_event, info->func_data) ? TRUE : FALSE;
222 static void
223 insert_hf(gpointer key, gpointer value, gpointer data)
225 GHashTable *new_table = (GHashTable *) data;
226 g_hash_table_insert (new_table, key, value);
229 static GHashTable* sKey_listener_list = nullptr;
231 static gint
232 mai_key_snooper(GtkWidget *the_widget, GdkEventKey *event, gpointer func_data)
234 /* notify each AtkKeySnoopFunc in turn... */
236 MaiKeyEventInfo *info = g_new0(MaiKeyEventInfo, 1);
237 gint consumed = 0;
238 if (sKey_listener_list) {
239 GHashTable *new_hash = g_hash_table_new(nullptr, nullptr);
240 g_hash_table_foreach (sKey_listener_list, insert_hf, new_hash);
241 info->key_event = atk_key_event_from_gdk_event_key (event);
242 info->func_data = func_data;
243 consumed = g_hash_table_foreach_steal (new_hash, notify_hf, info);
244 g_hash_table_destroy (new_hash);
245 g_free(info->key_event);
247 g_free(info);
248 return (consumed ? 1 : 0);
251 static guint sKey_snooper_id = 0;
253 static guint
254 mai_util_add_key_event_listener (AtkKeySnoopFunc listener,
255 gpointer data)
257 if (MOZ_UNLIKELY(!listener))
258 return 0;
260 static guint key=0;
262 if (!sKey_listener_list) {
263 sKey_listener_list = g_hash_table_new(nullptr, nullptr);
264 sKey_snooper_id = gtk_key_snooper_install(mai_key_snooper, data);
266 AtkKeySnoopFuncPointer atkKeySnoop;
267 atkKeySnoop.func_ptr = listener;
268 g_hash_table_insert(sKey_listener_list, GUINT_TO_POINTER (key++),
269 atkKeySnoop.data);
270 return key;
273 static void
274 mai_util_remove_key_event_listener (guint remove_listener)
276 if (!sKey_listener_list) {
277 // atk-bridge is initialized with gail (e.g. yelp)
278 // try gail_remove_key_event_listener
279 return gail_remove_key_event_listener(remove_listener);
282 g_hash_table_remove(sKey_listener_list, GUINT_TO_POINTER (remove_listener));
283 if (g_hash_table_size(sKey_listener_list) == 0) {
284 gtk_key_snooper_remove(sKey_snooper_id);
288 static AtkObject*
289 mai_util_get_root()
291 ApplicationAccessible* app = ApplicationAcc();
292 if (app)
293 return app->GetAtkObject();
295 // We've shutdown, try to use gail instead
296 // (to avoid assert in spi_atk_tidy_windows())
297 // XXX tbsaunde then why didn't we replace the gail atk_util impl?
298 if (gail_get_root)
299 return gail_get_root();
301 return nullptr;
304 static const gchar*
305 mai_util_get_toolkit_name()
307 return MAI_NAME;
310 static const gchar*
311 mai_util_get_toolkit_version()
313 return MAI_VERSION;
316 static void
317 _listener_info_destroy(gpointer data)
319 g_free(data);
322 static void
323 window_added (AtkObject *atk_obj,
324 guint index,
325 AtkObject *child)
327 if (!IS_MAI_OBJECT(child))
328 return;
330 static guint id = g_signal_lookup ("create", MAI_TYPE_ATK_OBJECT);
331 g_signal_emit (child, id, 0);
334 static void
335 window_removed (AtkObject *atk_obj,
336 guint index,
337 AtkObject *child)
339 if (!IS_MAI_OBJECT(child))
340 return;
342 static guint id = g_signal_lookup ("destroy", MAI_TYPE_ATK_OBJECT);
343 g_signal_emit (child, id, 0);
346 static void
347 UtilInterfaceInit(MaiUtilClass* klass)
349 AtkUtilClass *atk_class;
350 gpointer data;
352 data = g_type_class_peek(ATK_TYPE_UTIL);
353 atk_class = ATK_UTIL_CLASS(data);
355 // save gail function pointer
356 gail_add_global_event_listener = atk_class->add_global_event_listener;
357 gail_remove_global_event_listener = atk_class->remove_global_event_listener;
358 gail_remove_key_event_listener = atk_class->remove_key_event_listener;
359 gail_get_root = atk_class->get_root;
361 atk_class->add_global_event_listener =
362 mai_util_add_global_event_listener;
363 atk_class->remove_global_event_listener =
364 mai_util_remove_global_event_listener;
365 atk_class->add_key_event_listener = mai_util_add_key_event_listener;
366 atk_class->remove_key_event_listener = mai_util_remove_key_event_listener;
367 atk_class->get_root = mai_util_get_root;
368 atk_class->get_toolkit_name = mai_util_get_toolkit_name;
369 atk_class->get_toolkit_version = mai_util_get_toolkit_version;
371 sListener_list = g_hash_table_new_full(g_int_hash, g_int_equal, nullptr,
372 _listener_info_destroy);
373 // Keep track of added/removed windows.
374 AtkObject *root = atk_get_root ();
375 g_signal_connect (root, "children-changed::add", (GCallback) window_added, nullptr);
376 g_signal_connect (root, "children-changed::remove", (GCallback) window_removed, nullptr);
380 GType
381 mai_util_get_type()
383 static GType type = 0;
385 if (!type) {
386 static const GTypeInfo tinfo = {
387 sizeof(MaiUtilClass),
388 (GBaseInitFunc) nullptr, /* base init */
389 (GBaseFinalizeFunc) nullptr, /* base finalize */
390 (GClassInitFunc) UtilInterfaceInit, /* class init */
391 (GClassFinalizeFunc) nullptr, /* class finalize */
392 nullptr, /* class data */
393 sizeof(MaiUtil), /* instance size */
394 0, /* nb preallocs */
395 (GInstanceInitFunc) nullptr, /* instance init */
396 nullptr /* value table */
399 type = g_type_register_static(ATK_TYPE_UTIL,
400 "MaiUtil", &tinfo, GTypeFlags(0));
402 return type;