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