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