Update SMACK, Fix a crash of terminating sequence, etc, ...
[apps/livebox/data-provider-master.git] / src / xmonitor.c
1 /*
2  * Copyright 2013  Samsung Electronics Co., Ltd
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 <stdio.h>
18 #include <sys/ipc.h>
19 #include <sys/shm.h>
20 #include <assert.h>
21 #include <errno.h>
22 #include <malloc.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26
27 #include <Evas.h>
28 #include <Ecore_X.h>
29 #include <Ecore.h>
30
31 #include <sqlite3.h>
32
33 #include <gio/gio.h>
34 #include <dlog.h>
35 #include <livebox-errno.h>
36
37 #include "conf.h"
38 #include "debug.h"
39 #include "client_life.h"
40 #include "slave_life.h"
41 #include "main.h"
42 #include "util.h"
43 #include "setting.h"
44 #include "xmonitor.h"
45
46 int errno;
47
48 struct event_item {
49         int (*cb)(void *user_data);
50         void *user_data;
51 };
52
53 static struct info {
54         Ecore_Event_Handler *create_handler;
55         Ecore_Event_Handler *destroy_handler;
56         Ecore_Event_Handler *client_handler;
57
58         Eina_List *pause_list;
59         Eina_List *resume_list;
60
61         int paused;
62 } s_info = {
63         .create_handler = NULL,
64         .destroy_handler = NULL,
65         .client_handler = NULL,
66
67         .pause_list = NULL,
68         .resume_list = NULL,
69
70         .paused = 1, /*!< The provider is treated as paused process when it is launched */
71 };
72
73 static inline void touch_paused_file(void)
74 {
75         int fd;
76         fd = creat(PAUSED_FILE, 0644);
77         if (fd >= 0) {
78                 if (close(fd) < 0)
79                         ErrPrint("close: %s\n", strerror(errno));
80         } else {
81                 ErrPrint("Create .live.paused: %s\n", strerror(errno));
82         }
83 }
84
85 static inline void remove_paused_file(void)
86 {
87         if (unlink(PAUSED_FILE) < 0)
88                 ErrPrint("Unlink .live.paused: %s\n", strerror(errno));
89 }
90
91 static inline int get_pid(Ecore_X_Window win)
92 {
93         int pid;
94         Ecore_X_Atom atom;
95         unsigned char *in_pid;
96         int num;
97
98         atom = ecore_x_atom_get("X_CLIENT_PID");
99         if (ecore_x_window_prop_property_get(win, atom, ECORE_X_ATOM_CARDINAL,
100                                 sizeof(int), &in_pid, &num) == EINA_FALSE) {
101                 if (ecore_x_netwm_pid_get(win, &pid) == EINA_FALSE) {
102                         ErrPrint("Failed to get PID from a window 0x%X\n", win);
103                         return LB_STATUS_ERROR_INVALID;
104                 }
105         } else if (in_pid) {
106                 pid = *(int *)in_pid;
107                 DbgFree(in_pid);
108         } else {
109                 ErrPrint("Failed to get PID\n");
110                 return LB_STATUS_ERROR_INVALID;
111         }
112
113         return pid;
114 }
115
116 static Eina_Bool create_cb(void *data, int type, void *event)
117 {
118         Ecore_X_Event_Window_Create *info = event;
119         ecore_x_window_client_sniff(info->win);
120         return ECORE_CALLBACK_PASS_ON;
121 }
122
123 static Eina_Bool destroy_cb(void *data, int type, void *event)
124 {
125         // Ecore_X_Event_Window_Destroy *info = event;
126         return ECORE_CALLBACK_PASS_ON;
127 }
128
129 HAPI void xmonitor_handle_state_changes(void)
130 {
131         int paused;
132         Eina_List *l;
133         struct event_item *item;
134
135         paused = client_is_all_paused() || setting_is_lcd_off();
136         if (s_info.paused == paused)
137                 return;
138
139         s_info.paused = paused;
140
141         if (s_info.paused) {
142                 EINA_LIST_FOREACH(s_info.pause_list, l, item) {
143                         if (item->cb)
144                                 item->cb(item->user_data);
145                 }
146
147                 touch_paused_file();
148
149                 sqlite3_release_memory(SQLITE_FLUSH_MAX);
150                 malloc_trim(0);
151         } else {
152                 remove_paused_file();
153
154                 EINA_LIST_FOREACH(s_info.resume_list, l, item) {
155                         if (item->cb)
156                                 item->cb(item->user_data);
157                 }
158         }
159 }
160
161 HAPI int xmonitor_update_state(int target_pid)
162 {
163         Ecore_X_Window win;
164         struct client_node *client;
165         int pid;
166
167         if (!USE_XMONITOR)
168                 return LB_STATUS_SUCCESS;
169
170         win = ecore_x_window_focus_get();
171
172         pid = get_pid(win);
173         if (pid <= 0) {
174                 DbgPrint("Focused window has no PID %X\n", win);
175                 client = client_find_by_pid(target_pid);
176                 if (client) {
177                         DbgPrint("Client window has no focus now\n");
178                         client_paused(client);
179                 }
180                 return LB_STATUS_ERROR_NOT_EXIST;
181         }
182
183         client = client_find_by_pid(pid);
184         if (!client) {
185                 DbgPrint("Client %d is not registered yet\n", pid);
186                 client = client_find_by_pid(target_pid);
187                 if (client) {
188                         DbgPrint("Client window has no focus now\n");
189                         client_paused(client);
190                 }
191                 return LB_STATUS_ERROR_INVALID;
192         }
193
194         if (target_pid != pid) {
195                 DbgPrint("Client is paused\n");
196                 client_paused(client);
197         } else {
198                 DbgPrint("Client is resumed\n");
199                 client_resumed(client);
200         }
201
202         xmonitor_handle_state_changes();
203         return LB_STATUS_SUCCESS;
204 }
205
206 static Eina_Bool client_cb(void *data, int type, void *event)
207 {
208         Ecore_X_Event_Client_Message *info = event;
209         struct client_node *client;
210         char *name;
211         int pid;
212
213         pid = get_pid(info->win);
214         if (pid <= 0)
215                 return ECORE_CALLBACK_PASS_ON;
216
217         client = client_find_by_pid(pid);
218         if (!client)
219                 return ECORE_CALLBACK_PASS_ON;
220
221         name = ecore_x_atom_name_get(info->message_type);
222         if (!name)
223                 return ECORE_CALLBACK_PASS_ON;
224
225         if (!strcmp(name, "_X_ILLUME_DEACTIVATE_WINDOW")) {
226                 DbgPrint("PAUSE EVENT\n");
227                 xmonitor_pause(client);
228         } else if (!strcmp(name, "_X_ILLUME_ACTIVATE_WINDOW")) {
229                 DbgPrint("RESUME EVENT\n");
230                 xmonitor_resume(client);
231         } else {
232                 /* ignore event */
233         }
234
235         DbgFree(name);
236         return ECORE_CALLBACK_PASS_ON;
237 }
238
239 static inline void sniff_all_windows(void)
240 {
241         Ecore_X_Window root;
242         Ecore_X_Window ret;
243         struct stack_item *new_item;
244         struct stack_item *item;
245         Eina_List *win_stack;
246         //int pid;
247         struct stack_item {
248                 Ecore_X_Window *wins;
249                 int nr_of_wins;
250                 int i;
251         };
252
253         root = ecore_x_window_root_first_get();
254         ecore_x_window_sniff(root);
255
256         new_item = malloc(sizeof(*new_item));
257         if (!new_item) {
258                 ErrPrint("Error(%s)\n", strerror(errno));
259                 return;
260         }
261
262         new_item->nr_of_wins = 0;
263         new_item->wins =
264                 ecore_x_window_children_get(root, &new_item->nr_of_wins);
265         new_item->i = 0;
266
267         win_stack = NULL;
268
269         if (new_item->wins)
270                 win_stack = eina_list_append(win_stack, new_item);
271         else
272                 DbgFree(new_item);
273
274         while ((item = eina_list_nth(win_stack, 0))) {
275                 win_stack = eina_list_remove(win_stack, item);
276
277                 if (!item->wins) {
278                         DbgFree(item);
279                         continue;
280                 }
281
282                 while (item->i < item->nr_of_wins) {
283                         ret = item->wins[item->i];
284
285                         /*
286                          * Now we don't need to care about visibility of window,
287                          * just check whether it is registered or not.
288                          * (ecore_x_window_visible_get(ret))
289                          */
290                         ecore_x_window_client_sniff(ret);
291
292                         new_item = malloc(sizeof(*new_item));
293                         if (!new_item) {
294                                 ErrPrint("Error %s\n", strerror(errno));
295                                 item->i++;
296                                 continue;
297                         }
298
299                         new_item->i = 0;
300                         new_item->nr_of_wins = 0;
301                         new_item->wins =
302                                 ecore_x_window_children_get(ret,
303                                                         &new_item->nr_of_wins);
304                         if (new_item->wins) {
305                                 win_stack =
306                                         eina_list_append(win_stack, new_item);
307                         } else {
308                                 DbgFree(new_item);
309                         }
310
311                         item->i++;
312                 }
313
314                 DbgFree(item->wins);
315                 DbgFree(item);
316         }
317
318         return;
319 }
320
321 HAPI int xmonitor_pause(struct client_node *client)
322 {
323         DbgPrint("%d is paused\n", client_pid(client));
324         client_paused(client);
325         xmonitor_handle_state_changes();
326         return LB_STATUS_SUCCESS;
327 }
328
329 HAPI int xmonitor_resume(struct client_node *client)
330 {
331         DbgPrint("%d is resumed\n", client_pid(client));
332         client_resumed(client);
333         xmonitor_handle_state_changes();
334         return LB_STATUS_SUCCESS;
335 }
336
337 static inline void disable_xmonitor(void)
338 {
339         ecore_event_handler_del(s_info.create_handler);
340         ecore_event_handler_del(s_info.destroy_handler);
341         ecore_event_handler_del(s_info.client_handler);
342
343         s_info.create_handler = NULL;
344         s_info.destroy_handler = NULL;
345         s_info.client_handler = NULL;
346 }
347
348 static inline int enable_xmonitor(void)
349 {
350         if (ecore_x_composite_query() == EINA_FALSE)
351                 DbgPrint("====> COMPOSITOR IS NOT ENABLED\n");
352
353         s_info.create_handler =
354                 ecore_event_handler_add(ECORE_X_EVENT_WINDOW_CREATE,
355                                                         create_cb, NULL);
356         if (!s_info.create_handler) {
357                 ErrPrint("Failed to add create event handler\n");
358                 return LB_STATUS_ERROR_FAULT;
359         }
360
361         s_info.destroy_handler =
362                 ecore_event_handler_add(ECORE_X_EVENT_WINDOW_DESTROY,
363                                                         destroy_cb, NULL);
364         if (!s_info.create_handler) {
365                 ErrPrint("Failed to add destroy event handler\n");
366                 ecore_event_handler_del(s_info.create_handler);
367                 s_info.create_handler = NULL;
368                 return LB_STATUS_ERROR_FAULT;
369         }
370
371         s_info.client_handler =
372                 ecore_event_handler_add(ECORE_X_EVENT_CLIENT_MESSAGE,
373                                                         client_cb, NULL);
374         if (!s_info.client_handler) {
375                 ErrPrint("Failed to add focus out event handler\n");
376                 ecore_event_handler_del(s_info.create_handler);
377                 ecore_event_handler_del(s_info.destroy_handler);
378                 s_info.create_handler = NULL;
379                 s_info.destroy_handler = NULL;
380                 return LB_STATUS_ERROR_FAULT;
381         }
382
383         sniff_all_windows();
384         return LB_STATUS_SUCCESS;
385 }
386
387 HAPI int xmonitor_init(void)
388 {
389         if (USE_XMONITOR) {
390                 int ret;
391                 ret = enable_xmonitor();
392                 if (ret < 0)
393                         return ret;
394         }
395
396         s_info.paused = client_is_all_paused() || setting_is_lcd_off();
397         if (s_info.paused)
398                 touch_paused_file();
399         else
400                 remove_paused_file();
401
402         return LB_STATUS_SUCCESS;
403 }
404
405 HAPI void xmonitor_fini(void)
406 {
407         if (USE_XMONITOR)
408                 disable_xmonitor();
409 }
410
411 HAPI int xmonitor_add_event_callback(enum xmonitor_event event, int (*cb)(void *user_data), void *user_data)
412 {
413         struct event_item *item;
414
415         item = malloc(sizeof(*item));
416         if (!item) {
417                 ErrPrint("Heap: %s\n", strerror(errno));
418                 return LB_STATUS_ERROR_MEMORY;
419         }
420
421         item->cb = cb;
422         item->user_data = user_data;
423
424         switch (event) {
425         case XMONITOR_PAUSED:
426                 s_info.pause_list = eina_list_prepend(s_info.pause_list, item);
427                 break;
428         case XMONITOR_RESUMED:
429                 s_info.resume_list = eina_list_prepend(s_info.resume_list, item);
430                 break;
431         default:
432                 ErrPrint("Invalid event type\n");
433                 DbgFree(item);
434                 return LB_STATUS_ERROR_INVALID;
435         }
436
437         return LB_STATUS_SUCCESS;
438 }
439
440 HAPI int xmonitor_del_event_callback(enum xmonitor_event event, int (*cb)(void *user_data), void *user_data)
441 {
442         struct event_item *item;
443         Eina_List *l;
444         Eina_List *n;
445
446         switch (event) {
447         case XMONITOR_PAUSED:
448                 EINA_LIST_FOREACH_SAFE(s_info.pause_list, l, n, item) {
449                         if (item->cb == cb && item->user_data == user_data) {
450                                 s_info.pause_list = eina_list_remove(s_info.pause_list, item);
451                                 DbgFree(item);
452                                 return LB_STATUS_SUCCESS;
453                         }
454                 }
455                 break;
456
457         case XMONITOR_RESUMED:
458                 EINA_LIST_FOREACH_SAFE(s_info.resume_list, l, n, item) {
459                         if (item->cb == cb && item->user_data == user_data) {
460                                 s_info.resume_list = eina_list_remove(s_info.resume_list, item);
461                                 DbgFree(item);
462                                 return LB_STATUS_SUCCESS;
463                         }
464                 }
465                 break;
466         default:
467                 ErrPrint("Invalid event type\n");
468                 return LB_STATUS_ERROR_INVALID;
469         }
470
471         return LB_STATUS_ERROR_NOT_EXIST;
472 }
473
474 HAPI int xmonitor_is_paused(void)
475 {
476         return s_info.paused;
477 }
478
479 /* End of a file */