2 * Copyright 2013 Samsung Electronics Co., Ltd
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
8 * http://floralicense.org/license/
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.
17 #include <Elementary.h>
18 #include <Ecore_Evas.h>
24 #include <dynamicbox_service.h>
25 #include <dynamicbox_errno.h>
27 #include "dynamicbox.h"
30 #define QUALITY_N_COMPRESS "quality=100 compress=1"
31 #define PUBLIC __attribute__((visibility("default")))
33 struct snapshot_info {
35 dynamicbox_flush_cb flush_cb;
38 Ecore_Timer *flush_timer;
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);
47 static inline Evas *create_virtual_canvas(int w, int h)
49 Ecore_Evas *internal_ee;
52 // Create virtual canvas
53 internal_ee = ecore_evas_buffer_new(w, h);
55 DbgPrint("Failed to create a new canvas buffer\n");
59 ecore_evas_alpha_set(internal_ee, EINA_TRUE);
60 ecore_evas_manual_render_set(internal_ee, EINA_FALSE);
62 // Get the "Evas" object from a virtual canvas
63 internal_e = ecore_evas_get(internal_ee);
65 ecore_evas_free(internal_ee);
66 DbgPrint("Faield to get Evas object\n");
73 static inline int flush_data_to_file(Evas *e, const char *data, const char *filename, int w, int h)
77 output = evas_object_image_add(e);
79 DbgPrint("Failed to create an image object\n");
80 return DBOX_STATUS_ERROR_FAULT;
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);
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;
98 evas_object_del(output);
100 if (access(filename, F_OK) != 0) {
101 DbgPrint("File %s is not found\n", filename);
102 return DBOX_STATUS_ERROR_IO_ERROR;
105 DbgPrint("Flush data to a file (%s)\n", filename);
106 return DBOX_STATUS_ERROR_NONE;
109 static inline int destroy_virtual_canvas(Evas *e)
113 ee = ecore_evas_ecore_evas_get(e);
115 DbgPrint("Failed to ecore evas object\n");
116 return DBOX_STATUS_ERROR_FAULT;
120 return DBOX_STATUS_ERROR_NONE;
123 static inline int flush_to_file(const void *canvas, const char *filename, int w, int h)
129 shot_e = create_virtual_canvas(w, h);
131 ErrPrint("Unable to create a new virtual window\n");
132 return DBOX_STATUS_ERROR_FAULT;
135 shot_ee = ecore_evas_ecore_evas_get(shot_e);
137 ErrPrint("Unable to get Ecore_Evas\n");
138 destroy_virtual_canvas(shot_e);
139 return DBOX_STATUS_ERROR_FAULT;
142 ecore_evas_manual_render_set(shot_ee, EINA_TRUE);
144 status = flush_data_to_file(shot_e, canvas, filename, w, h);
145 destroy_virtual_canvas(shot_e);
150 static Eina_Bool snapshot_cb(void *data)
152 Evas_Object *snapshot_win = data;
153 struct snapshot_info *info;
157 dynamicbox_flush_cb flush_cb;
158 int status = DBOX_STATUS_ERROR_NONE;
160 info = evas_object_data_get(snapshot_win, "snapshot,info");
162 ErrPrint("Invalid object\n");
163 return ECORE_CALLBACK_CANCEL;
166 if (info->flush_timer) {
167 info->flush_timer = NULL;
169 status = DBOX_STATUS_ERROR_CANCEL;
172 flush_cb = info->flush_cb;
173 info->flush_cb = NULL; /* To prevent call this from the delete callback */
175 e = evas_object_evas_get(snapshot_win);
177 ErrPrint("Invalid object\n");
179 flush_cb(snapshot_win, info->id, DBOX_STATUS_ERROR_FAULT, info->data);
181 return ECORE_CALLBACK_CANCEL;
184 ee = ecore_evas_ecore_evas_get(e);
186 ErrPrint("Invalid object (ee)\n");
188 flush_cb(snapshot_win, info->id, DBOX_STATUS_ERROR_FAULT, info->data);
190 return ECORE_CALLBACK_CANCEL;
193 canvas = (void*)ecore_evas_buffer_pixels_get(ee);
195 DbgPrint("Failed to get pixel canvas\n");
197 flush_cb(snapshot_win, info->id, DBOX_STATUS_ERROR_FAULT, info->data);
199 return ECORE_CALLBACK_CANCEL;
207 ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
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) {
215 flush_cb(snapshot_win, info->id, status, info->data);
217 * Do not access info after this.
221 return ECORE_CALLBACK_CANCEL;
224 static void del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
226 struct snapshot_info *info;
228 info = evas_object_data_del(obj, "snapshot,info");
233 DbgPrint("Delete object (%s)\n", info->id);
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);
238 if (info->flush_timer) {
239 ecore_timer_del(info->flush_timer);
240 info->flush_timer = NULL;
242 (void)snapshot_cb(obj);
243 DbgPrint("Flush is canceled\n");
248 * Render callback will be deleted.
254 static Eina_Bool direct_snapshot_cb(void *data)
262 dynamicbox_flush_cb flush_cb;
263 Evas_Object *snapshot_win = data;
264 struct snapshot_info *info;
266 info = evas_object_data_get(snapshot_win, "snapshot,info");
268 ErrPrint("Unable to get snapshot info\n");
269 return ECORE_CALLBACK_CANCEL;
272 info->flush_timer = NULL;
273 flush_cb = info->flush_cb;
274 info->flush_cb = NULL; /* To prevent call this from the delete callback */
276 e = evas_object_evas_get(snapshot_win);
278 ErrPrint("Invalid object, failed to get Evas\n");
280 flush_cb(snapshot_win, info->id, DBOX_STATUS_ERROR_FAULT, info->data);
282 return ECORE_CALLBACK_CANCEL;
285 ee = ecore_evas_ecore_evas_get(e);
287 ErrPrint("Unable to get Ecore_Evas object\n");
289 flush_cb(snapshot_win, info->id, DBOX_STATUS_ERROR_FAULT, info->data);
291 return ECORE_CALLBACK_CANCEL;
294 ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
295 ecore_evas_manual_render_set(ee, EINA_TRUE);
297 canvas = ecore_evas_buffer_pixels_get(ee);
299 ErrPrint("Failed to get canvas\n");
301 flush_cb(snapshot_win, info->id, DBOX_STATUS_ERROR_FAULT, info->data);
303 return ECORE_CALLBACK_CANCEL;
306 status = flush_to_file(canvas, info->id, w, h);
308 flush_cb(snapshot_win, info->id, status, info->data);
310 return ECORE_CALLBACK_CANCEL;
313 static void post_render_cb(void *data, Evas *e, void *event_info)
315 Evas_Object *snapshot_win = data;
316 struct snapshot_info *info;
318 info = evas_object_data_get(snapshot_win, "snapshot,info");
320 ErrPrint("snapshot info is not valid\n");
326 if (info->flush_timer) {
329 * This has not to be happens.
331 ErrPrint("Flush timer is not cleared\n");
332 ecore_timer_del(info->flush_timer);
333 info->flush_timer = NULL;
336 if (!info->flush_cb) {
337 DbgPrint("Flush request is not initiated yet\n");
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.
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;
353 ErrPrint("Unalbe to add timer for getting the snapshot\n");
354 flush_cb = info->flush_cb;
355 info->flush_cb = NULL;
358 flush_cb(snapshot_win, info->id, DBOX_STATUS_ERROR_FAULT, info->data);
362 * Do not access info after from here.
367 static void pre_render_cb(void *data, Evas *e, void *event_info)
369 Evas_Object *snapshot_win = data;
370 struct snapshot_info *info;
372 info = evas_object_data_get(snapshot_win, "snapshot,info");
374 ErrPrint("snapshot info is not valid\n");
378 if (info->flush_timer) {
379 ecore_timer_del(info->flush_timer);
380 info->flush_timer = NULL;
381 DbgPrint("Clear the flush timer\n");
384 DbgPrint("Pre-render callback\n");
387 PUBLIC void *dynamicbox_snapshot_window_add(const char *id, int size_type)
389 struct snapshot_info *info;
390 Evas_Object *snapshot_win;
396 if (dynamicbox_service_get_size(size_type, &w, &h) != DBOX_STATUS_ERROR_NONE) {
397 ErrPrint("Invalid size\n");
401 info = malloc(sizeof(*info));
403 ErrPrint("Heap: %s\n", strerror(errno));
407 info->id = strdup(id);
409 ErrPrint("Heap: %s\n", strerror(errno));
414 info->flush_cb = NULL;
416 info->flush_timer = NULL;
417 info->render_cnt = 0;
419 e = create_virtual_canvas(w, h);
426 parent = evas_object_rectangle_add(e);
428 destroy_virtual_canvas(e);
434 snapshot_win = elm_win_add(parent, "DBox,Snapshot", ELM_WIN_DYNAMIC_BOX);
435 evas_object_del(parent);
437 destroy_virtual_canvas(e);
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);
448 evas_object_data_set(snapshot_win, "snapshot,info", info);
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);
456 PUBLIC int dynamicbox_snapshot_window_flush(void *snapshot_win, double timeout, dynamicbox_flush_cb flush_cb, void *data)
458 struct snapshot_info *info;
460 if (!flush_cb || timeout < 0.0f) {
461 ErrPrint("Invalid argument (%p, %lf)\n", flush_cb, timeout);
462 return DBOX_STATUS_ERROR_INVALID_PARAMETER;
465 info = evas_object_data_get(snapshot_win, "snapshot,info");
467 ErrPrint("Invalid argument\n");
468 return DBOX_STATUS_ERROR_INVALID_PARAMETER;
471 info->timeout = timeout;
473 if (timeout == 0.0f) {
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
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;
483 } else if (info->render_cnt) {
485 * Try to watit pre-render callback.
486 * If there is rendered contents.
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;
495 info->flush_cb = flush_cb;
498 return DBOX_STATUS_ERROR_NONE;