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