Enable Wayland build
[apps/native/widget/widget.git] / src / snapshot_window.c
1 #include <Ecore_Evas.h>
2 #include <Evas.h>
3 #include <Ecore.h>
4 #include <unistd.h>
5
6 #include <dlog.h>
7 #include <livebox-service.h>
8 #include <livebox-errno.h>
9
10 #include "livebox.h"
11 #include "debug.h"
12
13 #define QUALITY_N_COMPRESS "quality=100 compress=1"
14 #define PUBLIC __attribute__((visibility("default")))
15
16 struct snapshot_info {
17         char *id;
18         void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data);
19         void *data;
20
21         Ecore_Timer *flush_timer;
22
23         int render_cnt;
24         double timeout;
25 };
26
27 static void post_render_cb(void *data, Evas *e, void *event_info);
28 static void pre_render_cb(void *data, Evas *e, void *event_info);
29
30
31
32 static inline Evas *create_virtual_canvas(int w, int h)
33 {
34         Ecore_Evas *internal_ee;
35         Evas *internal_e;
36
37         // Create virtual canvas
38         internal_ee = ecore_evas_buffer_new(w, h);
39         if (!internal_ee) {
40                 LOGD("Failed to create a new canvas buffer\n");
41                 return NULL;
42         }
43
44         ecore_evas_alpha_set(internal_ee, EINA_TRUE);
45         ecore_evas_manual_render_set(internal_ee, EINA_FALSE);
46
47         // Get the "Evas" object from a virtual canvas
48         internal_e = ecore_evas_get(internal_ee);
49         if (!internal_e) {
50                 ecore_evas_free(internal_ee);
51                 LOGD("Faield to get Evas object\n");
52                 return NULL;
53         }
54
55         return internal_e;
56 }
57
58
59
60 static inline int flush_data_to_file(Evas *e, char *data, const char *filename, int w, int h)
61 {
62         Evas_Object *output;
63
64         output = evas_object_image_add(e);
65         if (!output) {
66                 LOGD("Failed to create an image object\n");
67                 return LB_STATUS_ERROR_FAULT;
68         }
69
70         evas_object_image_data_set(output, NULL);
71         evas_object_image_colorspace_set(output, EVAS_COLORSPACE_ARGB8888);
72         evas_object_image_alpha_set(output, EINA_TRUE);
73         evas_object_image_size_set(output, w, h);
74         evas_object_image_smooth_scale_set(output, EINA_TRUE);
75         evas_object_image_data_set(output, data);
76         evas_object_image_fill_set(output, 0, 0, w, h);
77         evas_object_image_data_update_add(output, 0, 0, w, h);
78
79         if (evas_object_image_save(output, filename, NULL, QUALITY_N_COMPRESS) == EINA_FALSE) {
80                 evas_object_del(output);
81                 SECURE_LOGD("Faield to save a captured image (%s)\n", filename);
82                 return LB_STATUS_ERROR_IO;
83         }
84
85         evas_object_del(output);
86
87         if (access(filename, F_OK) != 0) {
88                 SECURE_LOGD("File %s is not found\n", filename);
89                 return LB_STATUS_ERROR_IO;
90         }
91
92         SECURE_LOGD("Flush data to a file (%s)\n", filename);
93         return LB_STATUS_SUCCESS;
94 }
95
96
97
98 static inline int destroy_virtual_canvas(Evas *e)
99 {
100         Ecore_Evas *ee;
101
102         ee = ecore_evas_ecore_evas_get(e);
103         if (!ee) {
104                 LOGD("Failed to ecore evas object\n");
105                 return LB_STATUS_ERROR_FAULT;
106         }
107
108         ecore_evas_free(ee);
109         return LB_STATUS_SUCCESS;
110 }
111
112
113
114 static inline int flush_to_file(void *canvas, const char *filename, int w, int h)
115 {
116         int status;
117         Evas *shot_e;
118         Ecore_Evas *shot_ee;
119
120         shot_e = create_virtual_canvas(w, h);
121         if (!shot_e) {
122                 LOGE("Unable to create a new virtual window\n");
123                 return LB_STATUS_ERROR_FAULT;
124         }
125
126         shot_ee = ecore_evas_ecore_evas_get(shot_e);
127         if (!shot_ee) {
128                 LOGE("Unable to get Ecore_Evas\n");
129                 destroy_virtual_canvas(shot_e);
130                 return LB_STATUS_ERROR_FAULT;
131         }
132
133         ecore_evas_manual_render_set(shot_ee, EINA_TRUE);
134
135         status = flush_data_to_file(shot_e, canvas, filename, w, h);
136         destroy_virtual_canvas(shot_e);
137
138         return status;
139 }
140
141
142
143 static void del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
144 {
145         struct snapshot_info *info;
146
147         info = evas_object_data_del(obj, "snapshot,info");
148         if (!info) {
149                 return;
150         }
151
152         SECURE_LOGD("Delete object (%s)\n", info->id);
153
154         if (info->flush_cb) {
155                 info->flush_cb(obj, info->id, LB_STATUS_ERROR_CANCEL, info->data);
156                 LOGD("Flush is canceled\n");
157         }
158
159         /*!
160          * \note
161          * Render callback will be deleted.
162          */
163         destroy_virtual_canvas(e);
164         free(info->id);
165         free(info);
166 }
167
168
169
170 static Eina_Bool direct_snapshot_cb(void *data)
171 {
172         Evas *e;
173         Ecore_Evas *ee;
174         void *canvas;
175         int status;
176         int w;
177         int h;
178         void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data);
179         Evas_Object *snapshot_win = data;
180         struct snapshot_info *info;
181
182         info = evas_object_data_get(snapshot_win, "snapshot,info");
183         if (!info) {
184                 LOGE("Unable to get snapshot info\n");
185                 return ECORE_CALLBACK_CANCEL;
186         }
187
188         info->flush_timer = NULL;
189         flush_cb = info->flush_cb;
190         info->flush_cb = NULL; /* To prevent call this from the delete callback */
191
192         e = evas_object_evas_get(snapshot_win);
193         if (!e) {
194                 LOGE("Invalid object, failed to get Evas\n");
195                 if (flush_cb) {
196                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
197                 }
198                 return ECORE_CALLBACK_CANCEL;
199         }
200
201         ee = ecore_evas_ecore_evas_get(e);
202         if (!ee) {
203                 LOGE("Unable to get Ecore_Evas object\n");
204                 if (flush_cb) {
205                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
206                 }
207                 return ECORE_CALLBACK_CANCEL;
208         }
209
210         ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
211         ecore_evas_manual_render_set(ee, EINA_TRUE);
212
213         canvas = ecore_evas_buffer_pixels_get(ee);
214         if (!canvas) {
215                 LOGE("Failed to get canvas\n");
216                 if (flush_cb) {
217                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
218                 }
219                 return ECORE_CALLBACK_CANCEL;
220         }
221
222         status = flush_to_file(canvas, info->id, w, h);
223         if (flush_cb) {
224                 flush_cb(snapshot_win, info->id, status, info->data);
225         }
226         return ECORE_CALLBACK_CANCEL;
227 }
228
229
230
231 static Eina_Bool snapshot_cb(void *data)
232 {
233         Evas_Object *snapshot_win = data;
234         struct snapshot_info *info;
235         Evas *e;
236         Ecore_Evas *ee;
237         void *canvas;
238         void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data);
239
240         info = evas_object_data_get(snapshot_win, "snapshot,info");
241         if (!info) {
242                 LOGE("Invalid object\n");
243                 return ECORE_CALLBACK_CANCEL;
244         }
245
246         info->flush_timer = NULL;
247         flush_cb = info->flush_cb;
248         info->flush_cb = NULL; /* To prevent call this from the delete callback */
249
250         e = evas_object_evas_get(snapshot_win);
251         if (!e) {
252                 LOGE("Invalid object\n");
253                 if (flush_cb) {
254                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
255                 }
256                 return ECORE_CALLBACK_CANCEL;
257         }
258
259         ee = ecore_evas_ecore_evas_get(e);
260         if (!ee) {
261                 LOGE("Invalid object (ee)\n");
262                 if (flush_cb) {
263                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
264                 }
265                 return ECORE_CALLBACK_CANCEL;
266         }
267
268         canvas = (void*)ecore_evas_buffer_pixels_get(ee);
269         if (!canvas) {
270                 LOGD("Failed to get pixel canvas\n");
271                 if (flush_cb) {
272                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
273                 }
274                 return ECORE_CALLBACK_CANCEL;
275         }
276
277         if (flush_cb) {
278                 int w;
279                 int h;
280                 int status;
281
282                 ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
283
284                 SECURE_LOGD("Flush size: %dx%d\n", w, h);
285                 status = flush_to_file(canvas, info->id, w, h);
286
287                 flush_cb(snapshot_win, info->id, status, info->data);
288                 /*!
289                  * Do not access info after this.
290                  */
291         }
292
293         return ECORE_CALLBACK_CANCEL;
294 }
295
296
297
298 static void post_render_cb(void *data, Evas *e, void *event_info)
299 {
300         Evas_Object *snapshot_win = data;
301         struct snapshot_info *info;
302
303         info = evas_object_data_get(snapshot_win, "snapshot,info");
304         if (!info) {
305                 LOGE("snapshot info is not valid\n");
306                 return;
307         }
308
309         info->render_cnt++;
310
311         if (info->flush_timer) {
312                 /*!
313                  * This has not to be happens.
314                  */
315                 LOGE("Flush timer is not cleared\n");
316                 ecore_timer_del(info->flush_timer);
317                 info->flush_timer = NULL;
318         }
319
320         if (!info->flush_cb) {
321                 LOGD("Flush request is not initiated yet\n");
322                 return;
323         }
324
325         /*!
326          * \NOTE
327          * Even if tehre is no timer registered, we should capture the content
328          * from out of this callback.
329          * Or we can met unexpected problems.
330          * To avoid it, use the 0.0001f to get timer callback ASAP, not in this function.
331          */
332         LOGD("Fire the flush timer %lf (%d)\n", info->timeout, info->render_cnt);
333         info->flush_timer = ecore_timer_add(info->timeout, snapshot_cb, snapshot_win);
334         if (!info->flush_timer) {
335                 void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data);
336
337                 LOGE("Unalbe to add timer for getting the snapshot\n");
338                 flush_cb = info->flush_cb;
339                 info->flush_cb = NULL;
340
341                 flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
342                 /*!
343                  * \note
344                  * Do not access info after from here.
345                  */
346         }
347 }
348
349
350
351 static void pre_render_cb(void *data, Evas *e, void *event_info)
352 {
353         Evas_Object *snapshot_win = data;
354         struct snapshot_info *info;
355
356         info = evas_object_data_get(snapshot_win, "snapshot,info");
357         if (!info) {
358                 LOGE("snapshot info is not valid\n");
359                 return;
360         }
361
362         if (info->flush_timer) {
363                 ecore_timer_del(info->flush_timer);
364                 info->flush_timer = NULL;
365                 LOGD("Clear the flush timer\n");
366         }
367
368         LOGD("Pre-render callback\n");
369 }
370
371
372
373 static void resize_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
374 {
375         Ecore_Evas *ee;
376         int w;
377         int h;
378         int ow;
379         int oh;
380
381         ee = ecore_evas_ecore_evas_get(e);
382         if (!ee) {
383                 return;
384         }
385
386         ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
387         evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
388         if (ow == w && oh == h) {
389                 SECURE_LOGD("Size is not changed: %dx%d\n", w, h);
390                 return;
391         }
392
393         /*!
394          * Box(parent object) is resized.
395          * Try to resize the canvas too.
396          */
397         ecore_evas_resize(ee, w, h);
398         SECURE_LOGD("Canvas is resized to %dx%d\n", w, h);
399 }
400
401
402
403 PUBLIC Evas_Object *livebox_snapshot_window_add(const char *id, int size_type)
404 {
405         struct snapshot_info *info;
406         Evas_Object *snapshot_win;
407         Evas *e;
408         int w;
409         int h;
410
411         if (livebox_service_get_size(size_type, &w, &h) != LB_STATUS_SUCCESS) {
412                 LOGE("Invalid size\n");
413                 return NULL;
414         }
415
416         info = malloc(sizeof(*info));
417         if (!info) {
418                 LOGE("Heap: %s\n", strerror(errno));
419                 return NULL;
420         }
421
422         info->id = strdup(id);
423         if (!info->id) {
424                 LOGE("Heap: %s\n", strerror(errno));
425                 free(info);
426                 return NULL;
427         }
428
429         info->flush_cb = NULL;
430         info->data = NULL;
431         info->flush_timer = NULL;
432         info->render_cnt = 0;
433
434         e = create_virtual_canvas(w, h);
435         if (!e) {
436                 free(info->id);
437                 free(info);
438                 return NULL;
439         }
440
441         snapshot_win = evas_object_rectangle_add(e);
442         if (!snapshot_win) {
443                 destroy_virtual_canvas(e);
444                 free(info->id);
445                 free(info);
446                 return NULL;
447         }
448
449         SECURE_LOGD("Add new window %dx%d\n", w, h);
450         evas_object_event_callback_add(snapshot_win, EVAS_CALLBACK_DEL, del_cb, NULL);
451         evas_object_event_callback_add(snapshot_win, EVAS_CALLBACK_RESIZE, resize_cb, NULL);
452         evas_object_resize(snapshot_win, w, h);
453         evas_object_show(snapshot_win);
454
455         evas_object_data_set(snapshot_win, "snapshot,info", info);
456
457         evas_event_callback_add(e, EVAS_CALLBACK_RENDER_POST, post_render_cb, snapshot_win);
458         evas_event_callback_add(e, EVAS_CALLBACK_RENDER_PRE, pre_render_cb, snapshot_win);
459
460         return snapshot_win;
461 }
462
463
464
465 PUBLIC int livebox_snapshot_window_flush(Evas_Object *snapshot_win, double timeout, void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data), void *data)
466 {
467         struct snapshot_info *info;
468
469         if (!flush_cb || timeout < 0.0f) {
470                 LOGE("Invalid argument (%p, %lf)\n", flush_cb, timeout);
471                 return LB_STATUS_ERROR_INVALID;
472         }
473
474         info = evas_object_data_get(snapshot_win, "snapshot,info");
475         if (!info) {
476                 LOGE("Invalid argument\n");
477                 return LB_STATUS_ERROR_INVALID;
478         }
479
480         info->timeout = timeout;
481
482         if (timeout == 0.0f) {
483                 /*!
484                  * This timer is just used for guarantees same behavious even if it flushes content directly,
485                  * The callback should be called from next loop.
486                  * or the developer will get confused
487                  */
488                 info->flush_timer = ecore_timer_add(0.0001f, direct_snapshot_cb, snapshot_win);
489                 if (!info->flush_timer) {
490                         return LB_STATUS_ERROR_FAULT;
491                 }
492         } else if (info->render_cnt) {
493                 /*!
494                  * Try to watit pre-render callback.
495                  * If there is rendered contents.
496                  */
497                 DbgPrint("Rendered %d times already\n", info->render_cnt);
498                 info->flush_timer = ecore_timer_add(info->timeout, snapshot_cb, snapshot_win);
499                 if (!info->flush_timer) {
500                         return LB_STATUS_ERROR_FAULT;
501                 }
502         }
503
504         info->flush_cb = flush_cb;
505         info->data = data;
506
507         return LB_STATUS_SUCCESS;
508 }
509
510
511
512 PUBLIC int livebox_snapshot_window_del(Evas_Object *snapshot_win)
513 {
514         Evas *e;
515         if (!snapshot_win || !evas_object_data_get(snapshot_win, "snapshot,info")) {
516                 return LB_STATUS_ERROR_INVALID;
517         }
518
519         e = evas_object_evas_get(snapshot_win);
520         evas_event_callback_del(e, EVAS_CALLBACK_RENDER_POST, post_render_cb);
521         evas_event_callback_del(e, EVAS_CALLBACK_RENDER_PRE, pre_render_cb);
522
523         evas_object_del(snapshot_win);
524         return LB_STATUS_SUCCESS;
525 }
526
527
528
529 // End of a file