Merge from tizen 2.3.1
[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 <widget_service.h>
25 #include <widget_errno.h>
26
27 #include "widget.h"
28 #include "widget_internal.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         widget_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 WIDGET_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 WIDGET_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 WIDGET_ERROR_IO_ERROR;
104         }
105
106         DbgPrint("Flush data to a file (%s)\n", filename);
107         return WIDGET_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 WIDGET_ERROR_FAULT;
118         }
119
120         ecore_evas_free(ee);
121         return WIDGET_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 WIDGET_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 WIDGET_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         widget_flush_cb flush_cb;
159         int status = WIDGET_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 = WIDGET_ERROR_CANCELED;
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, WIDGET_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, WIDGET_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, WIDGET_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 == WIDGET_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         widget_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, WIDGET_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, WIDGET_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, WIDGET_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                 widget_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, WIDGET_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 *widget_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 (widget_service_get_size(size_type, &w, &h) != WIDGET_ERROR_NONE) {
398                 ErrPrint("Invalid size\n");
399                 return NULL;
400         }
401
402         info = malloc(sizeof(*info));
403         if (!info) {
404                 ErrPrint("Heap: %d\n", errno);
405                 return NULL;
406         }
407
408         info->id = strdup(id);
409         if (!info->id) {
410                 ErrPrint("Heap: %d\n", 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, "widget,Snapshot", ELM_WIN_TIZEN_WIDGET);
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 widget_snapshot_window_flush(void *snapshot_win, double timeout, widget_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 WIDGET_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 WIDGET_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 WIDGET_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 WIDGET_ERROR_FAULT;
493                 }
494         }
495
496         info->flush_cb = flush_cb;
497         info->data = data;
498
499         return WIDGET_ERROR_NONE;
500 }
501
502 /* End of a file */