mobile-lua: add additional LABELLED_BY relation
[profile/tv/apps/native/screen-reader.git] / src / app_tracker.c
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
3  *
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
7  *
8  *     http://floralicense.org/license/
9  *
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.
15  */
16
17 #include "app_tracker.h"
18 #include "screen_reader_tts.h"
19 #include "logger.h"
20
21 typedef struct {
22         AtspiAccessible *base_root;
23         AtspiAccessible *root;
24         GList *callbacks;
25         guint timer;
26 } SubTreeRootData;
27
28 typedef struct {
29         AppTrackerEventCB func;
30         AtspiAccessible *root;
31         void *user_data;
32 } EventCallbackData;
33
34 #define APP_TRACKER_INVACTIVITY_TIMEOUT 200
35
36 static int _init_count;
37 static GList *_roots;
38 static AtspiEventListener *_listener;
39 static AppTrackerEventCB _new_obj_highlighted_callback;
40
41 static int _is_descendant(AtspiAccessible * ancestor, AtspiAccessible * descendant)
42 {
43         return 1;
44
45 #if 0
46         do {
47                 if (ancestor == descendant)
48                         return 1;
49         }
50         while ((descendant = atspi_accessible_get_parent(descendant, NULL)) != NULL);
51
52         return 0;
53 #endif
54 }
55
56 static Eina_Bool _object_has_modal_state(AtspiAccessible * obj)
57 {
58         if (!obj)
59                 return EINA_FALSE;
60
61         Eina_Bool ret = EINA_FALSE;
62
63         AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
64
65         if (atspi_state_set_contains(ss, ATSPI_STATE_MODAL))
66                 ret = EINA_TRUE;
67         g_object_unref(ss);
68         return ret;
69 }
70
71 static Eina_Bool _object_has_showing_state(AtspiAccessible * obj)
72 {
73         if (!obj)
74                 return EINA_FALSE;
75
76         Eina_Bool ret = EINA_FALSE;
77
78         AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
79
80         if (atspi_state_set_contains(ss, ATSPI_STATE_SHOWING))
81                 ret = EINA_TRUE;
82         g_object_unref(ss);
83         return ret;
84 }
85
86 static Eina_Bool _object_has_highlighted_state(AtspiAccessible * obj)
87 {
88         if (!obj)
89                 return EINA_FALSE;
90
91         Eina_Bool ret = EINA_FALSE;
92
93         AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
94
95         if (atspi_state_set_contains(ss, ATSPI_STATE_HIGHLIGHTED))
96                 ret = EINA_TRUE;
97         g_object_unref(ss);
98         return ret;
99 }
100
101 static void _subtree_callbacks_call(SubTreeRootData * std)
102 {
103         DEBUG("START");
104         GList *l;
105         EventCallbackData *ecd;
106
107         for (l = std->callbacks; l != NULL; l = l->next) {
108                 ecd = l->data;
109                 ecd->func(std->root, ecd->user_data);
110         }
111         DEBUG("END");
112 }
113
114 static gboolean _on_timeout_cb(gpointer user_data)
115 {
116         DEBUG("START");
117         SubTreeRootData *std = user_data;
118
119         _subtree_callbacks_call(std);
120
121         std->timer = 0;
122         DEBUG("END");
123         return FALSE;
124 }
125
126 static void _print_event_object_info(const AtspiEvent * event)
127 {
128         gchar *name = atspi_accessible_get_name(event->source, NULL), *role = atspi_accessible_get_role_name(event->source, NULL);
129
130         DEBUG("signal:%s, name: %s, role: %s, detail1:%i, detail2: %i", event->type, role, name, event->detail1, event->detail2);
131         g_free(name);
132         g_free(role);
133 }
134
135 static void _on_atspi_event_cb(const AtspiEvent * event)
136 {
137         GList *l;
138         SubTreeRootData *std;
139
140         if (!event)
141                 return;
142         if (!event->source) {
143                 ERROR("empty event source");
144                 return;
145         }
146
147         if ((atspi_accessible_get_role(event->source, NULL) == ATSPI_ROLE_DESKTOP_FRAME)) {
148                 return;
149         }
150
151         _print_event_object_info(event);
152
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);
157                 g_free(name);
158                 return;
159         }
160         AtspiAccessible *new_highlighted_obj = NULL;
161
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);
166
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;
172         }
173
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) {
178                         std = l->data;
179
180                         if (!_object_has_showing_state(std->root) && std->base_root) {
181                                 std->root = std->base_root;
182                                 std->base_root = NULL;
183                         }
184
185                         if (_is_descendant(std->root, event->source)) {
186                                 if (std->timer)
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;
193                                         }
194                                 std->timer = g_timeout_add(APP_TRACKER_INVACTIVITY_TIMEOUT, _on_timeout_cb, std);
195                         }
196                 }
197         }
198 }
199
200 static int _app_tracker_init_internal(void)
201 {
202         DEBUG("START");
203         _new_obj_highlighted_callback = NULL;
204         _listener = atspi_event_listener_new_simple(_on_atspi_event_cb, NULL);
205
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);
214
215         return 0;
216 }
217
218 static void _free_callbacks(gpointer data)
219 {
220         g_free(data);
221 }
222
223 static void _free_rootdata(gpointer data)
224 {
225         SubTreeRootData *std = data;
226         g_list_free_full(std->callbacks, _free_callbacks);
227         if (std->timer)
228                 g_source_remove(std->timer);
229         g_free(std);
230 }
231
232 static void _app_tracker_shutdown_internal(void)
233 {
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);
242
243         g_object_unref(_listener);
244         _listener = NULL;
245         _new_obj_highlighted_callback = NULL;
246         g_list_free_full(_roots, _free_rootdata);
247         _roots = NULL;
248 }
249
250 int app_tracker_init(void)
251 {
252         DEBUG("START");
253         if (!_init_count)
254                 if (_app_tracker_init_internal())
255                         return -1;
256         return ++_init_count;
257 }
258
259 void app_tracker_shutdown(void)
260 {
261         if (_init_count == 1)
262                 _app_tracker_shutdown_internal();
263         if (--_init_count < 0)
264                 _init_count = 0;
265 }
266
267 void app_tracker_callback_register(AtspiAccessible * app, AppTrackerEventCB cb, void *user_data)
268 {
269         DEBUG("START");
270         SubTreeRootData *rd = NULL;
271         EventCallbackData *cd;
272         GList *l;
273
274         if (!_init_count || !cb)
275                 return;
276
277         for (l = _roots; l != NULL; l = l->next) {
278                 rd = l->data;
279                 if (((SubTreeRootData *) l->data)->root == app) {
280                         rd = l->data;
281                         break;
282                 }
283         }
284
285         if (!rd) {
286                 rd = g_new(SubTreeRootData, 1);
287                 rd->root = app;
288                 rd->base_root = NULL;
289                 rd->callbacks = NULL;
290                 rd->timer = 0;
291                 _roots = g_list_append(_roots, rd);
292         }
293
294         cd = g_new(EventCallbackData, 1);
295         cd->func = cb;
296         cd->user_data = user_data;
297
298         rd->callbacks = g_list_append(rd->callbacks, cd);
299         DEBUG("END");
300 }
301
302 void app_tracker_new_obj_highlighted_callback_register(AppTrackerEventCB cb)
303 {
304         _new_obj_highlighted_callback = cb;
305 }
306
307 void app_tracker_callback_unregister(AtspiAccessible * app, AppTrackerEventCB cb, void *user_data)
308 {
309         DEBUG("START");
310         GList *l;
311         EventCallbackData *ecd;
312         SubTreeRootData *std = NULL;
313
314         for (l = _roots; l != NULL; l = l->next) {
315                 if (((SubTreeRootData *) l->data)->root == app || ((SubTreeRootData *) l->data)->base_root == app) {
316                         std = l->data;
317                         break;
318                 }
319         }
320
321         if (!std)
322                 return;
323
324         for (l = std->callbacks; l != NULL; l = l->next) {
325                 ecd = l->data;
326                 if ((ecd->func == cb) && (ecd->user_data == user_data)) {
327                         std->callbacks = g_list_delete_link(std->callbacks, l);
328                         break;
329                 }
330         }
331
332         if (!std->callbacks) {
333                 if (std->timer)
334                         g_source_remove(std->timer);
335                 _roots = g_list_remove(_roots, std);
336                 g_free(std);
337         }
338 }
339
340 void app_tracker_new_obj_highlighted_callback_unregister(AppTrackerEventCB cb)
341 {
342         _new_obj_highlighted_callback = NULL;
343 }