Update License
[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                 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                 LOGD("File %s is not found\n", filename);
89                 return LB_STATUS_ERROR_IO;
90         }
91
92         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         LOGD("Delete object (%s)\n", info->id);
152
153         if (info->flush_cb) {
154                 info->flush_cb(obj, info->id, LB_STATUS_ERROR_CANCEL, info->data);
155                 LOGD("Flush is canceled\n");
156         }
157
158         /*!
159          * \note
160          * Render callback will be deleted.
161          */
162         destroy_virtual_canvas(e);
163         free(info->id);
164         free(info);
165 }
166
167
168
169 static Eina_Bool direct_snapshot_cb(void *data)
170 {
171         Evas *e;
172         Ecore_Evas *ee;
173         void *canvas;
174         int status;
175         int w;
176         int h;
177         void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data);
178         Evas_Object *snapshot_win = data;
179         struct snapshot_info *info;
180
181         info = evas_object_data_get(snapshot_win, "snapshot,info");
182         if (!info) {
183                 LOGE("Unable to get snapshot info\n");
184                 return ECORE_CALLBACK_CANCEL;
185         }
186
187         info->flush_timer = NULL;
188         flush_cb = info->flush_cb;
189         info->flush_cb = NULL; /* To prevent call this from the delete callback */
190
191         e = evas_object_evas_get(snapshot_win);
192         if (!e) {
193                 LOGE("Invalid object, failed to get Evas\n");
194                 if (flush_cb)
195                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
196                 return ECORE_CALLBACK_CANCEL;
197         }
198
199         ee = ecore_evas_ecore_evas_get(e);
200         if (!ee) {
201                 LOGE("Unable to get Ecore_Evas object\n");
202                 if (flush_cb)
203                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
204                 return ECORE_CALLBACK_CANCEL;
205         }
206
207         ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
208         ecore_evas_manual_render_set(ee, EINA_TRUE);
209
210         canvas = ecore_evas_buffer_pixels_get(ee);
211         if (!canvas) {
212                 LOGE("Failed to get canvas\n");
213                 if (flush_cb)
214                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
215                 return ECORE_CALLBACK_CANCEL;
216         }
217
218         status = flush_to_file(canvas, info->id, w, h);
219         if (flush_cb)
220                 flush_cb(snapshot_win, info->id, status, info->data);
221         return ECORE_CALLBACK_CANCEL;
222 }
223
224
225
226 static Eina_Bool snapshot_cb(void *data)
227 {
228         Evas_Object *snapshot_win = data;
229         struct snapshot_info *info;
230         Evas *e;
231         Ecore_Evas *ee;
232         void *canvas;
233         void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data);
234
235         info = evas_object_data_get(snapshot_win, "snapshot,info");
236         if (!info) {
237                 LOGE("Invalid object\n");
238                 return ECORE_CALLBACK_CANCEL;
239         }
240
241         info->flush_timer = NULL;
242         flush_cb = info->flush_cb;
243         info->flush_cb = NULL; /* To prevent call this from the delete callback */
244
245         e = evas_object_evas_get(snapshot_win);
246         if (!e) {
247                 LOGE("Invalid object\n");
248                 if (flush_cb)
249                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
250                 return ECORE_CALLBACK_CANCEL;
251         }
252
253         ee = ecore_evas_ecore_evas_get(e);
254         if (!ee) {
255                 LOGE("Invalid object (ee)\n");
256                 if (flush_cb)
257                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
258                 return ECORE_CALLBACK_CANCEL;
259         }
260
261         canvas = (void*)ecore_evas_buffer_pixels_get(ee);
262         if (!canvas) {
263                 LOGD("Failed to get pixel canvas\n");
264                 if (flush_cb)
265                         flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
266                 return ECORE_CALLBACK_CANCEL;
267         }
268
269         if (flush_cb) {
270                 int w;
271                 int h;
272                 int status;
273
274                 ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
275
276                 LOGD("Flush size: %dx%d\n", w, h);
277                 status = flush_to_file(canvas, info->id, w, h);
278
279                 flush_cb(snapshot_win, info->id, status, info->data);
280                 /*!
281                  * Do not access info after this.
282                  */
283         }
284
285         return ECORE_CALLBACK_CANCEL;
286 }
287
288
289
290 static void post_render_cb(void *data, Evas *e, void *event_info)
291 {
292         Evas_Object *snapshot_win = data;
293         struct snapshot_info *info;
294
295         info = evas_object_data_get(snapshot_win, "snapshot,info");
296         if (!info) {
297                 LOGE("snapshot info is not valid\n");
298                 return;
299         }
300
301         info->render_cnt++;
302
303         if (info->flush_timer) {
304                 /*!
305                  * This has not to be happens.
306                  */
307                 LOGE("Flush timer is not cleared\n");
308                 ecore_timer_del(info->flush_timer);
309                 info->flush_timer = NULL;
310         }
311
312         if (!info->flush_cb) {
313                 LOGD("Flush request is not initiated yet\n");
314                 return;
315         }
316
317         /*!
318          * \NOTE
319          * Even if tehre is no timer registered, we should capture the content
320          * from out of this callback.
321          * Or we can met unexpected problems.
322          * To avoid it, use the 0.0001f to get timer callback ASAP, not in this function.
323          */
324         LOGD("Fire the flush timer %lf (%d)\n", info->timeout, info->render_cnt);
325         info->flush_timer = ecore_timer_add(info->timeout, snapshot_cb, snapshot_win);
326         if (!info->flush_timer) {
327                 void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data);
328
329                 LOGE("Unalbe to add timer for getting the snapshot\n");
330                 flush_cb = info->flush_cb;
331                 info->flush_cb = NULL;
332
333                 flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
334                 /*!
335                  * \note
336                  * Do not access info after from here.
337                  */
338         }
339 }
340
341
342
343 static void pre_render_cb(void *data, Evas *e, void *event_info)
344 {
345         Evas_Object *snapshot_win = data;
346         struct snapshot_info *info;
347
348         info = evas_object_data_get(snapshot_win, "snapshot,info");
349         if (!info) {
350                 LOGE("snapshot info is not valid\n");
351                 return;
352         }
353
354         if (info->flush_timer) {
355                 ecore_timer_del(info->flush_timer);
356                 info->flush_timer = NULL;
357                 LOGD("Clear the flush timer\n");
358         }
359
360         LOGD("Pre-render callback\n");
361 }
362
363
364
365 static void resize_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
366 {
367         Ecore_Evas *ee;
368         int w;
369         int h;
370         int ow;
371         int oh;
372
373         ee = ecore_evas_ecore_evas_get(e);
374         if (!ee)
375                 return;
376
377         ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
378         evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
379         if (ow == w && oh == h) {
380                 LOGD("Size is not changed: %dx%d\n", w, h);
381                 return;
382         }
383
384         /*!
385          * Box(parent object) is resized.
386          * Try to resize the canvas too.
387          */
388         ecore_evas_resize(ee, w, h);
389         LOGD("Canvas is resized to %dx%d\n", w, h);
390 }
391
392
393
394 PUBLIC Evas_Object *livebox_snapshot_window_add(const char *id, int size_type)
395 {
396         struct snapshot_info *info;
397         Evas_Object *snapshot_win;
398         Evas *e;
399         int w;
400         int h;
401
402         if (livebox_service_get_size(size_type, &w, &h) != LB_STATUS_SUCCESS) {
403                 LOGE("Invalid size\n");
404                 return NULL;
405         }
406
407         info = malloc(sizeof(*info));
408         if (!info) {
409                 LOGE("Heap: %s\n", strerror(errno));
410                 return NULL;
411         }
412
413         info->id = strdup(id);
414         if (!info->id) {
415                 LOGE("Heap: %s\n", strerror(errno));
416                 free(info);
417                 return NULL;
418         }
419
420         info->flush_cb = NULL;
421         info->data = NULL;
422         info->flush_timer = NULL;
423         info->render_cnt = 0;
424
425         e = create_virtual_canvas(w, h);
426         if (!e) {
427                 free(info->id);
428                 free(info);
429                 return NULL;
430         }
431
432         snapshot_win = evas_object_rectangle_add(e);
433         if (!snapshot_win) {
434                 destroy_virtual_canvas(e);
435                 free(info->id);
436                 free(info);
437                 return NULL;
438         }
439
440         LOGD("Add new window %dx%d\n", w, h);
441         evas_object_event_callback_add(snapshot_win, EVAS_CALLBACK_DEL, del_cb, NULL);
442         evas_object_event_callback_add(snapshot_win, EVAS_CALLBACK_RESIZE, resize_cb, NULL);
443         evas_object_resize(snapshot_win, w, h);
444         evas_object_show(snapshot_win);
445
446         evas_object_data_set(snapshot_win, "snapshot,info", info);
447
448         evas_event_callback_add(e, EVAS_CALLBACK_RENDER_POST, post_render_cb, snapshot_win);
449         evas_event_callback_add(e, EVAS_CALLBACK_RENDER_PRE, pre_render_cb, snapshot_win);
450
451         return snapshot_win;
452 }
453
454
455
456 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)
457 {
458         struct snapshot_info *info;
459
460         if (!flush_cb || timeout < 0.0f) {
461                 LOGE("Invalid argument (%p, %lf)\n", flush_cb, timeout);
462                 return LB_STATUS_ERROR_INVALID;
463         }
464
465         info = evas_object_data_get(snapshot_win, "snapshot,info");
466         if (!info) {
467                 LOGE("Invalid argument\n");
468                 return LB_STATUS_ERROR_INVALID;
469         }
470
471         info->timeout = timeout;
472
473         if (timeout == 0.0f) {
474                 /*!
475                  * This timer is just used for guarantees same behavious even if it flushes content directly,
476                  * The callback should be called from next loop.
477                  * or the developer will get confused
478                  */
479                 info->flush_timer = ecore_timer_add(0.0001f, direct_snapshot_cb, snapshot_win);
480                 if (!info->flush_timer)
481                         return LB_STATUS_ERROR_FAULT;
482         } else if (info->render_cnt) {
483                 /*!
484                  * Try to watit pre-render callback.
485                  * If there is rendered contents.
486                  */
487                 DbgPrint("Rendered %d times already\n", info->render_cnt);
488                 info->flush_timer = ecore_timer_add(info->timeout, snapshot_cb, snapshot_win);
489                 if (!info->flush_timer)
490                         return LB_STATUS_ERROR_FAULT;
491         }
492
493         info->flush_cb = flush_cb;
494         info->data = data;
495
496         return LB_STATUS_SUCCESS;
497 }
498
499
500
501 PUBLIC int livebox_snapshot_window_del(Evas_Object *snapshot_win)
502 {
503         Evas *e;
504         if (!snapshot_win || !evas_object_data_get(snapshot_win, "snapshot,info"))
505                 return LB_STATUS_ERROR_INVALID;
506
507         e = evas_object_evas_get(snapshot_win);
508         evas_event_callback_del(e, EVAS_CALLBACK_RENDER_POST, post_render_cb);
509         evas_event_callback_del(e, EVAS_CALLBACK_RENDER_PRE, pre_render_cb);
510
511         evas_object_del(snapshot_win);
512         return LB_STATUS_SUCCESS;
513 }
514
515
516
517 // End of a file