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