[LICENSE] change to Flora-1.1 license
[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  * This file is a modified version of BSD licensed file and
5  * licensed under the Flora License, Version 1.1 (the License);
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://floralicense.org/license/
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an AS IS BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * Please, see the LICENSE file for the original copyright owner and
18  * license.
19  */
20
21 #include "app_tracker.h"
22 #include "logger.h"
23
24 typedef struct
25 {
26    AtspiAccessible *base_root;
27    AtspiAccessible *root;
28    GList *callbacks;
29    guint timer;
30 } SubTreeRootData;
31
32 typedef struct
33 {
34    AppTrackerEventCB func;
35    AtspiAccessible *root;
36    void *user_data;
37 } EventCallbackData;
38
39 #define APP_TRACKER_INVACTIVITY_TIMEOUT 200
40
41 static int _init_count;
42 static GList *_roots;
43 static AtspiEventListener *_listener;
44
45 static int
46 _is_descendant(AtspiAccessible *ancestor, AtspiAccessible *descendant)
47 {
48    return 1;
49
50 #if 0
51    do
52       {
53          if (ancestor == descendant) return 1;
54       }
55    while ((descendant = atspi_accessible_get_parent(descendant, NULL)) != NULL);
56
57    return 0;
58 #endif
59 }
60
61
62 static Eina_Bool _object_has_modal_state(AtspiAccessible *obj)
63 {
64    if (!obj)
65       return EINA_FALSE;
66
67    Eina_Bool ret = EINA_FALSE;
68
69    AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
70
71    if (atspi_state_set_contains(ss, ATSPI_STATE_MODAL))
72       ret = EINA_TRUE;
73    g_object_unref(ss);
74    return ret;
75 }
76
77 static Eina_Bool _object_has_showing_state(AtspiAccessible *obj)
78 {
79    if (!obj)
80       return EINA_FALSE;
81
82    Eina_Bool ret = EINA_FALSE;
83
84    AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
85
86    if (atspi_state_set_contains(ss, ATSPI_STATE_SHOWING))
87       ret = EINA_TRUE;
88    g_object_unref(ss);
89    return ret;
90 }
91
92 static void
93 _subtree_callbacks_call(SubTreeRootData *std)
94 {
95    DEBUG("START");
96    GList *l;
97    EventCallbackData *ecd;
98
99    for (l = std->callbacks; l != NULL; l = l->next)
100       {
101          ecd = l->data;
102          ecd->func(std->root, ecd->user_data);
103       }
104    DEBUG("END");
105 }
106
107 static gboolean
108 _on_timeout_cb(gpointer user_data)
109 {
110    DEBUG("START");
111    SubTreeRootData *std = user_data;
112
113    _subtree_callbacks_call(std);
114
115    std->timer = 0;
116    DEBUG("END");
117    return FALSE;
118 }
119 static void _print_event_object_info(const AtspiEvent *event)
120 {
121     gchar *name = atspi_accessible_get_name(event->source, NULL),
122           *role = atspi_accessible_get_role_name(event->source, NULL);
123
124     DEBUG("signal:%s, name: %s, role: %s, detail1:%i, detail2: %i", event->type, role, name, event->detail1, event->detail2);
125     g_free(name);
126     g_free(role);
127 }
128
129 static void
130 _on_atspi_event_cb(const AtspiEvent *event)
131 {
132    DEBUG("START");
133    GList *l;
134    SubTreeRootData *std;
135
136    if (!event) return;
137    if (!event->source)
138       {
139          ERROR("empty event source");
140          return;
141       }
142
143    if ((atspi_accessible_get_role(event->source, NULL) == ATSPI_ROLE_DESKTOP_FRAME))
144       {
145          return;
146       }
147
148    _print_event_object_info(event);
149
150    for (l = _roots; l != NULL; l = l->next)
151       {
152          std = l->data;
153
154          if (!_object_has_showing_state(std->root) && std->base_root)
155             {
156                std->root = std->base_root;
157                std->base_root = NULL;
158             }
159
160          if (_is_descendant(std->root, event->source))
161             {
162                if (std->timer)
163                   g_source_remove(std->timer);
164
165                DEBUG("Before Checking if modal is showing");
166
167                if (!strcmp("object:state-changed:showing",event->type))
168                   {
169
170                      DEBUG("Object is showing");
171
172                      if (_object_has_modal_state(event->source))
173                         {
174                            DEBUG("Object is modal");
175
176                            std->base_root = std->root;
177                            std->root = event->source;
178                         }
179                   }
180                std->timer = g_timeout_add(APP_TRACKER_INVACTIVITY_TIMEOUT, _on_timeout_cb, std);
181             }
182       }
183 }
184
185 static int
186 _app_tracker_init_internal(void)
187 {
188    DEBUG("START");
189    _listener = atspi_event_listener_new_simple(_on_atspi_event_cb, NULL);
190
191    atspi_event_listener_register(_listener, "object:state-changed:showing", NULL);
192    atspi_event_listener_register(_listener, "object:state-changed:visible", NULL);
193    atspi_event_listener_register(_listener, "object:state-changed:defunct", NULL);
194    atspi_event_listener_register(_listener, "object:bounds-changed", NULL);
195    atspi_event_listener_register(_listener, "object:visible-data-changed", NULL);
196
197    return 0;
198 }
199
200 static void
201 _free_callbacks(gpointer data)
202 {
203    g_free(data);
204 }
205
206 static void
207 _free_rootdata(gpointer data)
208 {
209    SubTreeRootData *std = data;
210    g_list_free_full(std->callbacks, _free_callbacks);
211    if (std->timer)
212       g_source_remove(std->timer);
213    g_free(std);
214 }
215
216 static void
217 _app_tracker_shutdown_internal(void)
218 {
219    atspi_event_listener_deregister(_listener, "object:state-changed:showing", NULL);
220    atspi_event_listener_deregister(_listener, "object:state-changed:visible", NULL);
221    atspi_event_listener_deregister(_listener, "object:bounds-changed", NULL);
222    atspi_event_listener_deregister(_listener, "object:state-changed:defunct", NULL);
223    atspi_event_listener_deregister(_listener, "object:visible-data-changed", NULL);
224
225    g_object_unref(_listener);
226    _listener = NULL;
227
228    g_list_free_full(_roots, _free_rootdata);
229    _roots = NULL;
230 }
231
232 int app_tracker_init(void)
233 {
234    DEBUG("START");
235    if (!_init_count)
236       if (_app_tracker_init_internal()) return -1;
237    return ++_init_count;
238 }
239
240 void app_tracker_shutdown(void)
241 {
242    if (_init_count == 1)
243       _app_tracker_shutdown_internal();
244    if (--_init_count < 0) _init_count = 0;
245 }
246
247 void app_tracker_callback_register(AtspiAccessible *app, AppTrackerEventCB cb, void *user_data)
248 {
249    DEBUG("START");
250    SubTreeRootData *rd = NULL;
251    EventCallbackData *cd;
252    GList *l;
253
254    if (!_init_count || !cb)
255       return;
256
257    for (l = _roots; l != NULL; l = l->next)
258       {
259          rd = l->data;
260          if (((SubTreeRootData*)l->data)->root == app)
261             {
262                rd = l->data;
263                break;
264             }
265       }
266
267    if (!rd)
268       {
269          rd = g_new(SubTreeRootData, 1);
270          rd->root = app;
271          rd->base_root = NULL;
272          rd->callbacks = NULL;
273          rd->timer = 0;
274          _roots = g_list_append(_roots, rd);
275       }
276
277    cd = g_new(EventCallbackData, 1);
278    cd->func = cb;
279    cd->user_data = user_data;
280
281    rd->callbacks = g_list_append(rd->callbacks, cd);
282    DEBUG("END");
283 }
284
285 void app_tracker_callback_unregister(AtspiAccessible *app, AppTrackerEventCB cb, void *user_data)
286 {
287    DEBUG("START");
288    GList *l;
289    EventCallbackData *ecd;
290    SubTreeRootData *std = NULL;
291
292    for (l = _roots; l != NULL; l = l->next)
293       {
294          if (((SubTreeRootData*)l->data)->root == app || ((SubTreeRootData*)l->data)->base_root == app)
295             {
296                std = l->data;
297                break;
298             }
299       }
300
301    if (!std) return;
302
303    for (l = std->callbacks; l != NULL; l = l->next)
304       {
305          ecd = l->data;
306          if ((ecd->func == cb) && (ecd->user_data == user_data))
307             {
308                std->callbacks = g_list_delete_link(std->callbacks, l);
309                break;
310             }
311       }
312
313    if (!std->callbacks)
314       {
315          if (std->timer) g_source_remove(std->timer);
316          _roots = g_list_remove(_roots, std);
317          g_free(std);
318       }
319 }