Sync with the private repository
authorSung-jae Park <nicesj.park@samsung.com>
Wed, 10 Apr 2013 11:44:53 +0000 (20:44 +0900)
committerGerrit Code Review <gerrit2@kim11>
Wed, 10 Apr 2013 12:12:15 +0000 (21:12 +0900)
RENDER_FLUSH_PRE/RENDER_FLUSH_POST -> RENDER_PRE/RENDER_POST

Merge the desc data.
If it is not read by the script engine of provider,
merge new desc blocks with previous one.

script engine should know the context of desc blocks.

Accessibility desc type added

Add dummy implmentation files.

These patch will not apply any changes to release binary.
Just keep the implementations only in this repository as source code level.
will not be built and distributed.
---------------------------------

Implementing the snapshot & virtual window add function.

The snapshot window is used for getting an image file from the rendering buffer.

Conceptual logic (how it works?)
-----------------------------------------------------------------------
1. Create a new snapshot window

2. Add widget objects for representing the content.

3. Trigger the snapshot flush opertaion

4. post-render callback will add the flush timer (0.5 sec period)

5. start rendering loop

5.1 If the pre render callback is called,
  reset the flush timer

5.2 If the flush timer is expired so the timer callback is called,
  flush the data in the rendering buffer to a file.
-----------------------------------------------------------------------
This logic can be failed when the system is going to slower and slower.
After set the flush timer from the post render callback, the process
has to start the rendering of a next frame in 0.5 sec.
If it couldn't start rendering, the flush timer will be expired.
then it will finsish the rendering loop and flush the rendered data to a file
If there are remained frames, we will lost it.

Change-Id: Ic7216ce392faaf679f8f024130599516477ffb2e

CMakeLists.txt
include/livebox.h
packaging/liblivebox.spec
src/livebox.c
src/snapshot_window.c [new file with mode: 0644]
src/virtual_window.c [new file with mode: 0644]

index 56ef8f8..4a16d10 100644 (file)
@@ -18,6 +18,8 @@ pkg_check_modules(pkgs REQUIRED
        dlog
        livebox-service
        provider
+       evas
+       ecore
 )
 
 FOREACH(flag ${pkgs_CFLAGS})
index 4a6b145..863df72 100644 (file)
@@ -21,6 +21,8 @@
 extern "C" {
 #endif
 
+#include <Evas.h>
+
 struct livebox_buffer; /* Defined by provider */
 
 /*!
@@ -51,6 +53,7 @@ extern const int LB_SYS_EVENT_RESUMED;
 #define LB_DESC_TYPE_INFO "info"
 #define LB_DESC_TYPE_DRAG "drag"
 #define LB_DESC_TYPE_SCRIPT "script"
+#define LB_DESC_TYPE_ACCESS "access"
 
 /*!
  * \brief
@@ -280,6 +283,15 @@ extern int livebox_buffer_pre_render(struct livebox_buffer *handle);
  */
 extern int livebox_buffer_post_render(struct livebox_buffer *handle);
 
+/*
+extern Evas_Object *livebox_snapshot_window_add(const char *id, int size_type);
+extern int livebox_snapshot_window_flush(Evas_Object *snapshot_win, double timeout, void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data), void *data);
+extern int livebox_snapshot_window_del(Evas_Object *snapshot_win);
+extern Evas_Object *livebox_virtual_window_add(const char *id, int width, int height);
+extern int livebox_virtual_window_del(Evas_Object *virtual_win);
+*/
+
+// End of a file
 #ifdef __cplusplus
 }
 #endif
index 3b8ba81..47d0ef4 100644 (file)
@@ -1,6 +1,6 @@
 Name: liblivebox
 Summary: Library for the development of a livebox 
-Version: 0.1.19
+Version: 0.2.1
 Release: 1
 Group: HomeTF/Livebox
 License: Flora License
@@ -9,7 +9,8 @@ BuildRequires: cmake, gettext-tools, coreutils
 BuildRequires: pkgconfig(dlog)
 BuildRequires: pkgconfig(livebox-service)
 BuildRequires: pkgconfig(provider)
-BuildRequires: pkgconfig(elementary)
+BuildRequires: pkgconfig(ecore)
+BuildRequires: pkgconfig(evas)
 
 %description
 Livebox development library
index ce03eba..339e59c 100644 (file)
@@ -19,6 +19,7 @@
 #include <stdlib.h> /* malloc */
 #include <string.h> /* strdup */
 #include <libgen.h>
+#include <unistd.h> /* access */
 
 #include <dlog.h>
 #include <livebox-service.h>
@@ -31,7 +32,7 @@
 #include "dlist.h"
 #include "util.h"
 
-#define EAPI __attribute__((visibility("default")))
+#define PUBLIC __attribute__((visibility("default")))
 
 #define FILE_SCHEMA    "file://"
 
@@ -67,23 +68,23 @@ struct livebox_buffer_data {
        int accelerated;
 };
 
-EAPI const int DONE = 0x00;
-EAPI const int OUTPUT_UPDATED = 0x02;
-EAPI const int USE_NET = 0x04;
+PUBLIC const int DONE = 0x00;
+PUBLIC const int OUTPUT_UPDATED = 0x02;
+PUBLIC const int USE_NET = 0x04;
 
-EAPI const int NEED_TO_SCHEDULE = 0x01;
-EAPI const int NEED_TO_CREATE = 0x01;
-EAPI const int NEED_TO_DESTROY = 0x01;
-EAPI const int NEED_TO_UPDATE = 0x01;
+PUBLIC const int NEED_TO_SCHEDULE = 0x01;
+PUBLIC const int NEED_TO_CREATE = 0x01;
+PUBLIC const int NEED_TO_DESTROY = 0x01;
+PUBLIC const int NEED_TO_UPDATE = 0x01;
 
-EAPI const int LB_SYS_EVENT_FONT_CHANGED = 0x01;
-EAPI const int LB_SYS_EVENT_LANG_CHANGED = 0x02;
-EAPI const int LB_SYS_EVENT_TIME_CHANGED = 0x04;
-EAPI const int LB_SYS_EVENT_REGION_CHANGED = 0x08;
-EAPI const int LB_SYS_EVENT_PAUSED = 0x0100;
-EAPI const int LB_SYS_EVENT_RESUMED = 0x0200;
+PUBLIC const int LB_SYS_EVENT_FONT_CHANGED = 0x01;
+PUBLIC const int LB_SYS_EVENT_LANG_CHANGED = 0x02;
+PUBLIC const int LB_SYS_EVENT_TIME_CHANGED = 0x04;
+PUBLIC const int LB_SYS_EVENT_REGION_CHANGED = 0x08;
+PUBLIC const int LB_SYS_EVENT_PAUSED = 0x0100;
+PUBLIC const int LB_SYS_EVENT_RESUMED = 0x0200;
 
-EAPI struct livebox_desc *livebox_desc_open(const char *filename, int for_pd)
+PUBLIC struct livebox_desc *livebox_desc_open(const char *filename, int for_pd)
 {
        struct livebox_desc *handle;
        char *new_fname;
@@ -113,8 +114,11 @@ EAPI struct livebox_desc *livebox_desc_open(const char *filename, int for_pd)
                }
        }
 
-       DbgPrint("Open a new file: %s\n", new_fname);
-       handle->fp = fopen(new_fname, "w+t");
+       DbgPrint("Open a file %s with merge mode %s\n",
+                               new_fname,
+                               access(new_fname, F_OK) == 0 ? "enabled" : "disabled");
+
+       handle->fp = fopen(new_fname, "at");
        free(new_fname);
        if (!handle->fp) {
                ErrPrint("Failed to open a file: %s\n", strerror(errno));
@@ -125,7 +129,7 @@ EAPI struct livebox_desc *livebox_desc_open(const char *filename, int for_pd)
        return handle;
 }
 
-EAPI int livebox_desc_close(struct livebox_desc *handle)
+PUBLIC int livebox_desc_close(struct livebox_desc *handle)
 {
        struct dlist *l;
        struct dlist *n;
@@ -187,7 +191,7 @@ EAPI int livebox_desc_close(struct livebox_desc *handle)
        return LB_STATUS_SUCCESS;
 }
 
-EAPI int livebox_desc_set_category(struct livebox_desc *handle, const char *id, const char *category)
+PUBLIC int livebox_desc_set_category(struct livebox_desc *handle, const char *id, const char *category)
 {
        struct block *block;
 
@@ -235,7 +239,7 @@ EAPI int livebox_desc_set_category(struct livebox_desc *handle, const char *id,
        return block->idx;
 }
 
-EAPI int livebox_desc_set_size(struct livebox_desc *handle, const char *id, int w, int h)
+PUBLIC int livebox_desc_set_size(struct livebox_desc *handle, const char *id, int w, int h)
 {
        struct block *block;
        char buffer[BUFSIZ];
@@ -284,7 +288,7 @@ EAPI int livebox_desc_set_size(struct livebox_desc *handle, const char *id, int
        return block->idx;
 }
 
-EAPI char *livebox_util_nl2br(const char *str)
+PUBLIC char *livebox_util_nl2br(const char *str)
 {
        int len;
        register int i;
@@ -338,7 +342,7 @@ EAPI char *livebox_util_nl2br(const char *str)
        return ret;
 }
 
-EAPI int livebox_desc_set_id(struct livebox_desc *handle, int idx, const char *id)
+PUBLIC int livebox_desc_set_id(struct livebox_desc *handle, int idx, const char *id)
 {
        struct dlist *l;
        struct block *block;
@@ -372,7 +376,7 @@ EAPI int livebox_desc_set_id(struct livebox_desc *handle, int idx, const char *i
 /*!
  * \return idx
  */
-EAPI int livebox_desc_add_block(struct livebox_desc *handle, const char *id, const char *type, const char *part, const char *data, const char *option)
+PUBLIC int livebox_desc_add_block(struct livebox_desc *handle, const char *id, const char *type, const char *part, const char *data, const char *option)
 {
        struct block *block;
 
@@ -445,7 +449,7 @@ EAPI int livebox_desc_add_block(struct livebox_desc *handle, const char *id, con
        return block->idx;
 }
 
-EAPI int livebox_desc_del_block(struct livebox_desc *handle, int idx)
+PUBLIC int livebox_desc_del_block(struct livebox_desc *handle, int idx)
 {
        struct dlist *l;
        struct block *block;
@@ -467,7 +471,7 @@ EAPI int livebox_desc_del_block(struct livebox_desc *handle, int idx)
        return LB_STATUS_ERROR_NOT_EXIST;
 }
 
-EAPI struct livebox_buffer *livebox_acquire_buffer(const char *filename, int is_pd, int width, int height, int (*handler)(struct livebox_buffer *, enum buffer_event, double, double, double, void *), void *data)
+PUBLIC struct livebox_buffer *livebox_acquire_buffer(const char *filename, int is_pd, int width, int height, int (*handler)(struct livebox_buffer *, enum buffer_event, double, double, double, void *), void *data)
 {
        struct livebox_buffer_data *user_data;
        const char *pkgname;
@@ -513,7 +517,7 @@ EAPI struct livebox_buffer *livebox_acquire_buffer(const char *filename, int is_
        return handle;
 }
 
-EAPI int livebox_request_update(const char *filename)
+PUBLIC int livebox_request_update(const char *filename)
 {
        int uri_len;
        char *uri;
@@ -537,12 +541,12 @@ EAPI int livebox_request_update(const char *filename)
        return ret;
 }
 
-EAPI unsigned long livebox_pixmap_id(struct livebox_buffer *handle)
+PUBLIC unsigned long livebox_pixmap_id(struct livebox_buffer *handle)
 {
        return provider_buffer_pixmap_id(handle);
 }
 
-EAPI int livebox_release_buffer(struct livebox_buffer *handle)
+PUBLIC int livebox_release_buffer(struct livebox_buffer *handle)
 {
        struct livebox_buffer_data *user_data;
 
@@ -559,7 +563,7 @@ EAPI int livebox_release_buffer(struct livebox_buffer *handle)
        return provider_buffer_release(handle);
 }
 
-EAPI void *livebox_ref_buffer(struct livebox_buffer *handle)
+PUBLIC void *livebox_ref_buffer(struct livebox_buffer *handle)
 {
        struct livebox_buffer_data *user_data;
        void *data;
@@ -590,7 +594,7 @@ EAPI void *livebox_ref_buffer(struct livebox_buffer *handle)
        return data;
 }
 
-EAPI int livebox_unref_buffer(void *buffer)
+PUBLIC int livebox_unref_buffer(void *buffer)
 {
        if (!buffer)
                return LB_STATUS_ERROR_INVALID;
@@ -599,7 +603,7 @@ EAPI int livebox_unref_buffer(void *buffer)
        return provider_buffer_unref(buffer);
 }
 
-EAPI int livebox_sync_buffer(struct livebox_buffer *handle)
+PUBLIC int livebox_sync_buffer(struct livebox_buffer *handle)
 {
        struct livebox_buffer_data *user_data;
        const char *pkgname;
@@ -651,7 +655,7 @@ EAPI int livebox_sync_buffer(struct livebox_buffer *handle)
        return LB_STATUS_SUCCESS;
 }
 
-EAPI int livebox_support_hw_buffer(struct livebox_buffer *handle)
+PUBLIC int livebox_support_hw_buffer(struct livebox_buffer *handle)
 {
        if (!handle)
                return LB_STATUS_ERROR_INVALID;
@@ -659,7 +663,7 @@ EAPI int livebox_support_hw_buffer(struct livebox_buffer *handle)
        return provider_buffer_pixmap_is_support_hw(handle);
 }
 
-EAPI int livebox_create_hw_buffer(struct livebox_buffer *handle)
+PUBLIC int livebox_create_hw_buffer(struct livebox_buffer *handle)
 {
        struct livebox_buffer_data *user_data;
        int ret;
@@ -679,7 +683,7 @@ EAPI int livebox_create_hw_buffer(struct livebox_buffer *handle)
        return ret;
 }
 
-EAPI int livebox_destroy_hw_buffer(struct livebox_buffer *handle)
+PUBLIC int livebox_destroy_hw_buffer(struct livebox_buffer *handle)
 {
        struct livebox_buffer_data *user_data;
        if (!handle)
@@ -694,7 +698,7 @@ EAPI int livebox_destroy_hw_buffer(struct livebox_buffer *handle)
        return provider_buffer_pixmap_destroy_hw(handle);
 }
 
-EAPI void *livebox_buffer_hw_buffer(struct livebox_buffer *handle)
+PUBLIC void *livebox_buffer_hw_buffer(struct livebox_buffer *handle)
 {
        struct livebox_buffer_data *user_data;
 
@@ -708,7 +712,7 @@ EAPI void *livebox_buffer_hw_buffer(struct livebox_buffer *handle)
        return provider_buffer_pixmap_hw_addr(handle);
 }
 
-EAPI int livebox_buffer_pre_render(struct livebox_buffer *handle)
+PUBLIC int livebox_buffer_pre_render(struct livebox_buffer *handle)
 {
        struct livebox_buffer_data *user_data;
 
@@ -729,7 +733,7 @@ EAPI int livebox_buffer_pre_render(struct livebox_buffer *handle)
        return provider_buffer_pre_render(handle);
 }
 
-EAPI int livebox_buffer_post_render(struct livebox_buffer *handle)
+PUBLIC int livebox_buffer_post_render(struct livebox_buffer *handle)
 {
        int ret;
        const char *pkgname;
diff --git a/src/snapshot_window.c b/src/snapshot_window.c
new file mode 100644 (file)
index 0000000..3108487
--- /dev/null
@@ -0,0 +1,517 @@
+#include <Ecore_Evas.h>
+#include <Evas.h>
+#include <Ecore.h>
+#include <unistd.h>
+
+#include <dlog.h>
+#include <livebox-service.h>
+#include <livebox-errno.h>
+
+#include "livebox.h"
+#include "debug.h"
+
+#define QUALITY_N_COMPRESS "quality=100 compress=1"
+#define PUBLIC __attribute__((visibility("default")))
+
+struct snapshot_info {
+       char *id;
+       void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data);
+       void *data;
+
+       Ecore_Timer *flush_timer;
+
+       int render_cnt;
+       double timeout;
+};
+
+static void post_render_cb(void *data, Evas *e, void *event_info);
+static void pre_render_cb(void *data, Evas *e, void *event_info);
+
+
+
+static inline Evas *create_virtual_canvas(int w, int h)
+{
+        Ecore_Evas *internal_ee;
+        Evas *internal_e;
+
+        // Create virtual canvas
+        internal_ee = ecore_evas_buffer_new(w, h);
+        if (!internal_ee) {
+                LOGD("Failed to create a new canvas buffer\n");
+                return NULL;
+        }
+
+       ecore_evas_alpha_set(internal_ee, EINA_TRUE);
+       ecore_evas_manual_render_set(internal_ee, EINA_FALSE);
+
+        // Get the "Evas" object from a virtual canvas
+        internal_e = ecore_evas_get(internal_ee);
+        if (!internal_e) {
+                ecore_evas_free(internal_ee);
+                LOGD("Faield to get Evas object\n");
+                return NULL;
+        }
+
+        return internal_e;
+}
+
+
+
+static inline int flush_data_to_file(Evas *e, char *data, const char *filename, int w, int h)
+{
+        Evas_Object *output;
+
+        output = evas_object_image_add(e);
+        if (!output) {
+               LOGD("Failed to create an image object\n");
+                return LB_STATUS_ERROR_FAULT;
+        }
+
+        evas_object_image_data_set(output, NULL);
+        evas_object_image_colorspace_set(output, EVAS_COLORSPACE_ARGB8888);
+        evas_object_image_alpha_set(output, EINA_TRUE);
+        evas_object_image_size_set(output, w, h);
+        evas_object_image_smooth_scale_set(output, EINA_TRUE);
+        evas_object_image_data_set(output, data);
+       evas_object_image_fill_set(output, 0, 0, w, h);
+        evas_object_image_data_update_add(output, 0, 0, w, h);
+
+        if (evas_object_image_save(output, filename, NULL, QUALITY_N_COMPRESS) == EINA_FALSE) {
+                evas_object_del(output);
+               LOGD("Faield to save a captured image (%s)\n", filename);
+                return LB_STATUS_ERROR_IO;
+        }
+
+       evas_object_del(output);
+
+        if (access(filename, F_OK) != 0) {
+               LOGD("File %s is not found\n", filename);
+                return LB_STATUS_ERROR_IO;
+        }
+
+       LOGD("Flush data to a file (%s)\n", filename);
+       return LB_STATUS_SUCCESS;
+}
+
+
+
+static inline int destroy_virtual_canvas(Evas *e)
+{
+        Ecore_Evas *ee;
+
+        ee = ecore_evas_ecore_evas_get(e);
+        if (!ee) {
+               LOGD("Failed to ecore evas object\n");
+                return LB_STATUS_ERROR_FAULT;
+        }
+
+        ecore_evas_free(ee);
+        return LB_STATUS_SUCCESS;
+}
+
+
+
+static inline int flush_to_file(void *canvas, const char *filename, int w, int h)
+{
+       int status;
+       Evas *shot_e;
+       Ecore_Evas *shot_ee;
+
+       shot_e = create_virtual_canvas(w, h);
+       if (!shot_e) {
+               LOGE("Unable to create a new virtual window\n");
+               return LB_STATUS_ERROR_FAULT;
+       }
+
+       shot_ee = ecore_evas_ecore_evas_get(shot_e);
+       if (!shot_ee) {
+               LOGE("Unable to get Ecore_Evas\n");
+               destroy_virtual_canvas(shot_e);
+               return LB_STATUS_ERROR_FAULT;
+       }
+
+       ecore_evas_manual_render_set(shot_ee, EINA_TRUE);
+
+       status = flush_data_to_file(shot_e, canvas, filename, w, h);
+       destroy_virtual_canvas(shot_e);
+
+       return status;
+}
+
+
+
+static void del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+       struct snapshot_info *info;
+
+       info = evas_object_data_del(obj, "snapshot,info");
+       if (!info)
+               return;
+
+       LOGD("Delete object (%s)\n", info->id);
+
+       if (info->flush_cb) {
+               info->flush_cb(obj, info->id, LB_STATUS_ERROR_CANCEL, info->data);
+               LOGD("Flush is canceled\n");
+       }
+
+       /*!
+        * \note
+        * Render callback will be deleted.
+        */
+       destroy_virtual_canvas(e);
+       free(info->id);
+       free(info);
+}
+
+
+
+static Eina_Bool direct_snapshot_cb(void *data)
+{
+       Evas *e;
+       Ecore_Evas *ee;
+       void *canvas;
+       int status;
+       int w;
+       int h;
+       void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data);
+       Evas_Object *snapshot_win = data;
+       struct snapshot_info *info;
+
+       info = evas_object_data_get(snapshot_win, "snapshot,info");
+       if (!info) {
+               LOGE("Unable to get snapshot info\n");
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       info->flush_timer = NULL;
+       flush_cb = info->flush_cb;
+       info->flush_cb = NULL; /* To prevent call this from the delete callback */
+
+       e = evas_object_evas_get(snapshot_win);
+       if (!e) {
+               LOGE("Invalid object, failed to get Evas\n");
+               if (flush_cb)
+                       flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       ee = ecore_evas_ecore_evas_get(e);
+       if (!ee) {
+               LOGE("Unable to get Ecore_Evas object\n");
+               if (flush_cb)
+                       flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
+       ecore_evas_manual_render_set(ee, EINA_TRUE);
+
+       canvas = ecore_evas_buffer_pixels_get(ee);
+       if (!canvas) {
+               LOGE("Failed to get canvas\n");
+               if (flush_cb)
+                       flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       status = flush_to_file(canvas, info->id, w, h);
+       if (flush_cb)
+               flush_cb(snapshot_win, info->id, status, info->data);
+       return ECORE_CALLBACK_CANCEL;
+}
+
+
+
+static Eina_Bool snapshot_cb(void *data)
+{
+       Evas_Object *snapshot_win = data;
+       struct snapshot_info *info;
+       Evas *e;
+       Ecore_Evas *ee;
+       void *canvas;
+       void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data);
+
+       info = evas_object_data_get(snapshot_win, "snapshot,info");
+       if (!info) {
+               LOGE("Invalid object\n");
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       info->flush_timer = NULL;
+       flush_cb = info->flush_cb;
+       info->flush_cb = NULL; /* To prevent call this from the delete callback */
+
+       e = evas_object_evas_get(snapshot_win);
+       if (!e) {
+               LOGE("Invalid object\n");
+               if (flush_cb)
+                       flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       ee = ecore_evas_ecore_evas_get(e);
+       if (!ee) {
+               LOGE("Invalid object (ee)\n");
+               if (flush_cb)
+                       flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       canvas = (void*)ecore_evas_buffer_pixels_get(ee);
+       if (!canvas) {
+               LOGD("Failed to get pixel canvas\n");
+               if (flush_cb)
+                       flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       if (flush_cb) {
+               int w;
+               int h;
+               int status;
+
+               ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
+
+               LOGD("Flush size: %dx%d\n", w, h);
+               status = flush_to_file(canvas, info->id, w, h);
+
+               flush_cb(snapshot_win, info->id, status, info->data);
+               /*!
+                * Do not access info after this.
+                */
+       }
+
+       return ECORE_CALLBACK_CANCEL;
+}
+
+
+
+static void post_render_cb(void *data, Evas *e, void *event_info)
+{
+       Evas_Object *snapshot_win = data;
+       struct snapshot_info *info;
+
+       info = evas_object_data_get(snapshot_win, "snapshot,info");
+       if (!info) {
+               LOGE("snapshot info is not valid\n");
+               return;
+       }
+
+       info->render_cnt++;
+
+       if (info->flush_timer) {
+               /*!
+                * This has not to be happens.
+                */
+               LOGE("Flush timer is not cleared\n");
+               ecore_timer_del(info->flush_timer);
+               info->flush_timer = NULL;
+       }
+
+       if (!info->flush_cb) {
+               LOGD("Flush request is not initiated yet\n");
+               return;
+       }
+
+       /*!
+        * \NOTE
+        * Even if tehre is no timer registered, we should capture the content
+        * from out of this callback.
+        * Or we can met unexpected problems.
+        * To avoid it, use the 0.0001f to get timer callback ASAP, not in this function.
+        */
+       LOGD("Fire the flush timer %lf (%d)\n", info->timeout, info->render_cnt);
+       info->flush_timer = ecore_timer_add(info->timeout, snapshot_cb, snapshot_win);
+       if (!info->flush_timer) {
+               void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data);
+
+               LOGE("Unalbe to add timer for getting the snapshot\n");
+               flush_cb = info->flush_cb;
+               info->flush_cb = NULL;
+
+               flush_cb(snapshot_win, info->id, LB_STATUS_ERROR_FAULT, info->data);
+               /*!
+                * \note
+                * Do not access info after from here.
+                */
+       }
+}
+
+
+
+static void pre_render_cb(void *data, Evas *e, void *event_info)
+{
+       Evas_Object *snapshot_win = data;
+       struct snapshot_info *info;
+
+       info = evas_object_data_get(snapshot_win, "snapshot,info");
+       if (!info) {
+               LOGE("snapshot info is not valid\n");
+               return;
+       }
+
+       if (info->flush_timer) {
+               ecore_timer_del(info->flush_timer);
+               info->flush_timer = NULL;
+               LOGD("Clear the flush timer\n");
+       }
+
+       LOGD("Pre-render callback\n");
+}
+
+
+
+static void resize_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+       Ecore_Evas *ee;
+       int w;
+       int h;
+       int ow;
+       int oh;
+
+       ee = ecore_evas_ecore_evas_get(e);
+       if (!ee)
+               return;
+
+       ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
+       evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
+       if (ow == w && oh == h) {
+               LOGD("Size is not changed: %dx%d\n", w, h);
+               return;
+       }
+
+       /*!
+        * Box(parent object) is resized.
+        * Try to resize the canvas too.
+        */
+       ecore_evas_resize(ee, w, h);
+       LOGD("Canvas is resized to %dx%d\n", w, h);
+}
+
+
+
+PUBLIC Evas_Object *livebox_snapshot_window_add(const char *id, int size_type)
+{
+       struct snapshot_info *info;
+       Evas_Object *snapshot_win;
+       Evas *e;
+       int w;
+       int h;
+
+       if (livebox_service_get_size(size_type, &w, &h) != LB_STATUS_SUCCESS) {
+               LOGE("Invalid size\n");
+               return NULL;
+       }
+
+       info = malloc(sizeof(*info));
+       if (!info) {
+               LOGE("Heap: %s\n", strerror(errno));
+               return NULL;
+       }
+
+       info->id = strdup(id);
+       if (!info->id) {
+               LOGE("Heap: %s\n", strerror(errno));
+               free(info);
+               return NULL;
+       }
+
+       info->flush_cb = NULL;
+       info->data = NULL;
+       info->flush_timer = NULL;
+       info->render_cnt = 0;
+
+       e = create_virtual_canvas(w, h);
+       if (!e) {
+               free(info->id);
+               free(info);
+               return NULL;
+       }
+
+       snapshot_win = evas_object_rectangle_add(e);
+       if (!snapshot_win) {
+               destroy_virtual_canvas(e);
+               free(info->id);
+               free(info);
+               return NULL;
+       }
+
+       LOGD("Add new window %dx%d\n", w, h);
+       evas_object_event_callback_add(snapshot_win, EVAS_CALLBACK_DEL, del_cb, NULL);
+       evas_object_event_callback_add(snapshot_win, EVAS_CALLBACK_RESIZE, resize_cb, NULL);
+       evas_object_resize(snapshot_win, w, h);
+       evas_object_show(snapshot_win);
+
+       evas_object_data_set(snapshot_win, "snapshot,info", info);
+
+       evas_event_callback_add(e, EVAS_CALLBACK_RENDER_POST, post_render_cb, snapshot_win);
+       evas_event_callback_add(e, EVAS_CALLBACK_RENDER_PRE, pre_render_cb, snapshot_win);
+
+       return snapshot_win;
+}
+
+
+
+PUBLIC int livebox_snapshot_window_flush(Evas_Object *snapshot_win, double timeout, void (*flush_cb)(Evas_Object *snapshot_window, const char *id, int status, void *data), void *data)
+{
+       struct snapshot_info *info;
+
+       if (!flush_cb || timeout < 0.0f) {
+               LOGE("Invalid argument (%p, %lf)\n", flush_cb, timeout);
+               return LB_STATUS_ERROR_INVALID;
+       }
+
+       info = evas_object_data_get(snapshot_win, "snapshot,info");
+       if (!info) {
+               LOGE("Invalid argument\n");
+               return LB_STATUS_ERROR_INVALID;
+       }
+
+       info->timeout = timeout;
+
+       if (timeout == 0.0f) {
+               /*!
+                * This timer is just used for guarantees same behavious even if it flushes content directly,
+                * The callback should be called from next loop.
+                * or the developer will get confused
+                */
+               info->flush_timer = ecore_timer_add(0.0001f, direct_snapshot_cb, snapshot_win);
+               if (!info->flush_timer)
+                       return LB_STATUS_ERROR_FAULT;
+       } else if (info->render_cnt) {
+               /*!
+                * Try to watit pre-render callback.
+                * If there is rendered contents.
+                */
+               DbgPrint("Rendered %d times already\n", info->render_cnt);
+               info->flush_timer = ecore_timer_add(info->timeout, snapshot_cb, snapshot_win);
+               if (!info->flush_timer)
+                       return LB_STATUS_ERROR_FAULT;
+       }
+
+       info->flush_cb = flush_cb;
+       info->data = data;
+
+       return LB_STATUS_SUCCESS;
+}
+
+
+
+PUBLIC int livebox_snapshot_window_del(Evas_Object *snapshot_win)
+{
+       Evas *e;
+       if (!snapshot_win || !evas_object_data_get(snapshot_win, "snapshot,info"))
+               return LB_STATUS_ERROR_INVALID;
+
+       e = evas_object_evas_get(snapshot_win);
+       evas_event_callback_del(e, EVAS_CALLBACK_RENDER_POST, post_render_cb);
+       evas_event_callback_del(e, EVAS_CALLBACK_RENDER_PRE, pre_render_cb);
+
+       evas_object_del(snapshot_win);
+       return LB_STATUS_SUCCESS;
+}
+
+
+
+// End of a file
diff --git a/src/virtual_window.c b/src/virtual_window.c
new file mode 100644 (file)
index 0000000..328126d
--- /dev/null
@@ -0,0 +1,265 @@
+#include <Ecore_Evas.h>
+#include <Evas.h>
+
+#include <dlog.h>
+#include <livebox-errno.h>
+
+#include "livebox.h"
+
+#define IS_PD 1
+
+#define PUBLIC __attribute__((visibility("default")))
+/*!
+ * \brief
+ * Abstracted Data Type of Virtual Window
+ */
+struct info {
+       char *id; /*!< Identification */
+       int width; /*!< Width */
+       int height; /*!< Height */
+       struct livebox_buffer *handle; /*!< Livebox buffer handle */
+       Evas_Object *window; /*!< Parent evas object - WARN: Incompatible with the elm_win object */
+       int is_hw; /*!< 1 if a buffer is created on the H/W accelerated place or 0 */
+};
+
+/*!
+ * \note
+ * Every user event (mouse) on the buffer will be passed via this event callback
+ */
+static int event_handler_cb(struct livebox_buffer *handler, enum buffer_event evt, double timestamp, double x, double y, void *data)
+{
+       struct info *info = data;
+       Evas *e;
+       int ix;
+       int iy;
+
+       if (!info->handle) {
+               /* Just ignore this event */
+               return 0;
+       }
+
+       /*!
+        * \note
+        * Calculate the event occurred X & Y on the buffer
+        */
+       ix = info->width * x;
+       iy = info->height * y;
+
+       e = evas_object_evas_get(info->window);
+
+       /*!
+        * \note
+        * Feed up events
+        */
+       switch (evt) {
+       case BUFFER_EVENT_ENTER:
+               evas_event_feed_mouse_in(e, timestamp, NULL);
+               break;
+       case BUFFER_EVENT_LEAVE:
+               evas_event_feed_mouse_out(e, timestamp, NULL);
+               break;
+       case BUFFER_EVENT_DOWN:
+               evas_event_feed_mouse_in(e, timestamp, NULL);
+               evas_event_feed_mouse_move(e, ix, iy, timestamp + 0.01f, NULL); /* + 0.1f just for fake event */
+               evas_event_feed_mouse_down(e, 1, EVAS_BUTTON_NONE, timestamp + 0.02f, NULL); /* + 0.2f just for fake event */
+               break;
+       case BUFFER_EVENT_MOVE:
+               evas_event_feed_mouse_move(e, ix, iy, timestamp, NULL);
+               break;
+       case BUFFER_EVENT_UP:
+               evas_event_feed_mouse_up(e, 1, EVAS_BUTTON_NONE, timestamp, NULL);
+               evas_event_feed_mouse_out(e, timestamp + 0.01f, NULL); /* + 0.1f just for fake event */
+               break;
+       default:
+               LOGD("Unhandled buffer event (%d)\n", evt);
+               break;
+       }
+
+       return 0;
+}
+
+static void *alloc_fb(void *data, int size)
+{
+       struct info *info = data;
+       void *buffer;
+
+       /*!
+        * Acquire a buffer for canvas.
+        */
+       info->handle = livebox_acquire_buffer(info->id, IS_PD,
+                                       info->width, info->height,
+                                       event_handler_cb, info);
+
+       /*!
+        * If it supports the H/W accelerated buffer,
+        * Use it.
+        */
+       if (livebox_support_hw_buffer(info->handle)) {
+               if (livebox_create_hw_buffer(info->handle) == 0) {
+                       buffer = livebox_buffer_hw_buffer(info->handle);
+                       if (buffer) {
+                               LOGD("HW Accelerated buffer is created\n");
+                               info->is_hw = 1;
+                               return buffer;
+                       }
+               }
+
+               LOGE("Failed to allocate HW Accelerated buffer\n");
+       }
+
+       /*!
+        * Or use the buffer of a S/W backend.
+        */
+       buffer = livebox_ref_buffer(info->handle);
+       LOGD("SW buffer is created\n");
+       info->is_hw = 0;
+       return buffer;
+}
+
+static void free_fb(void *data, void *ptr)
+{
+       struct info *info = data;
+
+       if (!info->handle)
+               return;
+
+       if (info->is_hw) {
+               if (livebox_destroy_hw_buffer(info->handle) == 0) {
+                       LOGD("HW Accelerated buffer is destroyed\n");
+                       goto out;
+               }
+       }
+
+       livebox_unref_buffer(ptr);
+       LOGD("SW buffer is destroyed\n");
+out:
+       livebox_release_buffer(info->handle);
+       info->handle = NULL;
+}
+
+static void resize_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+       struct info *info = data;
+       Ecore_Evas *ee;
+
+       ee = ecore_evas_ecore_evas_get(e);
+       if (!ee)
+               return;
+
+       evas_object_geometry_get(obj, NULL, NULL, &info->width, &info->height);
+       LOGD("Resize to %dx%d\n", info->width, info->height);
+       /*!
+        * Box(parent object) is resized.
+        * Try to resize the canvas too.
+        */
+       ecore_evas_resize(ee, info->width, info->height);
+}
+
+/*!
+ * If a canvas is destroyed,
+ * Free all buffer of canvas.
+ */
+static void del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+       Ecore_Evas *ee;
+       struct info *info = data;
+
+       ee = ecore_evas_ecore_evas_get(e);
+       if (!ee)
+               return;
+
+       LOGD("Try to release the ECORE_EVAS\n");
+       ecore_evas_free(ee);
+       free(info->id);
+       free(info);
+       LOGD("ECORE_EVAS is released\n");
+}
+
+static void pre_render_cb(void *data, Evas *e, void *event_info)
+{
+       struct info *info = data;
+
+       if (!info->handle)
+               return;
+
+       if (info->is_hw)
+               livebox_buffer_pre_render(info->handle);
+}
+
+static void post_render_cb(void *data, Evas *e, void *event_info)
+{
+       struct info *info = data;
+
+       if (!info->handle)
+               return;
+
+       if (info->is_hw)
+               livebox_buffer_post_render(info->handle);
+       else
+               livebox_sync_buffer(info->handle);
+}
+
+PUBLIC Evas_Object *livebox_virtual_window_add(const char *id, int width, int height)
+{
+       Ecore_Evas *ee;
+       Evas *e;
+       struct info *info;
+
+       info = calloc(1, sizeof(*info));
+       if (!info) {
+               return NULL;
+       }
+
+       info->id = strdup(id);
+       if (!info->id) {
+               free(info);
+               return NULL;
+       }
+
+       info->width = width;
+       info->height = height;
+
+       ee = ecore_evas_buffer_allocfunc_new(width, height, alloc_fb, free_fb, info);
+       if (!ee) {
+               free(info->id);
+               free(info);
+               return NULL;
+       }
+
+       pre_render_cb(info, NULL, NULL);
+       ecore_evas_alpha_set(ee, EINA_TRUE);
+       post_render_cb(info, NULL, NULL);
+       
+       ecore_evas_manual_render_set(ee, EINA_FALSE);
+       ecore_evas_resize(ee, width, height);
+
+       e = ecore_evas_get(ee);
+       if (!e) {
+               ecore_evas_free(ee);
+               return NULL;
+       }
+
+       evas_event_callback_add(e, EVAS_CALLBACK_RENDER_POST, post_render_cb, info);
+       evas_event_callback_add(e, EVAS_CALLBACK_RENDER_PRE, pre_render_cb, info);
+
+       info->window = evas_object_rectangle_add(e);
+       if (!info->window) {
+               ecore_evas_free(ee);
+               return NULL;
+       }
+
+       evas_object_resize(info->window, width, height);
+       evas_object_color_set(info->window, 0, 0, 0, 0);
+       evas_object_event_callback_add(info->window, EVAS_CALLBACK_DEL, del_cb, info);
+       evas_object_event_callback_add(info->window, EVAS_CALLBACK_RESIZE, resize_cb, info);
+
+       return info->window;
+}
+
+PUBLIC int livebox_virtual_window_del(Evas_Object *virtual_win)
+{
+       evas_object_del(virtual_win);
+       return LB_STATUS_SUCCESS;
+}
+
+/* End of a file */