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"
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"
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
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;
50 add_listener (GSignalEmissionHook listener
,
51 const gchar
*object_type
,
53 const gchar
*hook_data
,
54 guint gail_listenerid
= 0)
60 type
= g_type_from_name(object_type
);
62 signal_id
= g_signal_lookup(signal
, type
);
64 MaiUtilListenerInfo
*listener_info
;
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
,
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
),
83 g_warning("Invalid signal type %s\n", signal
);
87 g_warning("Invalid object type %s\n", object_type
);
93 mai_util_add_global_event_listener(GSignalEmissionHook listener
,
94 const gchar
*event_type
)
99 split_string
= g_strsplit (event_type
, ":", 3);
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
107 gail_add_global_event_listener(listener
, event_type
);
110 rc
= add_listener (listener
, "MaiAtkObject", split_string
[1],
111 event_type
, gail_listenerid
);
114 rc
= add_listener (listener
, split_string
[1], split_string
[2],
117 g_strfreev(split_string
);
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
);
148 g_warning("Invalid listener hook_id %ld or signal_id %d\n",
149 listener_info
->hook_id
, listener_info
->signal_id
);
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",
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);
174 event
->type
= ATK_KEY_EVENT_PRESS
;
176 case GDK_KEY_RELEASE
:
177 event
->type
= ATK_KEY_EVENT_RELEASE
;
180 g_assert_not_reached ();
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
;
201 struct MaiKeyEventInfo
203 AtkKeyEventStruct
*key_event
;
207 union AtkKeySnoopFuncPointer
209 AtkKeySnoopFunc func_ptr
;
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
;
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;
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);
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
);
248 return (consumed
? 1 : 0);
251 static guint sKey_snooper_id
= 0;
254 mai_util_add_key_event_listener (AtkKeySnoopFunc listener
,
257 if (MOZ_UNLIKELY(!listener
))
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
++),
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
);
291 ApplicationAccessible
* app
= ApplicationAcc();
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?
299 return gail_get_root();
305 mai_util_get_toolkit_name()
311 mai_util_get_toolkit_version()
317 _listener_info_destroy(gpointer data
)
323 window_added (AtkObject
*atk_obj
,
327 if (!IS_MAI_OBJECT(child
))
330 static guint id
= g_signal_lookup ("create", MAI_TYPE_ATK_OBJECT
);
331 g_signal_emit (child
, id
, 0);
335 window_removed (AtkObject
*atk_obj
,
339 if (!IS_MAI_OBJECT(child
))
342 static guint id
= g_signal_lookup ("destroy", MAI_TYPE_ATK_OBJECT
);
343 g_signal_emit (child
, id
, 0);
347 UtilInterfaceInit(MaiUtilClass
* klass
)
349 AtkUtilClass
*atk_class
;
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);
383 static GType type
= 0;
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));