2 * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
4 * Licensed under the Flora License, Version 1.1 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://floralicense.org/license/
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "app_tracker.h"
18 #include "screen_reader_tts.h"
22 AtspiAccessible *base_root;
23 AtspiAccessible *root;
29 AppTrackerEventCB func;
30 AtspiAccessible *root;
34 #define APP_TRACKER_INVACTIVITY_TIMEOUT 200
36 static int _init_count;
38 static AtspiEventListener *_listener;
39 static AppTrackerEventCB _new_obj_highlighted_callback;
41 static int _is_descendant(AtspiAccessible * ancestor, AtspiAccessible * descendant)
47 if (ancestor == descendant)
50 while ((descendant = atspi_accessible_get_parent(descendant, NULL)) != NULL);
56 static Eina_Bool _object_has_modal_state(AtspiAccessible * obj)
61 Eina_Bool ret = EINA_FALSE;
63 AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
65 if (atspi_state_set_contains(ss, ATSPI_STATE_MODAL))
71 static Eina_Bool _object_has_showing_state(AtspiAccessible * obj)
76 Eina_Bool ret = EINA_FALSE;
78 AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
80 if (atspi_state_set_contains(ss, ATSPI_STATE_SHOWING))
86 static Eina_Bool _object_has_highlighted_state(AtspiAccessible * obj)
91 Eina_Bool ret = EINA_FALSE;
93 AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
95 if (atspi_state_set_contains(ss, ATSPI_STATE_HIGHLIGHTED))
101 static void _subtree_callbacks_call(SubTreeRootData * std)
105 EventCallbackData *ecd;
107 for (l = std->callbacks; l != NULL; l = l->next) {
109 ecd->func(std->root, ecd->user_data);
114 static gboolean _on_timeout_cb(gpointer user_data)
117 SubTreeRootData *std = user_data;
119 _subtree_callbacks_call(std);
126 static void _print_event_object_info(const AtspiEvent * event)
128 gchar *name = atspi_accessible_get_name(event->source, NULL), *role = atspi_accessible_get_role_name(event->source, NULL);
130 DEBUG("signal:%s, name: %s, role: %s, detail1:%i, detail2: %i", event->type, role, name, event->detail1, event->detail2);
135 static void _on_atspi_event_cb(const AtspiEvent * event)
138 SubTreeRootData *std;
142 if (!event->source) {
143 ERROR("empty event source");
147 if ((atspi_accessible_get_role(event->source, NULL) == ATSPI_ROLE_DESKTOP_FRAME)) {
151 _print_event_object_info(event);
153 if (!strcmp(event->type, "object:property-change:accessible-name") && _object_has_highlighted_state(event->source)) {
154 gchar *name = atspi_accessible_get_name(event->source, NULL);
155 DEBUG("New name for object, read:%s", name);
156 tts_speak (name, EINA_TRUE);
160 AtspiAccessible *new_highlighted_obj = NULL;
162 if (!strcmp(event->type, "object:state-changed:highlighted"))
163 new_highlighted_obj = event->source;
164 else if (!strcmp(event->type, "object:active-descendant-changed"))
165 new_highlighted_obj = atspi_accessible_get_child_at_index(event->source, event->detail1, NULL);
167 if (new_highlighted_obj && _new_obj_highlighted_callback && _object_has_highlighted_state(new_highlighted_obj)) {
168 DEBUG("HIGHLIGHTED OBJECT IS ABOUT TO CHANGE");
169 _new_obj_highlighted_callback(new_highlighted_obj, NULL);
170 g_object_unref(new_highlighted_obj);
171 new_highlighted_obj = NULL;
174 if (!strcmp("object:state-changed:showing", event->type) ||
175 !strcmp("object:state-changed:visible", event->type) ||
176 !strcmp("object:state-changed:defunct", event->type)) {
177 for (l = _roots; l != NULL; l = l->next) {
180 if (!_object_has_showing_state(std->root) && std->base_root) {
181 std->root = std->base_root;
182 std->base_root = NULL;
185 if (_is_descendant(std->root, event->source)) {
187 g_source_remove(std->timer);
188 DEBUG("Before Checking if modal is showing");
189 if (_object_has_modal_state(event->source)) {
190 DEBUG("Object is modal");
191 std->base_root = std->root;
192 std->root = event->source;
194 std->timer = g_timeout_add(APP_TRACKER_INVACTIVITY_TIMEOUT, _on_timeout_cb, std);
200 static int _app_tracker_init_internal(void)
203 _new_obj_highlighted_callback = NULL;
204 _listener = atspi_event_listener_new_simple(_on_atspi_event_cb, NULL);
206 atspi_event_listener_register(_listener, "object:state-changed:showing", NULL);
207 atspi_event_listener_register(_listener, "object:state-changed:visible", NULL);
208 atspi_event_listener_register(_listener, "object:state-changed:defunct", NULL);
209 atspi_event_listener_register(_listener, "object:state-changed:highlighted", NULL);
210 atspi_event_listener_register(_listener, "object:bounds-changed", NULL);
211 atspi_event_listener_register(_listener, "object:visible-data-changed", NULL);
212 atspi_event_listener_register(_listener, "object:active-descendant-changed", NULL);
213 atspi_event_listener_register(_listener, "object:property-change", NULL);
218 static void _free_callbacks(gpointer data)
223 static void _free_rootdata(gpointer data)
225 SubTreeRootData *std = data;
226 g_list_free_full(std->callbacks, _free_callbacks);
228 g_source_remove(std->timer);
232 static void _app_tracker_shutdown_internal(void)
234 atspi_event_listener_deregister(_listener, "object:state-changed:showing", NULL);
235 atspi_event_listener_deregister(_listener, "object:state-changed:visible", NULL);
236 atspi_event_listener_deregister(_listener, "object:state-changed:highlighted", NULL);
237 atspi_event_listener_deregister(_listener, "object:bounds-changed", NULL);
238 atspi_event_listener_deregister(_listener, "object:state-changed:defunct", NULL);
239 atspi_event_listener_deregister(_listener, "object:visible-data-changed", NULL);
240 atspi_event_listener_deregister(_listener, "object:active-descendant-changed", NULL);
241 atspi_event_listener_deregister(_listener, "object:property-change", NULL);
243 g_object_unref(_listener);
245 _new_obj_highlighted_callback = NULL;
246 g_list_free_full(_roots, _free_rootdata);
250 int app_tracker_init(void)
254 if (_app_tracker_init_internal())
256 return ++_init_count;
259 void app_tracker_shutdown(void)
261 if (_init_count == 1)
262 _app_tracker_shutdown_internal();
263 if (--_init_count < 0)
267 void app_tracker_callback_register(AtspiAccessible * app, AppTrackerEventCB cb, void *user_data)
270 SubTreeRootData *rd = NULL;
271 EventCallbackData *cd;
274 if (!_init_count || !cb)
277 for (l = _roots; l != NULL; l = l->next) {
279 if (((SubTreeRootData *) l->data)->root == app) {
286 rd = g_new(SubTreeRootData, 1);
288 rd->base_root = NULL;
289 rd->callbacks = NULL;
291 _roots = g_list_append(_roots, rd);
294 cd = g_new(EventCallbackData, 1);
296 cd->user_data = user_data;
298 rd->callbacks = g_list_append(rd->callbacks, cd);
302 void app_tracker_new_obj_highlighted_callback_register(AppTrackerEventCB cb)
304 _new_obj_highlighted_callback = cb;
307 void app_tracker_callback_unregister(AtspiAccessible * app, AppTrackerEventCB cb, void *user_data)
311 EventCallbackData *ecd;
312 SubTreeRootData *std = NULL;
314 for (l = _roots; l != NULL; l = l->next) {
315 if (((SubTreeRootData *) l->data)->root == app || ((SubTreeRootData *) l->data)->base_root == app) {
324 for (l = std->callbacks; l != NULL; l = l->next) {
326 if ((ecd->func == cb) && (ecd->user_data == user_data)) {
327 std->callbacks = g_list_delete_link(std->callbacks, l);
332 if (!std->callbacks) {
334 g_source_remove(std->timer);
335 _roots = g_list_remove(_roots, std);
340 void app_tracker_new_obj_highlighted_callback_unregister(AppTrackerEventCB cb)
342 _new_obj_highlighted_callback = NULL;