move dpms, eom, screenshooter, video, viewport to enlightenment 29/137629/4 accepted/tizen/unified/20170712.165234 submit/tizen/20170712.081425
authorBoram Park <boram1288.park@samsung.com>
Thu, 29 Jun 2017 08:01:23 +0000 (17:01 +0900)
committerBoram Park <boram1288.park@samsung.com>
Wed, 12 Jul 2017 06:05:36 +0000 (15:05 +0900)
Change-Id: I0b3fc6229b5c70eaecefe84225926c99fb7ad3fd

22 files changed:
configure.ac
packaging/enlightenment.spec
src/bin/Makefile.mk
src/bin/e_comp_screen.c
src/bin/e_comp_screen.h
src/bin/e_comp_wl.c
src/bin/e_comp_wl_screenshooter.c [new file with mode: 0644]
src/bin/e_comp_wl_screenshooter.h [new file with mode: 0644]
src/bin/e_comp_wl_video.c [new file with mode: 0644]
src/bin/e_comp_wl_video.h [new file with mode: 0644]
src/bin/e_comp_wl_video_buffer.c [new file with mode: 0644]
src/bin/e_comp_wl_video_buffer.h [new file with mode: 0644]
src/bin/e_comp_wl_viewport.c [new file with mode: 0644]
src/bin/e_comp_wl_viewport.h [new file with mode: 0644]
src/bin/e_config.c
src/bin/e_config.h
src/bin/e_dpms.c
src/bin/e_dpms.h
src/bin/e_eom.c [new file with mode: 0644]
src/bin/e_eom.h [new file with mode: 0644]
src/bin/e_includes.h
src/bin/e_main.c

index ff856ca..09480cd 100755 (executable)
@@ -376,7 +376,7 @@ AC_MSG_RESULT([${have_shm_open}])
 AC_SUBST(SHM_OPEN_LIBS)
                            
 if test "x${e_cv_want_wayland_only}" != "xno" || test "x${e_cv_want_wayland_clients}" != "xno";then
-  PKG_CHECK_MODULES([WAYLAND], [wayland-server >= 1.8.0 xkbcommon uuid xdg-shell-server scaler-server transform-server screenshooter-server tizen-extension-server tizen-launch-server],
+  PKG_CHECK_MODULES([WAYLAND], [wayland-server >= 1.8.0 xkbcommon uuid xdg-shell-server scaler-server transform-server screenshooter-server tizen-extension-server tizen-launch-server eom-server],
     [
       have_wayland=yes
       AC_DEFINE_UNQUOTED([HAVE_WAYLAND],[1],[enable wayland support])
index ea13160..103a6c8 100755 (executable)
@@ -39,6 +39,7 @@ BuildRequires:  pkgconfig(tizen-extension-server)
 BuildRequires:  pkgconfig(tizen-launch-server)
 BuildRequires:  pkgconfig(wayland-tbm-server)
 BuildRequires:  pkgconfig(tizen-remote-surface-server)
+BuildRequires:  pkgconfig(eom-server)
 BuildRequires:  pkgconfig(ecore-drm)
 BuildRequires:  pkgconfig(libtdm)
 BuildRequires:  pkgconfig(gbm)
index a3f19ff..5357479 100644 (file)
@@ -41,6 +41,7 @@ src/bin/e_dialog.h \
 src/bin/e_dnd.h \
 src/bin/e_dpms.h \
 src/bin/e_env.h \
+src/bin/e_eom.h \
 src/bin/e_error.h \
 src/bin/e_focus.h \
 src/bin/e_grabinput.h \
@@ -96,6 +97,10 @@ endif
 
 ENLIGHTENMENTHEADERS += \
 src/bin/e_comp_wl_rsm.h \
+src/bin/e_comp_wl_video.h \
+src/bin/e_comp_wl_video_buffer.h \
+src/bin/e_comp_wl_viewport.h \
+src/bin/e_comp_wl_screenshooter.h \
 src/bin/services/e_service_gesture.h \
 src/bin/services/e_service_lockscreen.h \
 src/bin/services/e_service_quickpanel.h \
@@ -132,6 +137,7 @@ src/bin/e_dialog.c \
 src/bin/e_dpms.c \
 src/bin/e_dnd.c \
 src/bin/e_env.c \
+src/bin/e_eom.c \
 src/bin/e_error.c \
 src/bin/e_focus.c \
 src/bin/e_grabinput.c \
@@ -186,6 +192,10 @@ endif
 
 enlightenment_src += \
 src/bin/e_comp_wl_rsm.c \
+src/bin/e_comp_wl_video.c \
+src/bin/e_comp_wl_video_buffer.c \
+src/bin/e_comp_wl_viewport.c \
+src/bin/e_comp_wl_screenshooter.c \
 src/bin/services/e_service_gesture.c \
 src/bin/services/e_service_lockscreen.c \
 src/bin/services/e_service_quickpanel.c \
index aa96404..40c3879 100644 (file)
@@ -339,10 +339,21 @@ _e_comp_screen_new(E_Comp *comp)
         return NULL;
      }
 
+   /* tdm display init */
+   e_comp_screen->bufmgr = tbm_bufmgr_init(-1);
+   if (!e_comp_screen->bufmgr)
+     {
+        ERR("tbm_bufmgr_init failed\n");
+        tdm_display_deinit(e_comp_screen->tdisplay);
+        free(e_comp_screen);
+        return NULL;
+     }
+
    error = tdm_display_get_capabilities(e_comp_screen->tdisplay, &capabilities);
    if (error != TDM_ERROR_NONE)
      {
         ERR("tdm get_capabilities failed");
+        tbm_bufmgr_deinit(e_comp_screen->bufmgr);
         tdm_display_deinit(e_comp_screen->tdisplay);
         free(e_comp_screen);
         return NULL;
@@ -363,6 +374,7 @@ _e_comp_screen_del(E_Comp_Screen *e_comp_screen)
 {
    if (!e_comp_screen) return;
 
+   if (e_comp_screen->bufmgr) tbm_bufmgr_deinit(e_comp_screen->bufmgr);
    if (e_comp_screen->tdisplay) tdm_display_deinit(e_comp_screen->tdisplay);
 
    free(e_comp_screen);
index 3d405ab..644fa1f 100644 (file)
@@ -20,6 +20,7 @@ struct _E_Comp_Screen
 
    int            num_outputs;
    tdm_display   *tdisplay;
+   tbm_bufmgr     bufmgr;
 
    /* for sw compositing */
    const Eina_List *devices;
index dc97ad5..4c8861e 100644 (file)
@@ -4825,6 +4825,10 @@ e_comp_wl_init(void)
 
    e_pixmap_init();
 
+   e_comp_wl_screenshooter_init();
+   e_comp_wl_video_init();
+   e_comp_wl_viewport_init();
+
    /* add event handlers to catch E events */
    E_LIST_HANDLER_APPEND(handlers, E_EVENT_SCREEN_CHANGE,            _e_comp_wl_cb_randr_change,        NULL);
    E_LIST_HANDLER_APPEND(handlers, E_EVENT_COMP_OBJECT_ADD,         _e_comp_wl_cb_comp_object_add,     NULL);
@@ -4879,6 +4883,10 @@ e_comp_wl_shutdown(void)
    E_FREE_LIST(hooks, e_client_hook_del);
    _e_comp_wl_gl_shutdown();
 
+   e_comp_wl_viewport_shutdown();
+   e_comp_wl_video_shutdown();
+   e_comp_wl_screenshooter_shutdown();
+
 #ifdef HAVE_WAYLAND_TBM
    e_comp_wl_tbm_shutdown();
 #endif
diff --git a/src/bin/e_comp_wl_screenshooter.c b/src/bin/e_comp_wl_screenshooter.c
new file mode 100644 (file)
index 0000000..bdd221b
--- /dev/null
@@ -0,0 +1,1860 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "e.h"
+#include <wayland-server.h>
+#include <Ecore_Wayland.h>
+#include <Ecore_Drm.h>
+#include <screenshooter-server-protocol.h>
+#include <tizen-extension-server-protocol.h>
+#include <tdm.h>
+#ifdef HAVE_CYNARA
+#include <cynara-session.h>
+#include <cynara-client.h>
+#include <cynara-creds-socket.h>
+#endif
+
+#define DUMP_FPS     30
+
+typedef struct _E_Mirror
+{
+   struct wl_resource *resource;
+   struct wl_resource *shooter;
+   struct wl_resource *output;
+
+   Eina_Bool started;
+   enum tizen_screenmirror_stretch stretch;
+
+   Eina_List *buffer_queue;
+   E_Comp_Wl_Output *wl_output;
+   Ecore_Drm_Output *drm_output;
+   Ecore_Drm_Device *drm_device;
+
+   tdm_output *tdm_output;
+   tdm_layer *tdm_primary_layer;
+   tdm_capture *capture;
+   Ecore_Timer *capture_timer;
+
+   /* vblank info */
+   int per_vblank;
+   Eina_Bool wait_vblank;
+
+   /* timer info when dpms off */
+   Ecore_Timer *timer;
+
+   /* converter info */
+   tdm_pp *pp;
+   Eina_List *ui_buffer_list;
+   Eina_List *buffer_clear_check;
+
+   /* rotation info */
+   int eout_rotate;
+   int angle;
+   Eina_Bool rotate_change;
+
+   struct wl_listener client_destroy_listener;
+
+   Eina_Bool oneshot_client_destroy;
+#ifdef HAVE_CYNARA
+   cynara *p_cynara;
+   Eina_Bool cynara_initialized;
+#endif
+} E_Mirror;
+
+typedef struct _E_Mirror_Buffer
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+   E_Comp_Wl_Video_Buf *tmp;
+
+   E_Mirror *mirror;
+
+   Eina_Bool in_use;
+   Eina_Bool dirty;
+
+   /* in case of shm buffer */
+   struct wl_listener destroy_listener;
+} E_Mirror_Buffer;
+
+static uint mirror_format_table[] =
+{
+   TBM_FORMAT_ARGB8888,
+   TBM_FORMAT_XRGB8888,
+   TBM_FORMAT_NV12,
+   TBM_FORMAT_NV21,
+};
+
+#define NUM_MIRROR_FORMAT   (sizeof(mirror_format_table) / sizeof(mirror_format_table[0]))
+
+static E_Mirror *keep_stream_mirror;
+static Eina_Bool screenshot_auto_rotation;
+static Eina_List *mirror_list;
+
+static void _e_tz_screenmirror_destroy(E_Mirror *mirror);
+static void _e_tz_screenmirror_buffer_dequeue(E_Mirror_Buffer *buffer);
+static void _e_tz_screenmirror_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data);
+static void _e_tz_screenmirror_vblank_handler(void *data);
+
+#ifdef HAVE_CYNARA
+static void _e_screenmirror_cynara_log(const char *func_name, int err);
+
+#define PRIVILEGE_SCREENSHOT "http://tizen.org/privilege/screenshot"
+#define SMACK_LABEL_LEN 255
+#define PATH_MAX_LEN 64
+#define CYNARA_BUFSIZE 128
+#define E_SCREENMIRROR_CYNARA_ERROR_CHECK_GOTO(func_name, ret, label) \
+   do \
+     { \
+        if (EINA_UNLIKELY(ret != CYNARA_API_SUCCESS)) \
+          { \
+             _e_screenmirror_cynara_log(func_name, ret); \
+             goto label; \
+          } \
+     } \
+   while (0)
+
+static void
+_e_screenmirror_cynara_log(const char *func_name, int err)
+{
+   char buf[CYNARA_BUFSIZE] = "\0";
+   int ret;
+
+   ret = cynara_strerror(err, buf, CYNARA_BUFSIZE);
+   if (ret != CYNARA_API_SUCCESS)
+     {
+        DBG("Failed to cynara_strerror: %d (error log about %s: %d)\n", ret, func_name, err);
+        return;
+     }
+   DBG("%s is failed: %s\n", func_name, buf);
+}
+
+static Eina_Bool
+_e_screenmirror_su_check(struct wl_client *client)
+{
+   uid_t uid;
+
+   wl_client_get_credentials(client, NULL, &uid, NULL);
+
+   if (uid == 0) /* DBG("pass privilege check if super user"); */
+     return EINA_TRUE;
+
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_e_screenmirror_privilege(struct wl_client *client, cynara *p_cynara, int socket_fd, const char *rule)
+{
+   int ret, pid;
+   char *clientSmack = NULL, *uid = NULL, *client_session = NULL;
+   Eina_Bool res = EINA_FALSE;
+
+   ret = cynara_creds_socket_get_user(socket_fd, USER_METHOD_UID, &uid);
+   E_SCREENMIRROR_CYNARA_ERROR_CHECK_GOTO("cynara_creds_socket_get_user", ret, finish);
+
+   ret = cynara_creds_socket_get_pid(socket_fd, &pid);
+   E_SCREENMIRROR_CYNARA_ERROR_CHECK_GOTO("cynara_creds_socket_get_pid", ret, finish);
+
+   client_session = cynara_session_from_pid(pid);
+
+   ret = cynara_creds_socket_get_client(socket_fd, CLIENT_METHOD_SMACK, &clientSmack);
+   E_SCREENMIRROR_CYNARA_ERROR_CHECK_GOTO("cynara_creds_socket_get_client", ret, finish);
+
+   ret = cynara_check(p_cynara, clientSmack, client_session, uid, rule);
+
+   if (ret == CYNARA_API_ACCESS_ALLOWED)
+     res = EINA_TRUE;
+
+finish:
+   E_FREE(client_session);
+   E_FREE(clientSmack);
+   E_FREE(uid);
+
+   return res;
+}
+#endif
+
+static Eina_Bool
+_e_screenmirror_privilege_check(struct wl_client *client, E_Mirror *mirror, int socket_fd, const char *rule)
+{
+#ifdef HAVE_CYNARA
+   Eina_Bool res = EINA_FALSE;
+
+   if (mirror->p_cynara == NULL && !mirror->cynara_initialized) return EINA_TRUE;
+
+   if (_e_screenmirror_su_check(client) == EINA_TRUE) return EINA_TRUE;
+
+   res = _e_screenmirror_privilege(client, mirror->p_cynara, socket_fd, rule);
+
+   return res;
+#else
+   return EINA_TRUE;
+#endif
+}
+
+static Eina_Bool
+_e_screenmirror_privilege_check_with_cynara_init(struct wl_client *client, int socket_fd, const char *rule)
+{
+#ifdef HAVE_CYNARA
+   int ret;
+   Eina_Bool res = EINA_FALSE;
+   cynara *p_cynara;
+
+   if (_e_screenmirror_su_check(client) == EINA_TRUE) return EINA_TRUE;
+
+   ret = cynara_initialize(&p_cynara, NULL);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == CYNARA_API_SUCCESS, EINA_FALSE);
+
+   res = _e_screenmirror_privilege(client, p_cynara, socket_fd, rule);
+
+   if (p_cynara) cynara_finish(p_cynara);
+
+   return res;
+#else
+   return EINA_TRUE;
+#endif
+}
+
+static void
+_e_tz_screenmirror_center_rect (int src_w, int src_h, int dst_w, int dst_h, Eina_Rectangle *fit)
+{
+   float rw, rh;
+
+   if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0 || !fit)
+     return;
+
+   rw = (float)src_w / dst_w;
+   rh = (float)src_h / dst_h;
+
+   if (rw > rh)
+     {
+        fit->w = dst_w;
+        fit->h = src_h / rw;
+        fit->x = 0;
+        fit->y = (dst_h - fit->h) / 2;
+     }
+   else if (rw < rh)
+     {
+        fit->w = src_w / rh;
+        fit->h = dst_h;
+        fit->x = (dst_w - fit->w) / 2;
+        fit->y = 0;
+     }
+   else
+     {
+        fit->w = dst_w;
+        fit->h = dst_h;
+        fit->x = 0;
+        fit->y = 0;
+     }
+
+   if (fit->x % 2)
+     fit->x = fit->x - 1;
+}
+
+void
+_e_tz_screenmirror_rect_scale (int src_w, int src_h, int dst_w, int dst_h, Eina_Rectangle *scale)
+{
+   float ratio;
+   Eina_Rectangle center;
+
+   if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0 || !scale)
+     return;
+
+   _e_tz_screenmirror_center_rect(src_w, src_h, dst_w, dst_h, &center);
+
+   ratio = (float)center.w / src_w;
+
+   scale->x = scale->x * ratio + center.x;
+   scale->y = scale->y * ratio + center.y;
+   scale->w = scale->w * ratio;
+   scale->h = scale->h * ratio;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_cb_timeout(void *data)
+{
+   E_Mirror *mirror = data;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(mirror, ECORE_CALLBACK_RENEW);
+
+   _e_tz_screenmirror_vblank_handler((void*)mirror);
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_watch_vblank(E_Mirror *mirror)
+{
+   if (mirror != keep_stream_mirror)
+     return EINA_FALSE;
+
+   /* If not DPMS_ON, we call vblank handler directly to dump screen */
+   if (e_dpms_get(mirror->drm_output))
+     {
+        if (!mirror->timer)
+          mirror->timer = ecore_timer_add((double)1/DUMP_FPS,
+                                          _e_tz_screenmirror_cb_timeout, mirror);
+        EINA_SAFETY_ON_NULL_RETURN_VAL(mirror->timer, EINA_FALSE);
+
+        return EINA_TRUE;
+     }
+   else if (mirror->timer)
+     {
+        ecore_timer_del(mirror->timer);
+        mirror->timer = NULL;
+     }
+
+   if (mirror->wait_vblank)
+     return EINA_TRUE;
+
+   if (!ecore_drm_output_wait_vblank(mirror->drm_output, mirror->per_vblank,
+                                     _e_tz_screenmirror_vblank_handler, mirror))
+     {
+        ERR("failed: ecore_drm_output_wait_vblank");
+        return EINA_FALSE;
+     }
+
+   mirror->wait_vblank = EINA_TRUE;
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_buffer_check(struct wl_resource *resource)
+{
+   if (wl_shm_buffer_get(resource) ||
+       wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, resource))
+     return EINA_TRUE;
+
+   ERR("unrecognized buffer");
+
+   return EINA_FALSE;
+}
+
+static void
+_e_tz_screenmirror_ui_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data)
+{
+   E_Mirror *mirror = (E_Mirror*)data;
+
+   EINA_SAFETY_ON_NULL_RETURN(mirror);
+   mirror->ui_buffer_list = eina_list_remove(mirror->ui_buffer_list, vbuf);
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_tz_screenmirror_ui_buffer_get(E_Mirror *mirror)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+   tbm_surface_h buffer;
+   Eina_List *l;
+   tdm_error err = TDM_ERROR_NONE;
+
+   buffer = tdm_layer_get_displaying_buffer(mirror->tdm_primary_layer, &err);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, NULL);
+
+   EINA_LIST_FOREACH(mirror->ui_buffer_list, l, vbuf)
+      if (vbuf->tbm_surface == buffer)
+        return vbuf;
+
+   vbuf = e_comp_wl_video_buffer_create_tbm(buffer);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+   e_comp_wl_video_buffer_free_func_add(vbuf, _e_tz_screenmirror_ui_buffer_cb_free, mirror);
+   mirror->ui_buffer_list = eina_list_append(mirror->ui_buffer_list, vbuf);
+
+   return vbuf;
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_tz_screenmirror_buffer_by_tbm_surface_get(E_Mirror *mirror, tbm_surface_h buffer)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+   Eina_List *l;
+
+   EINA_LIST_FOREACH(mirror->ui_buffer_list, l, vbuf)
+      if (vbuf->tbm_surface == buffer)
+        return vbuf;
+
+   vbuf = e_comp_wl_video_buffer_create_tbm(buffer);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+   e_comp_wl_video_buffer_free_func_add(vbuf, _e_tz_screenmirror_ui_buffer_cb_free, mirror);
+   mirror->ui_buffer_list = eina_list_append(mirror->ui_buffer_list, vbuf);
+
+   return vbuf;
+}
+
+static void
+_e_tz_screenmirror_pp_destroy(E_Mirror *mirror)
+{
+   if (!mirror->pp)
+     return;
+
+   tdm_pp_destroy(mirror->pp);
+   mirror->pp = NULL;
+}
+
+static void
+_e_tz_screenmirror_drm_buffer_clear_check(E_Mirror_Buffer *buffer)
+{
+   E_Mirror *mirror = buffer->mirror;
+   E_Comp_Wl_Video_Buf *vbuf, *dst;
+   Eina_List *l;
+   uint32_t buf_id;
+
+   dst = buffer->vbuf;
+   buf_id = wl_resource_get_id(dst->resource);
+
+   EINA_LIST_FOREACH(mirror->buffer_clear_check, l, vbuf)
+     {
+        uint32_t id;
+
+        id = wl_resource_get_id(vbuf->resource);
+        if (id == buf_id)
+          return;
+     }
+
+   e_comp_wl_video_buffer_clear(dst);
+   mirror->buffer_clear_check = eina_list_append(mirror->buffer_clear_check, dst);
+}
+
+static Eina_Bool
+_e_tz_screenmirror_tmp_buffer_create(E_Mirror_Buffer *buffer)
+{
+   tbm_surface_h tbm_surface = NULL;
+   E_Comp_Wl_Video_Buf *vbuf = NULL;
+
+   tbm_surface = tbm_surface_create(buffer->vbuf->width, buffer->vbuf->height, buffer->vbuf->tbmfmt);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surface, EINA_FALSE);
+
+   vbuf = e_comp_wl_video_buffer_create_tbm(tbm_surface);
+   if (vbuf == NULL)
+     {
+        tbm_surface_destroy(tbm_surface);
+        return EINA_FALSE;
+     }
+
+   e_comp_wl_video_buffer_clear(vbuf);
+   buffer->tmp = vbuf;
+
+   return EINA_TRUE;
+}
+
+static void
+_e_tz_screenmirror_copy_tmp_buffer(E_Mirror_Buffer *buffer)
+{
+   tbm_surface_h tbm_surface = NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN(buffer->tmp);
+   EINA_SAFETY_ON_NULL_RETURN(buffer->vbuf);
+
+   e_comp_wl_video_buffer_copy(buffer->tmp, buffer->vbuf);
+
+   tbm_surface = buffer->tmp->tbm_surface;
+
+   e_comp_wl_video_buffer_unref(buffer->tmp);
+   buffer->tmp = NULL;
+
+   tbm_surface_destroy(tbm_surface);
+}
+
+static void
+_e_tz_screenmirror_showing_rect_get(Eina_Rectangle *out_rect, Eina_Rectangle *dst_rect, Eina_Rectangle *showing_rect)
+{
+   showing_rect->x = dst_rect->x;
+   showing_rect->y = dst_rect->y;
+
+   if (dst_rect->x >= out_rect->w)
+     showing_rect->w = 0;
+   else if (dst_rect->x + dst_rect->w > out_rect->w)
+     showing_rect->w = out_rect->w - dst_rect->x;
+   else
+     showing_rect->w = dst_rect->w;
+
+   if (dst_rect->y >= out_rect->h)
+     showing_rect->h = 0;
+   else if (dst_rect->y + dst_rect->h > out_rect->h)
+     showing_rect->h = out_rect->h - dst_rect->y;
+   else
+     showing_rect->h = dst_rect->h;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_src_crop_get(E_Mirror *mirror, tdm_layer *layer, Eina_Rectangle *fit, Eina_Rectangle *showing_rect)
+{
+   tdm_info_layer info;
+   tdm_error err = TDM_ERROR_NONE;
+   const tdm_output_mode *mode = NULL;
+   float ratio_x, ratio_y;
+   Eina_Rectangle out_rect;
+   Eina_Rectangle dst_rect;
+
+   fit->x = 0;
+   fit->y = 0;
+   fit->w = 0;
+   fit->h = 0;
+
+   tdm_output_get_mode(mirror->tdm_output, &mode);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+   out_rect.x = 0;
+   out_rect.y = 0;
+   out_rect.w = mode->hdisplay;
+   out_rect.h = mode->vdisplay;
+
+   err = tdm_layer_get_info(layer, &info);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+   dst_rect.x = info.dst_pos.x;
+   dst_rect.y = info.dst_pos.y;
+   dst_rect.w = info.dst_pos.w;
+   dst_rect.h = info.dst_pos.h;
+
+   _e_tz_screenmirror_showing_rect_get(&out_rect, &dst_rect, showing_rect);
+
+   fit->x = info.src_config.pos.x;
+   fit->y = info.src_config.pos.y;
+
+   if (info.transform % 2 == 0)
+     {
+        ratio_x = (float)info.src_config.pos.w / dst_rect.w;
+        ratio_y = (float)info.src_config.pos.h / dst_rect.h;
+
+        fit->w = showing_rect->w * ratio_x;
+        fit->h = showing_rect->h * ratio_y;
+     }
+   else
+     {
+        ratio_x = (float)info.src_config.pos.w / dst_rect.h;
+        ratio_y = (float)info.src_config.pos.h / dst_rect.w;
+
+        fit->w = showing_rect->h * ratio_x;
+        fit->h = showing_rect->w * ratio_y;
+     }
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_position_get(E_Mirror *mirror, E_Comp_Wl_Video_Buf *vbuf, Eina_Rectangle *fit)
+{
+   E_Comp_Wl_Video_Buf *ui = NULL;
+
+   ui = _e_tz_screenmirror_ui_buffer_get(mirror);
+   if (ui == NULL)
+     {
+        ERR("_e_tz_screenmirror_position_get: get ui buf failed");
+        return EINA_FALSE;
+     }
+
+   if (screenshot_auto_rotation &&
+       ((mirror->angle + mirror->eout_rotate) % 360 == 90 || (mirror->angle + mirror->eout_rotate) % 360 == 270))
+     _e_tz_screenmirror_center_rect(ui->height, ui->width, vbuf->width, vbuf->height, fit);
+   else
+     _e_tz_screenmirror_center_rect(ui->width, ui->height, vbuf->width, vbuf->height, fit);
+
+   return EINA_TRUE;
+}
+
+static void
+_e_tz_screenmirror_dump_still_get_cropinfo(E_Comp_Wl_Video_Buf *tmp, E_Comp_Wl_Video_Buf *dst, tdm_layer *layer,
+                                           int w, int h, Eina_Rectangle *pos, Eina_Rectangle *showing_pos,
+                                           Eina_Rectangle *dst_crop, int rotate)
+{
+   tdm_info_layer info;
+   tdm_error err = TDM_ERROR_NONE;
+
+   dst_crop->x = 0;
+   dst_crop->y = 0;
+   dst_crop->w = 0;
+   dst_crop->h = 0;
+
+   err = tdm_layer_get_info(layer, &info);
+   EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+
+   if (info.src_config.pos.w == w && info.src_config.pos.h == h &&
+       pos->x == 0 && pos->y == 0 && pos->w == tmp->width && pos->h == tmp->height)
+     {
+        dst_crop->x = pos->x;
+        dst_crop->y = pos->y;
+        dst_crop->w = pos->w;
+        dst_crop->h = pos->h;
+     }
+   else if ((w == pos->w) && (h == pos->h) && (showing_pos->w == pos->w) && (showing_pos->h == pos->h))
+     {
+        dst_crop->x = info.dst_pos.x + pos->x;
+        dst_crop->y = info.dst_pos.y + pos->y;
+        dst_crop->w = info.dst_pos.w;
+        dst_crop->h = info.dst_pos.h;
+     }
+   else if (rotate == 0)
+     {
+        dst_crop->x = showing_pos->x * pos->w / w + pos->x;
+        dst_crop->y = showing_pos->y * pos->h / h + pos->y;
+        dst_crop->w = showing_pos->w * pos->w / w;
+        dst_crop->h = showing_pos->h * pos->h / h;
+     }
+   else if (rotate == 90)
+     {
+        dst_crop->x = (h - showing_pos->y - showing_pos->h) * pos->w / h + pos->x;
+        dst_crop->y = showing_pos->x * pos->h / w + pos->y;
+        dst_crop->w = showing_pos->h * pos->w / h;
+        dst_crop->h = showing_pos->w * pos->h / w;
+     }
+   else if (rotate == 180)
+     {
+        dst_crop->x = (w - showing_pos->x - showing_pos->w) * pos->w / w + pos->x;
+        dst_crop->y = (h - showing_pos->y - showing_pos->h) * pos->h / h + pos->y;
+        dst_crop->w = showing_pos->w * pos->w / w;
+        dst_crop->h = showing_pos->h * pos->h / h;
+     }
+   else if (rotate == 270)
+     {
+        dst_crop->x = showing_pos->y * pos->w / h + pos->x;
+        dst_crop->y = (w - showing_pos->x - showing_pos->w) * pos->h / w + pos->y;
+        dst_crop->w = showing_pos->h * pos->w / h;
+        dst_crop->h = showing_pos->w * pos->h / w;
+     }
+   else
+     {
+        dst_crop->x = pos->x;
+        dst_crop->y = pos->y;
+        dst_crop->w = pos->w;
+        dst_crop->h = pos->h;
+        ERR("_e_tz_screenmirror_dump_still_get_cropinfo: unknown case error");
+     }
+}
+
+static void
+_e_tz_screenmirror_dump_still(E_Mirror_Buffer *buffer)
+{
+   E_Mirror *mirror = buffer->mirror;
+   E_Comp_Wl_Video_Buf *ui, *dst;
+   tdm_error err = TDM_ERROR_NONE;
+   int count;
+   int i;
+   int rotate = 0;
+
+   if (buffer->vbuf->type == TYPE_SHM)
+     {
+        if (!_e_tz_screenmirror_tmp_buffer_create(buffer))
+          {
+             ERR("_e_tz_screenmirror_dump_still: tmp buffer create fail");
+             return;
+          }
+
+        dst = buffer->tmp;
+     }
+   else
+     dst = buffer->vbuf;
+   EINA_SAFETY_ON_NULL_RETURN(dst);
+
+   e_comp_wl_video_buffer_clear(dst);
+
+   ui = _e_tz_screenmirror_ui_buffer_get(mirror);
+   EINA_SAFETY_ON_NULL_RETURN(ui);
+
+   if (mirror->rotate_change)
+     {
+        int angle = 0;
+
+        angle = (mirror->angle + mirror->eout_rotate) % 360;
+        if (angle == 90)
+          rotate = 90;
+        else if (angle == 180)
+          rotate = 180;
+        else if (angle == 270)
+          rotate = 270;
+     }
+   else if (screenshot_auto_rotation && mirror->eout_rotate == 90)
+     rotate = 90;
+   else if (screenshot_auto_rotation && mirror->eout_rotate == 180)
+     rotate = 180;
+   else if (screenshot_auto_rotation && mirror->eout_rotate == 270)
+     rotate = 270;
+
+   err = tdm_output_get_layer_count(mirror->tdm_output, &count);
+   EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+   EINA_SAFETY_ON_FALSE_RETURN(count >= 0);
+
+   for (i = 0; i < count; i++)
+     {
+        tdm_layer *layer;
+        tdm_layer_capability capability;
+        tbm_surface_h surface = NULL;
+        E_Comp_Wl_Video_Buf *tmp = NULL;
+        Eina_Rectangle dst_pos = {0, };
+        Eina_Rectangle showing_pos = {0, };
+        Eina_Rectangle src_crop = {0, };
+        Eina_Rectangle dst_crop = {0, };
+
+        layer = tdm_output_get_layer(mirror->tdm_output, i, &err);
+        EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+
+        if (layer != mirror->tdm_primary_layer)
+          {
+             err = tdm_layer_get_capabilities(layer, &capability);
+             EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+             if (capability & TDM_LAYER_CAPABILITY_VIDEO)
+               continue;
+
+             surface = tdm_layer_get_displaying_buffer(layer, &err);
+             if (surface == NULL)
+               continue;
+
+             tmp = _e_tz_screenmirror_buffer_by_tbm_surface_get(mirror, surface);
+             if (tmp == NULL)
+               continue;
+
+             _e_tz_screenmirror_src_crop_get(mirror, layer, &src_crop, &showing_pos);
+          }
+        else
+          {
+             tmp = ui;
+             src_crop.x = showing_pos.x = 0;
+             src_crop.y = showing_pos.y = 0;
+             src_crop.w = showing_pos.w = tmp->width;
+             src_crop.h = showing_pos.h = tmp->height;
+          }
+
+        _e_tz_screenmirror_position_get(mirror, buffer->vbuf, &dst_pos);
+
+        _e_tz_screenmirror_dump_still_get_cropinfo(tmp, dst, layer, ui->width, ui->height,
+                                                   &dst_pos, &showing_pos, &dst_crop, rotate);
+        e_comp_wl_video_buffer_convert(tmp, dst,
+                                       src_crop.x, src_crop.y, src_crop.w, src_crop.h,
+                                       dst_crop.x, dst_crop.y, dst_crop.w, dst_crop.h,
+                                       EINA_TRUE, rotate, 0, 0);
+
+     }
+
+   if (buffer->vbuf->type == TYPE_SHM)
+     _e_tz_screenmirror_copy_tmp_buffer(buffer);
+}
+
+static void
+_e_tz_screenmirror_buffer_free(E_Mirror_Buffer *buffer)
+{
+   E_Mirror *mirror = buffer->mirror;
+
+   if (buffer->tmp)
+     _e_tz_screenmirror_copy_tmp_buffer(buffer);
+
+   /* then, dequeue and send dequeue event */
+   _e_tz_screenmirror_buffer_dequeue(buffer);
+
+   if (buffer->destroy_listener.notify)
+     {
+        wl_list_remove(&buffer->destroy_listener.link);
+        buffer->destroy_listener.notify = NULL;
+     }
+
+   if (buffer->vbuf)
+     {
+        e_comp_wl_video_buffer_free_func_del(buffer->vbuf, _e_tz_screenmirror_buffer_cb_free, buffer);
+        e_comp_wl_video_buffer_unref(buffer->vbuf);
+
+        mirror->buffer_clear_check = eina_list_remove(mirror->buffer_clear_check, buffer->vbuf);
+     }
+
+   E_FREE(buffer);
+}
+
+static void
+_e_tz_screenmirror_capture_oneshot_done_handler(tdm_capture *capture, tbm_surface_h buffer, void *user_data)
+{
+   E_Mirror_Buffer *mirror_buffer = user_data;
+   E_Mirror *mirror = mirror_buffer->mirror;
+
+   if (mirror_buffer->vbuf->type == TYPE_SHM)
+     _e_tz_screenmirror_copy_tmp_buffer(mirror_buffer);
+
+   _e_tz_screenmirror_destroy(mirror);
+
+   DBG("_e_tz_screenmirror_capture_oneshot_done");
+}
+
+static Eina_Bool
+_e_tz_screenmirror_capture_stream_done(void *data)
+{
+   E_Mirror *mirror = data;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(mirror, ECORE_CALLBACK_CANCEL);
+
+   if (mirror != keep_stream_mirror)
+     return ECORE_CALLBACK_CANCEL;
+
+   if (mirror->capture)
+     {
+        tdm_capture_destroy(mirror->capture);
+        mirror->capture = NULL;
+     }
+
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static E_Mirror_Buffer *
+_e_tz_screenmirror_mirrorbuf_find(E_Mirror *mirror, tbm_surface_h surf)
+{
+   Eina_List *l;
+   E_Mirror_Buffer *buffer = NULL;
+
+   if (!mirror->buffer_queue)
+     return NULL;
+
+   EINA_LIST_FOREACH(mirror->buffer_queue, l, buffer)
+     {
+        if (!buffer || !buffer->vbuf)
+          continue;
+
+        if (buffer->vbuf->type == TYPE_SHM)
+          {
+             if (!buffer->tmp || !buffer->tmp->tbm_surface)
+               continue;
+
+             if (buffer->tmp->tbm_surface == surf)
+               {
+                  _e_tz_screenmirror_copy_tmp_buffer(buffer);
+                  return buffer;
+               }
+          }
+        else
+          {
+             if (!buffer->vbuf->tbm_surface)
+               continue;
+
+             if (buffer->vbuf->tbm_surface == surf)
+               return buffer;
+          }
+     }
+
+   return NULL;
+}
+
+static void
+_e_tz_screenmirror_capture_stream_done_handler(tdm_capture *capture, tbm_surface_h surf, void *user_data)
+{
+   E_Mirror *mirror = user_data;
+   E_Mirror_Buffer *buffer = NULL;
+
+   if (mirror != keep_stream_mirror)
+     return;
+
+   buffer = _e_tz_screenmirror_mirrorbuf_find(mirror, surf);
+   if (buffer == NULL)
+     {
+        ERR("_e_tz_screenmirror_capture_stream_done_handler: find mirror buffer failed");
+        return;
+     }
+
+   _e_tz_screenmirror_buffer_dequeue(buffer);
+
+   if (mirror->started == EINA_FALSE)
+     {
+        if (eina_list_count(mirror->buffer_queue) == 0)
+          mirror->capture_timer = ecore_timer_add((double)1/DUMP_FPS, _e_tz_screenmirror_capture_stream_done, mirror);
+     }
+}
+
+static E_Client *
+_e_tz_screenmirror_top_visible_ec_get()
+{
+   E_Client *ec;
+   Evas_Object *o;
+   E_Comp_Wl_Client_Data *cdata;
+
+   for (o = evas_object_top_get(e_comp->evas); o; o = evas_object_below_get(o))
+     {
+        ec = evas_object_data_get(o, "E_Client");
+
+        /* check e_client and skip e_clients not intersects with zone */
+        if (!ec) continue;
+        if (e_object_is_del(E_OBJECT(ec))) continue;
+        if (e_client_util_ignored_get(ec)) continue;
+        if (ec->iconic) continue;
+        if (ec->visible == 0) continue;
+        if (!(ec->visibility.obscured == 0 || ec->visibility.obscured == 1)) continue;
+        if (!ec->frame) continue;
+        if (!evas_object_visible_get(ec->frame)) continue;
+        /* if ec is subsurface, skip this */
+        cdata = (E_Comp_Wl_Client_Data *)ec->comp_data;
+        if (cdata && cdata->sub.data) continue;
+
+        return ec;
+     }
+
+   return NULL;
+}
+
+static void
+_e_tz_screenmirror_get_angle(E_Mirror *mirror)
+{
+   E_Client *ec = NULL;
+   E_Output *eout = NULL;
+
+   mirror->eout_rotate = 0;
+   mirror->angle = 0;
+
+   ec = _e_tz_screenmirror_top_visible_ec_get();
+   if (ec)
+     {
+        mirror->angle = ec->e.state.rot.ang.curr;
+
+        eout = e_output_find(ec->zone->output_id);
+        if (eout)
+          mirror->eout_rotate = eout->config.rotation;
+     }
+}
+
+static Eina_Bool
+_e_tz_screenmirror_tdm_capture_handle_set(E_Mirror_Buffer *buffer, tdm_capture *capture, tdm_capture_capability cap)
+{
+   E_Mirror *mirror = buffer->mirror;
+   tdm_error err = TDM_ERROR_NONE;
+
+   if (cap == TDM_CAPTURE_CAPABILITY_ONESHOT)
+     err = tdm_capture_set_done_handler(capture, _e_tz_screenmirror_capture_oneshot_done_handler, buffer);
+   else
+     err = tdm_capture_set_done_handler(capture, _e_tz_screenmirror_capture_stream_done_handler, mirror);
+   if (err != TDM_ERROR_NONE)
+     {
+        ERR("_e_tz_screenmirror_tdm_capture_support: tdm_capture set_handler failed");
+        return EINA_FALSE;
+     }
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_tdm_capture_support(E_Mirror_Buffer *buffer, tdm_capture_capability cap)
+{
+   E_Mirror *mirror = buffer->mirror;
+   tdm_error err = TDM_ERROR_NONE;
+   tdm_capture *capture = NULL;
+   tdm_info_capture capture_info;
+   tdm_capture_capability capabilities;
+   Eina_Rectangle dst_pos;
+
+   err = tdm_display_get_capture_capabilities(e_comp->e_comp_screen->tdisplay, &capabilities);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(err == TDM_ERROR_NO_CAPABILITY, EINA_FALSE);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+   if (capabilities & cap)
+     {
+        if (mirror->capture)
+          return EINA_TRUE;
+
+        capture = tdm_output_create_capture(mirror->tdm_output, &err);
+        if (err != TDM_ERROR_NONE)
+          {
+             ERR("_e_tz_screenmirror_tdm_capture_support: create tdm_capture failed");
+             return EINA_FALSE;
+          }
+
+        memset(&capture_info, 0, sizeof(tdm_info_capture));
+        capture_info.dst_config.size.h = buffer->vbuf->width_from_pitch;
+        capture_info.dst_config.size.v = buffer->vbuf->height;
+        capture_info.dst_config.pos.x = 0;
+        capture_info.dst_config.pos.y = 0;
+        capture_info.dst_config.pos.w = buffer->vbuf->width;
+        capture_info.dst_config.pos.h = buffer->vbuf->height;
+        capture_info.dst_config.format = buffer->vbuf->tbmfmt;
+        capture_info.transform = TDM_TRANSFORM_NORMAL;
+        if (cap == TDM_CAPTURE_CAPABILITY_ONESHOT)
+          capture_info.type = TDM_CAPTURE_TYPE_ONESHOT;
+        else
+          capture_info.type = TDM_CAPTURE_TYPE_STREAM;
+
+        if (_e_tz_screenmirror_position_get(mirror, buffer->vbuf, &dst_pos))
+          {
+             capture_info.dst_config.pos.x = dst_pos.x;
+             capture_info.dst_config.pos.y = dst_pos.y;
+             capture_info.dst_config.pos.w = dst_pos.w;
+             capture_info.dst_config.pos.h = dst_pos.h;
+
+             if (mirror->rotate_change)
+               {
+                  int tmp;
+
+                  tmp = (mirror->angle + mirror->eout_rotate) % 360;
+                  if (tmp == 90)
+                    capture_info.transform = TDM_TRANSFORM_90;
+                  else if (tmp == 180)
+                    capture_info.transform = TDM_TRANSFORM_180;
+                  else if (tmp == 270)
+                    capture_info.transform = TDM_TRANSFORM_270;
+               }
+             else if (screenshot_auto_rotation && mirror->eout_rotate == 90)
+               capture_info.transform = TDM_TRANSFORM_90;
+             else if (screenshot_auto_rotation && mirror->eout_rotate == 180)
+               capture_info.transform = TDM_TRANSFORM_180;
+             else if (screenshot_auto_rotation && mirror->eout_rotate == 270)
+               capture_info.transform = TDM_TRANSFORM_270;
+          }
+
+        err = tdm_capture_set_info(capture, &capture_info);
+        if (err != TDM_ERROR_NONE)
+          {
+             ERR("_e_tz_screenmirror_tdm_capture_support: tdm_capture set_info failed");
+             tdm_capture_destroy(capture);
+             return EINA_FALSE;
+          }
+
+        if (_e_tz_screenmirror_tdm_capture_handle_set(buffer, capture, cap) == EINA_FALSE)
+          {
+             tdm_capture_destroy(capture);
+             return EINA_FALSE;
+          }
+        mirror->capture = capture;
+
+        return EINA_TRUE;
+     }
+
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_capture_cb_timeout(void *data)
+{
+   E_Mirror *mirror = data;
+   E_Mirror_Buffer *buffer;
+   Eina_List *l;
+
+   EINA_SAFETY_ON_NULL_GOTO(mirror, done);
+
+   EINA_LIST_FOREACH(mirror->buffer_queue, l, buffer)
+     {
+        if (!buffer->in_use) break;
+     }
+
+   /* can be null when client doesn't queue a buffer previously */
+   if (!buffer)
+     goto done;
+
+   buffer->in_use = EINA_TRUE;
+
+   _e_tz_screenmirror_buffer_dequeue(buffer);
+
+done:
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_tdm_capture_attach(E_Mirror *mirror, E_Mirror_Buffer *buffer)
+{
+   tdm_error err = TDM_ERROR_NONE;
+
+   if (buffer->vbuf->type == TYPE_SHM)
+     {
+        if (!_e_tz_screenmirror_tmp_buffer_create(buffer))
+          {
+             ERR("_e_tz_screenmirror_tdm_capture_attach: tmp buffer create fail");
+             return EINA_FALSE;
+          }
+
+        err = tdm_capture_attach(mirror->capture, buffer->tmp->tbm_surface);
+        if (err != TDM_ERROR_NONE)
+          {
+             e_comp_wl_video_buffer_unref(buffer->tmp);
+             buffer->tmp = NULL;
+             return EINA_FALSE;
+          }
+     }
+   else
+     {
+        err = tdm_capture_attach(mirror->capture, buffer->vbuf->tbm_surface);
+        EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+     }
+
+   return EINA_TRUE;
+}
+
+static void
+_e_tz_screenmirror_buffer_queue(E_Mirror_Buffer *buffer)
+{
+   E_Mirror *mirror = buffer->mirror;
+   tdm_error err = TDM_ERROR_NONE;
+   E_Mirror_Buffer *buffer_list;
+   Eina_List *l;
+
+   mirror->buffer_queue = eina_list_append(mirror->buffer_queue, buffer);
+
+   if (mirror->started)
+     {
+        if (_e_tz_screenmirror_tdm_capture_support(buffer, TDM_CAPTURE_CAPABILITY_STREAM))
+          {
+             _e_tz_screenmirror_drm_buffer_clear_check(buffer);
+
+             if (e_dpms_get(mirror->drm_output))
+               {
+                  if (!mirror->timer)
+                    {
+                       eina_list_free(mirror->buffer_clear_check);
+                       mirror->buffer_clear_check = NULL;
+                       _e_tz_screenmirror_drm_buffer_clear_check(buffer);
+                       mirror->timer = ecore_timer_add((double)1/DUMP_FPS, _e_tz_screenmirror_capture_cb_timeout, mirror);
+                    }
+                  EINA_SAFETY_ON_NULL_RETURN(mirror->timer);
+                  return;
+               }
+             else if (mirror->timer)
+               {
+                  ecore_timer_del(mirror->timer);
+                  mirror->timer = NULL;
+                  EINA_LIST_FOREACH(mirror->buffer_queue, l, buffer_list)
+                    {
+                       if (!buffer_list->in_use)
+                         {
+                            buffer_list->in_use = EINA_TRUE;
+
+                            if (!_e_tz_screenmirror_tdm_capture_attach(mirror, buffer_list))
+                              {
+                                 ERR("_e_tz_screenmirror_buffer_queue: attach fail");
+                                 return;
+                              }
+                         }
+                    }
+                  err = tdm_capture_commit(mirror->capture);
+                  EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+               }
+             else
+               {
+                  buffer->in_use = EINA_TRUE;
+
+                  if (!_e_tz_screenmirror_tdm_capture_attach(mirror, buffer))
+                    {
+                       ERR("_e_tz_screenmirror_buffer_queue: attach fail");
+                       return;
+                    }
+                  err = tdm_capture_commit(mirror->capture);
+                  EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+               }
+          }
+        else
+          {
+             if (buffer->vbuf->type == TYPE_SHM)
+               {
+                  if (!_e_tz_screenmirror_tmp_buffer_create(buffer))
+                    {
+                       ERR("_e_tz_screenmirror_buffer_queue: tmp buffer create fail");
+                       return;
+                    }
+               }
+             _e_tz_screenmirror_watch_vblank(mirror);
+          }
+     }
+   else
+     {
+        if (_e_tz_screenmirror_tdm_capture_support(buffer, TDM_CAPTURE_CAPABILITY_STREAM))
+          {
+             _e_tz_screenmirror_drm_buffer_clear_check(buffer);
+
+             if (e_dpms_get(mirror->drm_output))
+               return;
+
+             if (!_e_tz_screenmirror_tdm_capture_attach(mirror, buffer))
+               {
+                  ERR("_e_tz_screenmirror_buffer_queue: attach fail");
+                  return;
+               }
+          }
+        else
+          {
+             if (buffer->vbuf->type == TYPE_SHM)
+               {
+                  if (!_e_tz_screenmirror_tmp_buffer_create(buffer))
+                    {
+                       ERR("_e_tz_screenmirror_buffer_queue: tmp buffer create fail");
+                       return;
+                    }
+               }
+          }
+     }
+}
+
+static void
+_e_tz_screenmirror_buffer_dequeue(E_Mirror_Buffer *buffer)
+{
+   E_Mirror *mirror = buffer->mirror;
+
+   EINA_SAFETY_ON_NULL_RETURN(mirror);
+   if (!mirror->buffer_queue || !eina_list_data_find_list(mirror->buffer_queue, buffer))
+     return;
+
+   buffer->in_use = EINA_FALSE;
+   mirror->buffer_queue = eina_list_remove(mirror->buffer_queue, buffer);
+
+   /* resource == shooter means that we're using weston screenshooter
+    * In case of wetson screenshooter, send a done event. Otherwise, send
+    * a dequeued event for tizen_screenmirror.
+    */
+   if (mirror->resource == mirror->shooter)
+     {
+        if (!mirror->oneshot_client_destroy)
+          screenshooter_send_done(mirror->resource);
+     }
+   else
+     tizen_screenmirror_send_dequeued(mirror->resource, buffer->vbuf->resource);
+}
+
+static Eina_Bool
+_e_tz_screenmirror_tdm_capture_oneshot(E_Mirror *mirror, E_Mirror_Buffer *buffer)
+{
+   tdm_error err = TDM_ERROR_NONE;
+
+   if (!_e_tz_screenmirror_tdm_capture_attach(mirror, buffer))
+     {
+        ERR("_e_tz_screenmirror_tdm_capture_oneshot: attach fail");
+        return EINA_FALSE;
+     }
+   err = tdm_capture_commit(mirror->capture);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+   return EINA_TRUE;
+}
+
+static void
+_e_tz_screenmirror_buffer_cb_destroy(struct wl_listener *listener, void *data)
+{
+   E_Mirror_Buffer *buffer = container_of(listener, E_Mirror_Buffer, destroy_listener);
+
+   if (buffer->destroy_listener.notify)
+     {
+        wl_list_remove(&buffer->destroy_listener.link);
+        buffer->destroy_listener.notify = NULL;
+     }
+}
+
+static void
+_e_tz_screenmirror_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data)
+{
+   E_Mirror_Buffer *buffer = data;
+   E_Mirror *mirror = buffer->mirror;
+
+   if (mirror->resource == mirror->shooter)
+     mirror->oneshot_client_destroy = EINA_TRUE;
+
+   _e_tz_screenmirror_buffer_free(buffer);
+}
+
+static E_Mirror_Buffer*
+_e_tz_screenmirror_buffer_get(E_Mirror *mirror, struct wl_resource *resource)
+{
+   E_Mirror_Buffer *buffer = NULL;
+   struct wl_listener *listener;
+
+   listener = wl_resource_get_destroy_listener(resource, _e_tz_screenmirror_buffer_cb_destroy);
+   if (listener)
+     return container_of(listener, E_Mirror_Buffer, destroy_listener);
+
+   if (!(buffer = E_NEW(E_Mirror_Buffer, 1)))
+     return NULL;
+
+   /* FIXME: this is very tricky. DON'T add listner after e_comp_wl_video_buffer_create. */
+   buffer->destroy_listener.notify = _e_tz_screenmirror_buffer_cb_destroy;
+   wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
+
+   buffer->vbuf = e_comp_wl_video_buffer_create(resource);
+   EINA_SAFETY_ON_NULL_GOTO(buffer->vbuf, fail_get);
+
+   buffer->mirror = mirror;
+
+   DBG("capture buffer: %c%c%c%c %dx%d (%d,%d,%d) (%d,%d,%d)",
+       FOURCC_STR(buffer->vbuf->tbmfmt),
+       buffer->vbuf->width, buffer->vbuf->height,
+       buffer->vbuf->pitches[0], buffer->vbuf->pitches[1], buffer->vbuf->pitches[2],
+       buffer->vbuf->offsets[0], buffer->vbuf->offsets[1], buffer->vbuf->offsets[2]);
+
+   e_comp_wl_video_buffer_free_func_add(buffer->vbuf, _e_tz_screenmirror_buffer_cb_free, buffer);
+
+   return buffer;
+fail_get:
+   E_FREE(buffer);
+   return NULL;
+}
+
+static void
+_e_tz_screenmirror_vblank_handler(void *data)
+{
+   E_Mirror *mirror = data;
+   E_Mirror_Buffer *buffer;
+   Eina_List *l;
+
+   EINA_SAFETY_ON_NULL_RETURN(mirror);
+   if (mirror != keep_stream_mirror)
+     return;
+
+   mirror->wait_vblank = EINA_FALSE;
+
+   EINA_LIST_FOREACH(mirror->buffer_queue, l, buffer)
+     {
+        if (!buffer->in_use) break;
+     }
+
+   /* can be null when client doesn't queue a buffer previously */
+   if (!buffer)
+     return;
+
+   _e_tz_screenmirror_dump_still(buffer);
+   _e_tz_screenmirror_buffer_dequeue(buffer);
+
+   /* timer is a substitution for vblank during dpms off. so if timer is running,
+    * we don't watch vblank events recursively.
+    */
+   if (!mirror->timer)
+     _e_tz_screenmirror_watch_vblank(mirror);
+}
+
+static void
+_e_tz_screenmirror_cb_client_destroy(struct wl_listener *listener, void *data)
+{
+   E_Mirror *mirror = container_of(listener, E_Mirror, client_destroy_listener);
+
+   if (mirror->resource == mirror->shooter)
+     {
+        mirror->oneshot_client_destroy = EINA_TRUE;
+        return;
+     }
+   _e_tz_screenmirror_destroy(mirror);
+}
+
+static E_Mirror*
+_e_tz_screenmirror_create(struct wl_client *client, struct wl_resource *shooter_resource, struct wl_resource *output_resource)
+{
+   E_Mirror *mirror = NULL;
+   Ecore_Drm_Output *drm_output;
+   Ecore_Drm_Device *dev;
+   Eina_List *devs;
+   Eina_List *l, *ll;
+   tdm_error err = TDM_ERROR_NONE;
+   int count, i, ret;
+   unsigned int crtc_id;
+
+   mirror = E_NEW(E_Mirror, 1);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(mirror, NULL);
+
+   mirror->stretch = TIZEN_SCREENMIRROR_STRETCH_KEEP_RATIO;
+   mirror->shooter = shooter_resource;
+   mirror->output = output_resource;
+   mirror->wl_output = wl_resource_get_user_data(mirror->output);
+   EINA_SAFETY_ON_NULL_GOTO(mirror->wl_output, fail_create);
+
+   mirror->per_vblank = (mirror->wl_output->refresh / (DUMP_FPS * 1000));
+
+   devs = eina_list_clone(ecore_drm_devices_get());
+   EINA_LIST_FOREACH(devs, l, dev)
+      EINA_LIST_FOREACH(dev->outputs, ll, drm_output)
+        {
+           int x, y;
+           ecore_drm_output_position_get(drm_output, &x, &y);
+           if (x != mirror->wl_output->x || y != mirror->wl_output->y) continue;
+           mirror->drm_output = drm_output;
+           mirror->drm_device = dev;
+           break;
+        }
+   eina_list_free(devs);
+   EINA_SAFETY_ON_NULL_GOTO(mirror->drm_output, fail_create);
+
+   crtc_id = ecore_drm_output_crtc_id_get(mirror->drm_output);
+
+   mirror->tdm_output = tdm_display_get_output(e_comp->e_comp_screen->tdisplay, crtc_id, &err);
+   EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, fail_create);
+
+   err = tdm_output_get_layer_count(mirror->tdm_output, &count);
+   EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, fail_create);
+   EINA_SAFETY_ON_FALSE_GOTO(count >= 0, fail_create);
+
+   for (i = 0; i < count; i++)
+     {
+        tdm_layer *layer;
+        tdm_layer_capability capability;
+
+        layer = tdm_output_get_layer(mirror->tdm_output, i, &err);
+        EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, fail_create);
+
+        err = tdm_layer_get_capabilities(layer, &capability);
+        EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, fail_create);
+
+        if (capability & TDM_LAYER_CAPABILITY_PRIMARY)
+          {
+             mirror->tdm_primary_layer = layer;
+             break;
+          }
+     }
+   EINA_SAFETY_ON_NULL_GOTO(mirror->tdm_primary_layer, fail_create);
+
+   INF("per_vblank(%d)", mirror->per_vblank);
+
+#ifdef HAVE_CYNARA
+   ret = cynara_initialize(&mirror->p_cynara, NULL);
+   EINA_SAFETY_ON_FALSE_GOTO(ret == CYNARA_API_SUCCESS, fail_create);
+   mirror->cynara_initialized = EINA_TRUE;
+#endif
+
+   mirror_list = eina_list_append(mirror_list, mirror);
+
+   mirror->client_destroy_listener.notify = _e_tz_screenmirror_cb_client_destroy;
+   wl_client_add_destroy_listener(client, &mirror->client_destroy_listener);
+
+   mirror->oneshot_client_destroy = EINA_FALSE;
+
+   return mirror;
+fail_create:
+#ifdef HAVE_CYNARA
+   mirror->p_cynara = NULL;
+#endif
+
+   E_FREE(mirror);
+
+   return NULL;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_find_mirror(E_Mirror *mirror)
+{
+   if (!eina_list_data_find(mirror_list, mirror))
+     return EINA_FALSE;
+   else
+     return EINA_TRUE;
+}
+
+static void
+_e_tz_screenmirror_destroy(E_Mirror *mirror)
+{
+   E_Mirror_Buffer *buffer;
+   Eina_List *l, *ll;
+   E_Comp_Wl_Video_Buf *vbuf;
+
+   if (!mirror)
+     return;
+
+   if (!_e_tz_screenmirror_find_mirror(mirror))
+     return;
+   mirror_list = eina_list_remove(mirror_list, mirror);
+
+#ifdef HAVE_CYNARA
+   if (mirror->p_cynara) cynara_finish(mirror->p_cynara);
+   mirror->p_cynara = NULL;
+   mirror->cynara_initialized = EINA_FALSE;
+#endif
+
+   if (mirror->capture_timer)
+     ecore_timer_del(mirror->capture_timer);
+   mirror->capture_timer = NULL;
+
+   if (mirror->timer)
+     ecore_timer_del(mirror->timer);
+   mirror->timer = NULL;
+
+   if (mirror->client_destroy_listener.notify)
+     wl_list_remove(&mirror->client_destroy_listener.link);
+   mirror->client_destroy_listener.notify = NULL;
+
+   wl_resource_set_destructor(mirror->resource, NULL);
+
+   _e_tz_screenmirror_pp_destroy(mirror);
+
+   eina_list_free(mirror->buffer_clear_check);
+   mirror->buffer_clear_check = NULL;
+
+   EINA_LIST_FOREACH_SAFE(mirror->buffer_queue, l, ll, buffer)
+      _e_tz_screenmirror_buffer_free(buffer);
+   mirror->buffer_queue = NULL;
+
+   EINA_LIST_FOREACH_SAFE(mirror->ui_buffer_list, l, ll, vbuf)
+      e_comp_wl_video_buffer_unref(vbuf);
+   mirror->ui_buffer_list = NULL;
+
+   if (mirror->capture)
+     tdm_capture_destroy(mirror->capture);
+   mirror->capture = NULL;
+
+   if (keep_stream_mirror == mirror)
+     keep_stream_mirror = NULL;
+
+   free(mirror);
+#if 0
+   if (e_comp_wl_video_buffer_list_length() > 0)
+     e_comp_wl_video_buffer_list_print(NULL);
+#endif
+}
+
+static void
+destroy_tz_screenmirror(struct wl_resource *resource)
+{
+   E_Mirror *mirror = wl_resource_get_user_data(resource);
+
+   _e_tz_screenmirror_destroy(mirror);
+}
+
+static void
+_e_tz_screenmirror_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   wl_resource_destroy(resource);
+}
+
+static void
+_e_tz_screenmirror_cb_set_stretch(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t stretch)
+{
+   E_Mirror *mirror = wl_resource_get_user_data(resource);
+
+   EINA_SAFETY_ON_NULL_RETURN(mirror);
+
+   if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+                                       PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+     {
+        ERR("_e_tz_screenmirror_cb_set_stretch: priv check failed");
+        return;
+     }
+
+   if (mirror->stretch == stretch)
+     return;
+
+   mirror->stretch = stretch;
+}
+
+static void
+_e_tz_screenmirror_cb_queue(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *buffer_resource)
+{
+   E_Mirror *mirror = wl_resource_get_user_data(resource);
+   E_Mirror_Buffer *buffer;
+
+   EINA_SAFETY_ON_NULL_RETURN(mirror);
+
+   if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+                                       PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+     {
+        ERR("_e_tz_screenmirror_cb_queue: priv check failed");
+        return;
+     }
+
+   if (!_e_tz_screenmirror_buffer_check(buffer_resource))
+     {
+        wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+                               "tizen_screenshooter failed: wrong buffer resource");
+        return;
+     }
+
+   buffer = _e_tz_screenmirror_buffer_get(mirror, buffer_resource);
+   if (!buffer)
+     {
+        wl_resource_post_no_memory(resource);
+        return;
+     }
+
+   _e_tz_screenmirror_buffer_queue(buffer);
+}
+
+static void
+_e_tz_screenmirror_cb_dequeue(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *buffer_resource)
+{
+   E_Mirror *mirror = wl_resource_get_user_data(resource);
+   E_Mirror_Buffer *buffer;
+
+   EINA_SAFETY_ON_NULL_RETURN(mirror);
+
+   if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+                                       PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+     {
+        ERR("_e_tz_screenmirror_cb_dequeue: priv check failed");
+        return;
+     }
+
+   if (!_e_tz_screenmirror_buffer_check(buffer_resource))
+     {
+        wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+                               "tizen_screenshooter failed: wrong buffer resource");
+        return;
+     }
+
+   buffer = _e_tz_screenmirror_buffer_get(mirror, buffer_resource);
+   if (!buffer || !eina_list_data_find_list(mirror->buffer_queue, buffer))
+     return;
+
+   _e_tz_screenmirror_buffer_dequeue(buffer);
+}
+
+static void
+_e_tz_screenmirror_cb_start(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   E_Mirror *mirror = wl_resource_get_user_data(resource);
+   tdm_error err = TDM_ERROR_NONE;
+
+   EINA_SAFETY_ON_NULL_RETURN(mirror);
+
+   if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+                                       PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+     {
+        ERR("_e_tz_screenmirror_cb_start: priv check failed");
+        return;
+     }
+
+   if (mirror->started) return;
+
+   mirror->started = EINA_TRUE;
+
+   if (!mirror->buffer_queue)
+     return;
+
+   if (mirror->capture)
+     {
+        if (e_dpms_get(mirror->drm_output))
+          {
+             if (!mirror->timer)
+               mirror->timer = ecore_timer_add((double)1/DUMP_FPS, _e_tz_screenmirror_capture_cb_timeout, mirror);
+             EINA_SAFETY_ON_NULL_RETURN(mirror->timer);
+             return;
+          }
+
+        err = tdm_capture_commit(mirror->capture);
+        EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+     }
+   else
+     _e_tz_screenmirror_watch_vblank(mirror);
+}
+
+static void
+_e_tz_screenmirror_cb_stop(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   E_Mirror *mirror = wl_resource_get_user_data(resource);
+
+   EINA_SAFETY_ON_NULL_RETURN(mirror);
+
+   if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+                                       PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+     {
+        ERR("_e_tz_screenmirror_cb_stop: priv check failed");
+        return;
+     }
+
+   if (!mirror->started) return;
+
+   mirror->started = EINA_FALSE;
+
+   _e_tz_screenmirror_pp_destroy(mirror);
+   tizen_screenmirror_send_stop(resource);
+}
+
+static const struct tizen_screenmirror_interface _e_tz_screenmirror_interface = {
+     _e_tz_screenmirror_cb_destroy,
+     _e_tz_screenmirror_cb_set_stretch,
+     _e_tz_screenmirror_cb_queue,
+     _e_tz_screenmirror_cb_dequeue,
+     _e_tz_screenmirror_cb_start,
+     _e_tz_screenmirror_cb_stop
+};
+
+static void
+_e_tz_screenshooter_get_screenmirror(struct wl_client *client,
+                                     struct wl_resource *resource,
+                                     uint32_t id,
+                                     struct wl_resource *output)
+{
+   int version = wl_resource_get_version(resource);
+   E_Mirror *mirror;
+
+   if (keep_stream_mirror != NULL)
+     {
+        wl_resource_post_no_memory(resource);
+        return;
+     }
+
+   mirror = _e_tz_screenmirror_create(client, resource, output);
+   if (!mirror)
+     {
+        wl_resource_post_no_memory(resource);
+        return;
+     }
+   keep_stream_mirror = mirror;
+
+   mirror->resource = wl_resource_create(client, &tizen_screenmirror_interface, version, id);
+   if (mirror->resource == NULL)
+     {
+        _e_tz_screenmirror_destroy(mirror);
+        wl_client_post_no_memory(client);
+        keep_stream_mirror = NULL;
+        return;
+     }
+
+   wl_resource_set_implementation(mirror->resource, &_e_tz_screenmirror_interface,
+                                  mirror, destroy_tz_screenmirror);
+
+   if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+                                       PRIVILEGE_SCREENSHOT) == EINA_TRUE)
+     DBG("_e_tz_screenshooter_get_screenmirror: priv check success");
+   else
+     DBG("_e_tz_screenshooter_get_screenmirror: priv check failed");
+
+   tizen_screenmirror_send_content(mirror->resource, TIZEN_SCREENMIRROR_CONTENT_NORMAL);
+}
+
+static void
+_e_tz_screenshooter_set_oneshot_auto_rotation(struct wl_client *client,
+                                              struct wl_resource *resource,
+                                              uint32_t set)
+{
+   DBG("_e_tz_screenshooter_set_oneshot_auto_rotation: %d", set);
+
+   if (set)
+     screenshot_auto_rotation = EINA_TRUE;
+   else
+     screenshot_auto_rotation = EINA_FALSE;
+}
+
+static const struct tizen_screenshooter_interface _e_tz_screenshooter_interface =
+{
+   _e_tz_screenshooter_get_screenmirror,
+   _e_tz_screenshooter_set_oneshot_auto_rotation
+};
+
+static void
+_e_tz_screenshooter_cb_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+   struct wl_resource *res;
+   int i;
+
+   if (!(res = wl_resource_create(client, &tizen_screenshooter_interface, MIN(version, 1), id)))
+     {
+        ERR("Could not create tizen_screenshooter resource: %m");
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   if (_e_screenmirror_privilege_check_with_cynara_init(client, wl_client_get_fd(client),
+                                                        PRIVILEGE_SCREENSHOT) == EINA_TRUE)
+     tizen_screenshooter_send_screenshooter_notify(res, EINA_TRUE);
+   else
+     tizen_screenshooter_send_screenshooter_notify(res, EINA_FALSE);
+
+   wl_resource_set_implementation(res, &_e_tz_screenshooter_interface, NULL, NULL);
+
+   for (i = 0; i < NUM_MIRROR_FORMAT; i++)
+     tizen_screenshooter_send_format(res, mirror_format_table[i]);
+}
+
+static void
+_e_screenshooter_cb_shoot(struct wl_client *client,
+                          struct wl_resource *resource,
+                          struct wl_resource *output_resource,
+                          struct wl_resource *buffer_resource)
+{
+   E_Mirror *mirror;
+   E_Mirror_Buffer *buffer;
+
+   if (!_e_tz_screenmirror_buffer_check(buffer_resource))
+     {
+        wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+                               "tizen_screenshooter failed: wrong buffer resource");
+        return;
+     }
+
+   mirror = _e_tz_screenmirror_create(client, resource, output_resource);
+   if (!mirror)
+     {
+        wl_resource_post_no_memory(resource);
+        return;
+     }
+
+   /* resource == shooter means that we're using weston screenshooter */
+   mirror->resource = mirror->shooter;
+
+   if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+                                       PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+     {
+        ERR("_e_screenshooter_cb_shoot: priv check failed");
+        screenshooter_send_done(mirror->resource);
+        goto privilege_fail;
+     }
+
+   buffer = _e_tz_screenmirror_buffer_get(mirror, buffer_resource);
+   if (!buffer)
+     {
+        wl_resource_post_no_memory(resource);
+        _e_tz_screenmirror_destroy(mirror);
+        return;
+     }
+   e_comp_wl_video_buffer_clear(buffer->vbuf);
+
+   mirror->buffer_queue = eina_list_append(mirror->buffer_queue, buffer);
+
+   if (e_dpms_get(mirror->drm_output))
+     {
+        ERR("_e_screenshooter_cb_shoot: dpms on fail");
+        goto dump_done;
+     }
+
+   _e_tz_screenmirror_get_angle(mirror);
+   if (screenshot_auto_rotation)
+     if (mirror->angle == 90 || mirror->angle == 270)
+       mirror->rotate_change = EINA_TRUE;
+
+   if (_e_tz_screenmirror_tdm_capture_support(buffer, TDM_CAPTURE_CAPABILITY_ONESHOT))
+     {
+        if (_e_tz_screenmirror_tdm_capture_oneshot(mirror, buffer) == EINA_TRUE)
+          return;
+        else
+          {
+             tdm_capture_destroy(mirror->capture);
+             mirror->capture = NULL;
+          }
+     }
+   else
+     _e_tz_screenmirror_dump_still(buffer);
+
+dump_done:
+   _e_tz_screenmirror_buffer_free(buffer);
+
+privilege_fail:
+   _e_tz_screenmirror_destroy(mirror);
+}
+
+static const struct screenshooter_interface _e_screenshooter_interface =
+{
+   _e_screenshooter_cb_shoot
+};
+
+static void
+_e_screenshooter_cb_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+   struct wl_resource *res;
+
+   if (!(res = wl_resource_create(client, &screenshooter_interface, MIN(version, 1), id)))
+     {
+        ERR("Could not create screenshooter resource");
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   screenshot_auto_rotation = EINA_TRUE;
+
+   wl_resource_set_implementation(res, &_e_screenshooter_interface, NULL, NULL);
+}
+
+EINTERN int
+e_comp_wl_screenshooter_init(void)
+{
+   if (!e_comp_wl) return 0;
+   if (!e_comp_wl->wl.disp) return 0;
+
+   /* try to add screenshooter to wayland globals */
+   if (!wl_global_create(e_comp_wl->wl.disp, &screenshooter_interface, 1,
+                         NULL, _e_screenshooter_cb_bind))
+     {
+        ERR("Could not add screenshooter to wayland globals");
+        return 0;
+     }
+
+   /* try to add tizen_screenshooter to wayland globals */
+   if (!wl_global_create(e_comp_wl->wl.disp, &tizen_screenshooter_interface, 1,
+                         NULL, _e_tz_screenshooter_cb_bind))
+     {
+        ERR("Could not add tizen_screenshooter to wayland globals");
+        return 0;
+     }
+
+   return 1;
+}
+
+EINTERN void
+e_comp_wl_screenshooter_shutdown(void)
+{
+}
diff --git a/src/bin/e_comp_wl_screenshooter.h b/src/bin/e_comp_wl_screenshooter.h
new file mode 100644 (file)
index 0000000..6c65b4e
--- /dev/null
@@ -0,0 +1,11 @@
+#ifdef E_TYPEDEFS
+
+#else
+#ifndef E_COMP_WL_SCREENSHOOTER_H
+#define E_COMP_WL_SCREENSHOOTER_H
+
+EINTERN int e_comp_wl_screenshooter_init(void);
+EINTERN void e_comp_wl_screenshooter_shutdown(void);
+
+#endif
+#endif
diff --git a/src/bin/e_comp_wl_video.c b/src/bin/e_comp_wl_video.c
new file mode 100644 (file)
index 0000000..e53d50d
--- /dev/null
@@ -0,0 +1,2855 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "e.h"
+#include <tdm.h>
+#include <values.h>
+
+//#define DUMP_BUFFER
+
+static int _video_detail_log_dom = -1;
+static Eina_Bool video_to_primary;
+static Eina_Bool video_punch;
+
+#define BUFFER_MAX_COUNT   5
+#define MIN_WIDTH   32
+
+#undef NEVER_GET_HERE
+#define NEVER_GET_HERE()     CRI("** need to improve more **")
+
+#ifndef CLEAR
+#define CLEAR(x) memset(&(x), 0, sizeof (x))
+#endif
+
+#define VER(fmt,arg...)   ERR("window(0x%08"PRIxPTR") ec(%p): "fmt, video->window, video->ec, ##arg)
+#define VWR(fmt,arg...)   WRN("window(0x%08"PRIxPTR") ec(%p): "fmt, video->window, video->ec, ##arg)
+#define VIN(fmt,arg...)   INF("window(0x%08"PRIxPTR") ec(%p): "fmt, video->window, video->ec, ##arg)
+#define VDB(fmt,arg...)   DBG("window(0x%08"PRIxPTR") ec(%p): "fmt, video->window, video->ec, ##arg)
+
+#define DET(...)          EINA_LOG_DOM_DBG(_video_detail_log_dom, __VA_ARGS__)
+#define VDT(fmt,arg...)   DET("window(0x%08"PRIxPTR"): "fmt, video->window, ##arg)
+
+#define GEO_FMT   "%dx%d(%dx%d+%d+%d) -> (%dx%d+%d+%d) transform(%d)"
+#define GEO_ARG(g) \
+   (g)->input_w, (g)->input_h, \
+   (g)->input_r.w, (g)->input_r.h, (g)->input_r.x, (g)->input_r.y, \
+   (g)->output_r.w, (g)->output_r.h, (g)->output_r.x, (g)->output_r.y, \
+   (g)->transform
+
+struct _E_Video
+{
+   struct wl_resource *video_object;
+   struct wl_resource *surface;
+   E_Client *ec;
+   Ecore_Window window;
+   Ecore_Drm_Output *drm_output;
+   tdm_output *output;
+   E_Output *e_output;
+   tdm_layer *layer;
+   E_Plane *e_plane;
+   Eina_Bool external_video;
+
+   /* input info */
+   tbm_format tbmfmt;
+   Eina_List *input_buffer_list;
+
+   /* in screen coordinates */
+   struct
+     {
+        int input_w, input_h;    /* input buffer's size */
+        Eina_Rectangle input_r;  /* input buffer's content rect */
+        Eina_Rectangle output_r; /* video plane rect */
+        uint transform;          /* rotate, flip */
+
+        Eina_Rectangle tdm_output_r; /* video plane rect in physical output coordinates */
+        uint tdm_transform;          /* rotate, flip in physical output coordinates */
+     } geo, old_geo;
+
+   E_Comp_Wl_Buffer *old_comp_buffer;
+
+   /* converter info */
+   tbm_format pp_tbmfmt;
+   tdm_pp *pp;
+   Eina_Rectangle pp_r;    /* converter dst content rect */
+   Eina_List *pp_buffer_list;
+   Eina_List *next_buffer;
+
+   int output_align;
+   int pp_align;
+   int pp_minw, pp_minh, pp_maxw, pp_maxh;
+   int video_align;
+
+   /* When a video buffer be attached, it will be appended to the end of waiting_list .
+    * And when it's committed, it will be moved to committed_list.
+    * Finally when the commit handler is called, it will become current_fb.
+    */
+   Eina_List    *waiting_list;   /* buffers which are not committed yet */
+   Eina_List    *committed_list; /* buffers which are committed, but not shown on screen yet */
+   E_Comp_Wl_Video_Buf *current_fb;     /* buffer which is showing on screen currently */
+   Eina_Bool     waiting_vblank;
+
+   /* attributes */
+   Eina_List *tdm_prop_list;
+   Eina_List *late_tdm_prop_list;
+   int tdm_mute_id;
+
+   Eina_Bool  cb_registered;
+   Eina_Bool  need_force_render;
+   Eina_Bool  follow_topmost_visibility;
+   Eina_Bool  allowed_attribute;
+
+   Eina_Bool     waiting_video_set;
+   E_Plane_Hook *video_set_hook;
+};
+
+typedef struct _Tdm_Prop_Value
+{
+   unsigned int id;
+   char name[TDM_NAME_LEN];
+   tdm_value value;
+} Tdm_Prop_Value;
+
+static Eina_List *video_list = NULL;
+static Eina_List *video_layers = NULL;
+
+static void _e_video_set(E_Video *video, E_Client *ec);
+static void _e_video_destroy(E_Video *video);
+static void _e_video_render(E_Video *video, const char *func);
+static Eina_Bool _e_video_frame_buffer_show(E_Video *video, E_Comp_Wl_Video_Buf *vbuf);
+static void _e_video_pp_buffer_cb_release(tbm_surface_h surface, void *user_data);
+static void _e_video_video_set_hook(void *data, E_Plane *plane);
+
+static tdm_output* _e_video_tdm_output_get(Ecore_Drm_Output *output);
+static tdm_layer* _e_video_tdm_video_layer_get(tdm_output *output);
+static tdm_layer* _e_video_tdm_avaiable_video_layer_get(tdm_output *output);
+static void _e_video_tdm_set_layer_usable(tdm_layer *layer, Eina_Bool usable);
+static Eina_Bool _e_video_tdm_get_layer_usable(tdm_layer *layer);
+
+static int
+gcd(int a, int b)
+{
+   if (a % b == 0)
+     return b;
+   return gcd(b, a % b);
+}
+
+static int
+lcm(int a, int b)
+{
+   return a * b / gcd(a, b);
+}
+
+static void
+buffer_transform(int width, int height, uint32_t transform, int32_t scale,
+                 int sx, int sy, int *dx, int *dy)
+{
+   switch (transform)
+     {
+      case WL_OUTPUT_TRANSFORM_NORMAL:
+      default:
+         *dx = sx, *dy = sy;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED:
+         *dx = width - sx, *dy = sy;
+         break;
+      case WL_OUTPUT_TRANSFORM_90:
+         *dx = height - sy, *dy = sx;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+         *dx = height - sy, *dy = width - sx;
+         break;
+      case WL_OUTPUT_TRANSFORM_180:
+         *dx = width - sx, *dy = height - sy;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+         *dx = sx, *dy = height - sy;
+         break;
+      case WL_OUTPUT_TRANSFORM_270:
+         *dx = sy, *dy = width - sx;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+         *dx = sy, *dy = sx;
+         break;
+     }
+
+   *dx *= scale;
+   *dy *= scale;
+}
+
+static E_Video*
+find_video_with_surface(struct wl_resource *surface)
+{
+   E_Video *video;
+   Eina_List *l;
+   EINA_LIST_FOREACH(video_list, l, video)
+     {
+        if (video->surface == surface)
+          return video;
+     }
+   return NULL;
+}
+
+static E_Client*
+find_video_child_get(E_Client *ec)
+{
+   E_Client *subc = NULL;
+   Eina_List *l;
+   if (!ec) return NULL;
+   if (e_object_is_del(E_OBJECT(ec))) return NULL;
+   if (!ec->comp_data) return NULL;
+
+   if (ec->comp_data->video_client) return ec;
+
+   EINA_LIST_FOREACH(ec->comp_data->sub.below_list, l, subc)
+     {
+        E_Client *temp= NULL;
+        if (!subc->comp_data || e_object_is_del(E_OBJECT(subc))) continue;
+        temp = find_video_child_get(subc);
+        if(temp) return temp;
+     }
+
+   return NULL;
+}
+
+static E_Client*
+find_topmost_parent_get(E_Client *ec)
+{
+   E_Client *parent = NULL;
+
+   if (!ec->comp_data || !ec->comp_data->sub.data)
+     return ec;
+
+   parent = ec->comp_data->sub.data->parent;
+   while (parent)
+     {
+        if (!parent->comp_data || !parent->comp_data->sub.data)
+          return parent;
+
+        parent = parent->comp_data->sub.data->parent;
+     }
+
+   return ec;
+}
+
+static E_Client*
+find_offscreen_parent_get(E_Client *ec)
+{
+   E_Client *parent = NULL;
+
+   if (!ec->comp_data || !ec->comp_data->sub.data)
+     return NULL;
+
+   parent = ec->comp_data->sub.data->parent;
+   while (parent)
+     {
+        if (!parent->comp_data || !parent->comp_data->sub.data)
+          return NULL;
+
+        if (parent->comp_data->sub.data->remote_surface.offscreen_parent)
+          return parent->comp_data->sub.data->remote_surface.offscreen_parent;
+
+        parent = parent->comp_data->sub.data->parent;
+     }
+
+   return NULL;
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_video_vbuf_find(Eina_List *list, tbm_surface_h buffer)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+   Eina_List *l = NULL;
+
+   EINA_LIST_FOREACH(list, l, vbuf)
+     {
+        if (vbuf->tbm_surface == buffer)
+          return vbuf;
+     }
+
+   return NULL;
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_video_vbuf_find_with_comp_buffer(Eina_List *list, E_Comp_Wl_Buffer *comp_buffer)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+   Eina_List *l = NULL;
+
+   EINA_LIST_FOREACH(list, l, vbuf)
+     {
+        if (vbuf->comp_buffer == comp_buffer)
+          return vbuf;
+     }
+
+   return NULL;
+}
+
+static Eina_Bool
+_e_video_set_layer(E_Video *video, Eina_Bool set)
+{
+   if (!set)
+     {
+        unsigned int usable = 1;
+        if (!video->layer) return EINA_TRUE;
+        tdm_layer_is_usable(video->layer, &usable);
+        if (!usable && !video->waiting_video_set)
+          {
+             VIN("stop video");
+             tdm_layer_unset_buffer(video->layer);
+             tdm_layer_commit(video->layer, NULL, NULL);
+          }
+        VIN("release layer: %p", video->layer);
+        _e_video_tdm_set_layer_usable(video->layer, EINA_TRUE);
+        video->layer = NULL;
+        video->old_comp_buffer = NULL;
+
+        e_plane_video_set(video->e_plane, EINA_FALSE, NULL);
+        video->e_plane = NULL;
+        video->waiting_video_set = EINA_FALSE;
+        if (video->video_set_hook)
+          {
+             e_plane_hook_del(video->video_set_hook);
+             video->video_set_hook = NULL;
+          }
+     }
+   else
+     {
+        if (video->layer) return EINA_TRUE;
+        if (!video->layer)
+          {
+             int zpos;
+             tdm_error ret;
+
+             video->layer = _e_video_tdm_avaiable_video_layer_get(video->output);
+
+             ret = tdm_layer_get_zpos(video->layer, &zpos);
+             if (ret == TDM_ERROR_NONE)
+               video->e_plane = e_output_plane_get_by_zpos(video->e_output, zpos);
+
+             if (!video->e_plane)
+               {
+                  VWR("fail get e_plane");
+                  video->layer = NULL;
+                  return EINA_FALSE;
+               }
+          }
+        if (!video->layer)
+          {
+             VWR("no available layer for video");
+             return EINA_FALSE;
+          }
+        if (!e_plane_video_set(video->e_plane, EINA_TRUE, &video->waiting_video_set))
+          {
+             VWR("fail set video to e_plane");
+             video->layer = NULL;
+             video->e_plane = NULL;
+             return EINA_FALSE;
+          }
+        if (video->waiting_video_set)
+          {
+             if (!video->video_set_hook)
+               video->video_set_hook = e_plane_hook_add(E_PLANE_HOOK_VIDEO_SET, _e_video_video_set_hook, video);
+          }
+
+        VIN("assign layer: %p", video->layer);
+        _e_video_tdm_set_layer_usable(video->layer, EINA_FALSE);
+     }
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_video_is_visible(E_Video *video)
+{
+   E_Client *offscreen_parent;
+
+   if (e_object_is_del(E_OBJECT(video->ec))) return EINA_FALSE;
+
+   if (!e_pixmap_resource_get(video->ec->pixmap))
+     {
+        VDB("no comp buffer");
+        return EINA_FALSE;
+     }
+
+   if (video->ec->comp_data->sub.data && video->ec->comp_data->sub.data->stand_alone)
+     return EINA_TRUE;
+
+   offscreen_parent = find_offscreen_parent_get(video->ec);
+   if (offscreen_parent && offscreen_parent->visibility.obscured == E_VISIBILITY_FULLY_OBSCURED)
+     {
+        VDB("video surface invisible: offscreen fully obscured");
+        return EINA_FALSE;
+     }
+
+   if (!evas_object_visible_get(video->ec->frame))
+     {
+        VDB("evas obj invisible");
+        return EINA_FALSE;
+     }
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_video_parent_is_viewable(E_Video *video)
+{
+   E_Client *topmost_parent;
+
+   if (e_object_is_del(E_OBJECT(video->ec))) return EINA_FALSE;
+
+   topmost_parent = find_topmost_parent_get(video->ec);
+
+   if (!topmost_parent)
+     return EINA_FALSE;
+
+   if (topmost_parent == video->ec)
+     {
+        VDB("There is no video parent surface");
+        return EINA_FALSE;
+     }
+
+   if (!topmost_parent->visible)
+     {
+        VDB("parent(0x%08"PRIxPTR") not viewable", (Ecore_Window)e_client_util_win_get(topmost_parent));
+        return EINA_FALSE;
+     }
+
+   if (!e_pixmap_resource_get(topmost_parent->pixmap))
+     {
+        VDB("parent(0x%08"PRIxPTR") no comp buffer", (Ecore_Window)e_client_util_win_get(topmost_parent));
+        return EINA_FALSE;
+     }
+
+   return EINA_TRUE;
+}
+
+static void
+_e_video_input_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data)
+{
+   E_Video *video = data;
+   Eina_Bool need_hide = EINA_FALSE;
+
+   VDT("Buffer(%p) to be free, refcnt(%d)", vbuf, vbuf->ref_cnt);
+
+   video->input_buffer_list = eina_list_remove(video->input_buffer_list, vbuf);
+
+   if (vbuf->comp_buffer)
+     e_comp_wl_buffer_reference(&vbuf->buffer_ref, NULL);
+
+   if (video->current_fb == vbuf)
+     {
+        VIN("current fb destroyed");
+        e_comp_wl_video_buffer_set_use(video->current_fb, EINA_FALSE);
+        video->current_fb = NULL;
+        need_hide = EINA_TRUE;
+     }
+
+   if (eina_list_data_find(video->committed_list, vbuf))
+     {
+        VIN("committed fb destroyed");
+        video->committed_list = eina_list_remove(video->committed_list, vbuf);
+        e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+        need_hide = EINA_TRUE;
+     }
+
+   if (eina_list_data_find(video->waiting_list, vbuf))
+     {
+        VIN("waiting fb destroyed");
+        video->waiting_list = eina_list_remove(video->waiting_list, vbuf);
+     }
+
+   if (need_hide && video->layer)
+     _e_video_frame_buffer_show(video, NULL);
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_video_input_buffer_get(E_Video *video, E_Comp_Wl_Buffer *comp_buffer, Eina_Bool scanout)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+
+   vbuf = _e_video_vbuf_find_with_comp_buffer(video->input_buffer_list, comp_buffer);
+   if (vbuf)
+     {
+        vbuf->content_r = video->geo.input_r;
+        return vbuf;
+     }
+
+   vbuf = e_comp_wl_video_buffer_create_comp(comp_buffer);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+   if (video->pp)
+     {
+        if (video->pp_align != -1 && (vbuf->width_from_pitch % video->pp_align))
+          {
+             E_Comp_Wl_Video_Buf *temp, *temp2;
+             int aligned_width = ROUNDUP(vbuf->width_from_pitch, video->pp_align);
+
+             temp = e_comp_wl_video_buffer_alloc(aligned_width, vbuf->height, vbuf->tbmfmt, scanout);
+             if (!temp)
+               {
+                  e_comp_wl_video_buffer_unref(vbuf);
+                  return NULL;
+               }
+
+             VDB("copy vbuf(%d,%dx%d) => vbuf(%d,%dx%d)",
+                 MSTAMP(vbuf), vbuf->width_from_pitch, vbuf->height,
+                 MSTAMP(temp), temp->width, temp->height);
+
+             e_comp_wl_video_buffer_copy(vbuf, temp);
+             temp2 = vbuf;
+             vbuf = temp;
+             e_comp_wl_video_buffer_unref(temp2);
+
+             video->geo.input_w = vbuf->width_from_pitch;
+#ifdef DUMP_BUFFER
+             char file[256];
+             static int i;
+             snprintf(file, sizeof file, "%s_%d", "copy", i++);
+             tbm_surface_internal_dump_buffer(tbm_buffer, file, i++, 0);
+#endif
+          }
+     }
+
+   vbuf->content_r = video->geo.input_r;
+
+   video->input_buffer_list = eina_list_append(video->input_buffer_list, vbuf);
+   e_comp_wl_video_buffer_free_func_add(vbuf, _e_video_input_buffer_cb_free, video);
+
+   VDT("Client(%s):PID(%d) RscID(%d), Buffer(%p) created, refcnt:%d"
+       " scanout=%d", e_client_util_name_get(video->ec) ?: "No Name" ,
+       video->ec->netwm.pid, wl_resource_get_id(video->surface), vbuf,
+       vbuf->ref_cnt, scanout);
+
+   return vbuf;
+}
+
+static void
+_e_video_input_buffer_valid(E_Video *video, E_Comp_Wl_Buffer *comp_buffer)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+   Eina_List *l;
+
+   EINA_LIST_FOREACH(video->input_buffer_list, l, vbuf)
+     {
+        tbm_surface_h tbm_surf;
+        tbm_bo bo;
+        uint32_t size = 0, offset = 0, pitch = 0;
+
+        if (!vbuf->comp_buffer) continue;
+        if (vbuf->resource == comp_buffer->resource)
+          {
+             WRN("got wl_buffer@%d twice", wl_resource_get_id(comp_buffer->resource));
+             return;
+          }
+
+        tbm_surf = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, comp_buffer->resource);
+        bo = tbm_surface_internal_get_bo(tbm_surf, 0);
+        tbm_surface_internal_get_plane_data(tbm_surf, 0, &size, &offset, &pitch);
+
+        if (vbuf->names[0] == tbm_bo_export(bo) && vbuf->offsets[0] == offset)
+          {
+             WRN("can tearing: wl_buffer@%d, wl_buffer@%d are same. gem_name(%d)",
+                 wl_resource_get_id(vbuf->resource),
+                 wl_resource_get_id(comp_buffer->resource), vbuf->names[0]);
+             return;
+          }
+     }
+}
+
+static void
+_e_video_pp_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data)
+{
+   E_Video *video = data;
+
+   e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+
+   if (video->current_fb == vbuf)
+     video->current_fb = NULL;
+
+   video->committed_list = eina_list_remove(video->committed_list, vbuf);
+
+   video->waiting_list = eina_list_remove(video->waiting_list, vbuf);
+
+   video->pp_buffer_list = eina_list_remove(video->pp_buffer_list, vbuf);
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_video_pp_buffer_get(E_Video *video, int width, int height)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+   Eina_List *l;
+   int i = 0;
+   int aligned_width;
+
+   if (video->video_align != -1)
+     aligned_width = ROUNDUP(width, video->video_align);
+   else
+     aligned_width = width;
+
+   if (video->pp_buffer_list)
+     {
+        vbuf = eina_list_data_get(video->pp_buffer_list);
+        EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+        /* if we need bigger pp_buffers, destroy all pp_buffers and create */
+        if (aligned_width > vbuf->width_from_pitch || height != vbuf->height)
+          {
+             Eina_List *ll;
+
+             VIN("pp buffer changed: %dx%d => %dx%d",
+                 vbuf->width_from_pitch, vbuf->height,
+                 aligned_width, height);
+
+             EINA_LIST_FOREACH_SAFE(video->pp_buffer_list, l, ll, vbuf)
+               {
+                  tdm_buffer_remove_release_handler(vbuf->tbm_surface,
+                                                    _e_video_pp_buffer_cb_release, vbuf);
+                  /* free forcely */
+                  e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+                  e_comp_wl_video_buffer_unref(vbuf);
+               }
+             if (video->pp_buffer_list)
+               NEVER_GET_HERE();
+
+             if (video->waiting_list)
+               NEVER_GET_HERE();
+          }
+     }
+
+   if (!video->pp_buffer_list)
+     {
+        for (i = 0; i < BUFFER_MAX_COUNT; i++)
+          {
+             vbuf = e_comp_wl_video_buffer_alloc(aligned_width, height, video->pp_tbmfmt, EINA_TRUE);
+             EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+             e_comp_wl_video_buffer_free_func_add(vbuf, _e_video_pp_buffer_cb_free, video);
+             video->pp_buffer_list = eina_list_append(video->pp_buffer_list, vbuf);
+
+          }
+
+        VIN("pp buffer created: %dx%d, %c%c%c%c",
+            vbuf->width_from_pitch, height, FOURCC_STR(video->pp_tbmfmt));
+
+        video->next_buffer = video->pp_buffer_list;
+     }
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(video->pp_buffer_list, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(video->next_buffer, NULL);
+
+   l = video->next_buffer;
+   while ((vbuf = video->next_buffer->data))
+     {
+        video->next_buffer = (video->next_buffer->next) ? video->next_buffer->next : video->pp_buffer_list;
+
+        if (!vbuf->in_use)
+          return vbuf;
+
+        if (l == video->next_buffer)
+          {
+             VWR("all video framebuffers in use (max:%d)", BUFFER_MAX_COUNT);
+             return NULL;
+          }
+     }
+
+   return NULL;
+}
+
+static Ecore_Drm_Output*
+_e_video_drm_output_find(E_Client *ec)
+{
+   Ecore_Drm_Device *dev;
+   Ecore_Drm_Output *output;
+   Eina_List *devs;
+   Eina_List *l, *ll;
+
+   devs = eina_list_clone(ecore_drm_devices_get());
+   EINA_LIST_FOREACH(devs, l, dev)
+      EINA_LIST_FOREACH(dev->outputs, ll, output)
+        {
+           int x, y, w, h;
+           ecore_drm_output_position_get(output, &x, &y);
+           ecore_drm_output_current_resolution_get(output, &w, &h, NULL);
+           if (x <= ec->x && y <= ec->y && ec->x < x + w && ec->y < y + h)
+             {
+                eina_list_free(devs);
+                return output;
+             }
+        }
+   eina_list_free(devs);
+   return NULL;
+}
+
+/* convert from logical screen to physical output */
+static void
+_e_video_geometry_cal_physical(E_Video *video)
+{
+   E_Zone *zone;
+   E_Comp_Wl_Output *output;
+   E_Client *topmost;
+   int tran, flip;
+   int transform;
+
+   topmost = find_topmost_parent_get(video->ec);
+   EINA_SAFETY_ON_NULL_GOTO(topmost, normal);
+
+   output = e_comp_wl_output_find(topmost);
+   EINA_SAFETY_ON_NULL_GOTO(output, normal);
+
+   zone = e_comp_zone_xy_get(topmost->x, topmost->y);
+   EINA_SAFETY_ON_NULL_GOTO(zone, normal);
+
+   tran = video->geo.transform & 0x3;
+   flip = video->geo.transform & 0x4;
+   transform = flip + (tran + output->transform) % 4;
+   switch(transform)
+     {
+      case WL_OUTPUT_TRANSFORM_90:
+         video->geo.tdm_transform = TDM_TRANSFORM_270;
+         break;
+      case WL_OUTPUT_TRANSFORM_180:
+         video->geo.tdm_transform = TDM_TRANSFORM_180;
+         break;
+      case WL_OUTPUT_TRANSFORM_270:
+         video->geo.tdm_transform = TDM_TRANSFORM_90;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED:
+         video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+         video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_270;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+         video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_180;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+         video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_90;
+         break;
+      case WL_OUTPUT_TRANSFORM_NORMAL:
+      default:
+         video->geo.tdm_transform = TDM_TRANSFORM_NORMAL;
+         break;
+     }
+
+   if (output->transform % 2)
+     {
+        if (video->geo.tdm_transform == TDM_TRANSFORM_FLIPPED)
+          video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_180;
+        else if (video->geo.tdm_transform == TDM_TRANSFORM_FLIPPED_90)
+          video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_270;
+        else if (video->geo.tdm_transform == TDM_TRANSFORM_FLIPPED_180)
+          video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED;
+        else if (video->geo.tdm_transform == TDM_TRANSFORM_FLIPPED_270)
+          video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_90;
+     }
+
+   if (output->transform == 0)
+     video->geo.tdm_output_r = video->geo.output_r;
+   else
+     e_comp_wl_rect_convert(zone->w, zone->h, output->transform, 1,
+                            video->geo.output_r.x, video->geo.output_r.y,
+                            video->geo.output_r.w, video->geo.output_r.h,
+                            &video->geo.tdm_output_r.x, &video->geo.tdm_output_r.y,
+                            &video->geo.tdm_output_r.w, &video->geo.tdm_output_r.h);
+
+   VDB("geomtry: screen(%d,%d %dx%d | %d) => %d => physical(%d,%d %dx%d | %d)",
+       EINA_RECTANGLE_ARGS(&video->geo.output_r), video->geo.transform, transform,
+       EINA_RECTANGLE_ARGS(&video->geo.tdm_output_r), video->geo.tdm_transform);
+
+   return;
+normal:
+   video->geo.tdm_output_r = video->geo.output_r;
+   video->geo.tdm_transform = video->geo.transform;
+}
+
+static Eina_Bool
+_e_video_geometry_cal_viewport(E_Video *video)
+{
+   E_Client *ec = video->ec;
+   E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+   E_Comp_Wl_Subsurf_Data *sdata;
+   int x1, y1, x2, y2;
+   int tx1, ty1, tx2, ty2;
+   E_Comp_Wl_Buffer *comp_buffer;
+   tbm_surface_h tbm_surf;
+   uint32_t size = 0, offset = 0, pitch = 0;
+   int bw, bh;
+   int width_from_buffer, height_from_buffer;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ec, EINA_FALSE);
+
+   comp_buffer = e_pixmap_resource_get(video->ec->pixmap);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(comp_buffer, EINA_FALSE);
+
+   tbm_surf = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, comp_buffer->resource);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surf, EINA_FALSE);
+
+   tbm_surface_internal_get_plane_data(tbm_surf, 0, &size, &offset, &pitch);
+
+   /* input geometry */
+   if (IS_RGB(video->tbmfmt))
+     video->geo.input_w = pitch / 4;
+   else
+     video->geo.input_w = pitch;
+
+   video->geo.input_h = tbm_surface_get_height(tbm_surf);
+
+   bw = tbm_surface_get_width(tbm_surf);
+   bh = tbm_surface_get_height(tbm_surf);
+
+   switch (vp->buffer.transform)
+     {
+      case WL_OUTPUT_TRANSFORM_90:
+      case WL_OUTPUT_TRANSFORM_270:
+      case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+      case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+         width_from_buffer = bh / vp->buffer.scale;
+         height_from_buffer = bw / vp->buffer.scale;
+         break;
+      default:
+         width_from_buffer = bw / vp->buffer.scale;
+         height_from_buffer = bh / vp->buffer.scale;
+         break;
+     }
+
+
+   if (vp->buffer.src_width == wl_fixed_from_int(-1))
+     {
+        x1 = 0.0;
+        y1 = 0.0;
+        x2 = width_from_buffer;
+        y2 = height_from_buffer;
+     }
+   else
+     {
+        x1 = wl_fixed_to_int(vp->buffer.src_x);
+        y1 = wl_fixed_to_int(vp->buffer.src_y);
+        x2 = wl_fixed_to_int(vp->buffer.src_x + vp->buffer.src_width);
+        y2 = wl_fixed_to_int(vp->buffer.src_y + vp->buffer.src_height);
+     }
+
+#if 0
+   VDB("transform(%d) scale(%d) buffer(%dx%d) src(%d,%d %d,%d) viewport(%dx%d)",
+       vp->buffer.transform, vp->buffer.scale,
+       width_from_buffer, height_from_buffer,
+       x1, y1, x2 - x1, y2 - y1,
+       ec->comp_data->width_from_viewport, ec->comp_data->height_from_viewport);
+#endif
+
+   buffer_transform(width_from_buffer, height_from_buffer,
+                    vp->buffer.transform, vp->buffer.scale, x1, y1, &tx1, &ty1);
+   buffer_transform(width_from_buffer, height_from_buffer,
+                    vp->buffer.transform, vp->buffer.scale, x2, y2, &tx2, &ty2);
+
+   video->geo.input_r.x = (tx1 <= tx2) ? tx1 : tx2;
+   video->geo.input_r.y = (ty1 <= ty2) ? ty1 : ty2;
+   video->geo.input_r.w = (tx1 <= tx2) ? tx2 - tx1 : tx1 - tx2;
+   video->geo.input_r.h = (ty1 <= ty2) ? ty2 - ty1 : ty1 - ty2;
+
+   /* output geometry */
+   if ((sdata = ec->comp_data->sub.data))
+     {
+        if (sdata->parent)
+          {
+             video->geo.output_r.x = sdata->parent->x + sdata->position.x;
+             video->geo.output_r.y = sdata->parent->y + sdata->position.y;
+          }
+        else
+          {
+             video->geo.output_r.x = sdata->position.x;
+             video->geo.output_r.y = sdata->position.y;
+          }
+     }
+   else
+     {
+        video->geo.output_r.x = ec->x;
+        video->geo.output_r.y = ec->y;
+     }
+
+   video->geo.output_r.w = ec->comp_data->width_from_viewport;
+   video->geo.output_r.w = (video->geo.output_r.w + 1) & ~1;
+   video->geo.output_r.h = ec->comp_data->height_from_viewport;
+
+   e_comp_object_frame_xy_unadjust(ec->frame,
+                                   video->geo.output_r.x, video->geo.output_r.y,
+                                   &video->geo.output_r.x, &video->geo.output_r.y);
+   e_comp_object_frame_wh_unadjust(ec->frame,
+                                   video->geo.output_r.w, video->geo.output_r.h,
+                                   &video->geo.output_r.w, &video->geo.output_r.h);
+
+   video->geo.transform = vp->buffer.transform;
+
+   _e_video_geometry_cal_physical(video);
+
+#if 0
+   VDB("geometry(%dx%d  %d,%d %dx%d  %d,%d %dx%d  %d)",
+       video->geo.input_w, video->geo.input_h,
+       EINA_RECTANGLE_ARGS(&video->geo.input_r),
+       EINA_RECTANGLE_ARGS(&video->geo.output_r),
+       video->geo.transform);
+#endif
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_video_geometry_cal_map(E_Video *video)
+{
+   E_Client *ec;
+   const Evas_Map *m;
+   Evas_Coord x1, x2, y1, y2;
+   Eina_Rectangle output_r;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(video, EINA_FALSE);
+
+   ec = video->ec;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ec, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ec->frame, EINA_FALSE);
+
+   m = evas_object_map_get(ec->frame);
+   if (!m) return EINA_TRUE;
+
+   /* If frame has map, it means that ec's geometry is decided by map's geometry.
+    * ec->x,y,w,h and ec->client.x,y,w,h is not useful.
+    */
+
+   evas_map_point_coord_get(m, 0, &x1, &y1, NULL);
+   evas_map_point_coord_get(m, 2, &x2, &y2, NULL);
+
+   output_r.x = x1;
+   output_r.y = y1;
+   output_r.w = x2 - x1;
+   output_r.w = (output_r.w + 1) & ~1;
+   output_r.h = y2 - y1;
+   output_r.h = (output_r.h + 1) & ~1;
+
+   if (!memcmp(&video->geo.output_r, &output_r, sizeof(Eina_Rectangle)))
+     return EINA_FALSE;
+
+   VDB("frame(%p) m(%p) output(%d,%d %dx%d) => (%d,%d %dx%d)", ec->frame, m,
+       EINA_RECTANGLE_ARGS(&video->geo.output_r), EINA_RECTANGLE_ARGS(&output_r));
+
+   video->geo.output_r = output_r;
+
+   _e_video_geometry_cal_physical(video);
+
+   return EINA_TRUE;
+}
+
+static void
+_e_video_geometry_cal_to_input(int output_w, int output_h, int input_w, int input_h,
+                               tdm_transform trasnform, int ox, int oy, int *ix, int *iy)
+{
+   float ratio_w, ratio_h;
+
+   switch(trasnform)
+     {
+      case WL_OUTPUT_TRANSFORM_NORMAL:
+      default:
+         *ix = ox, *iy = oy;
+         break;
+      case WL_OUTPUT_TRANSFORM_270:
+         *ix = oy, *iy = output_w - ox;
+         break;
+      case WL_OUTPUT_TRANSFORM_180:
+         *ix = output_w - ox, *iy = output_h - oy;
+         break;
+      case WL_OUTPUT_TRANSFORM_90:
+         *ix = output_h - oy, *iy = ox;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED:
+         *ix = output_w - ox, *iy = oy;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+         *ix = oy, *iy = ox;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+         *ix = ox, *iy = output_h - oy;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+         *ix = output_h - oy, *iy = output_w - ox;
+         break;
+     }
+   if (trasnform & 0x1)
+     {
+        ratio_w = (float)input_w / output_h;
+        ratio_h = (float)input_h / output_w;
+     }
+   else
+     {
+        ratio_w = (float)input_w / output_w;
+        ratio_h = (float)input_h / output_h;
+     }
+   *ix *= ratio_w;
+   *iy *= ratio_h;
+}
+
+static void
+_e_video_geometry_cal_to_input_rect(E_Video * video, Eina_Rectangle *srect, Eina_Rectangle *drect)
+{
+   int xf1, yf1, xf2, yf2;
+
+   /* first transform box coordinates if the scaler is set */
+
+   xf1 = srect->x;
+   yf1 = srect->y;
+   xf2 = srect->x + srect->w;
+   yf2 = srect->y + srect->h;
+
+   _e_video_geometry_cal_to_input(video->geo.output_r.w, video->geo.output_r.h,
+                                  video->geo.input_r.w, video->geo.input_r.h,
+                                  video->geo.transform, xf1, yf1, &xf1, &yf1);
+   _e_video_geometry_cal_to_input(video->geo.output_r.w, video->geo.output_r.h,
+                                  video->geo.input_r.w, video->geo.input_r.h,
+                                  video->geo.transform, xf2, yf2, &xf2, &yf2);
+
+   drect->x = MIN(xf1, xf2);
+   drect->y = MIN(yf1, yf2);
+   drect->w = MAX(xf1, xf2) - drect->x;
+   drect->h = MAX(yf1, yf2) - drect->y;
+}
+
+static Eina_Bool
+_e_video_geometry_cal(E_Video * video)
+{
+   Eina_Rectangle screen = {0,};
+   Eina_Rectangle output_r = {0,}, input_r = {0,};
+   const tdm_output_mode *mode = NULL;
+   tdm_error tdm_err = TDM_ERROR_NONE;
+
+   /* get geometry information with buffer scale, transform and viewport. */
+   if (!_e_video_geometry_cal_viewport(video))
+     return EINA_FALSE;
+
+   _e_video_geometry_cal_map(video);
+
+   if (e_config->eom_enable == EINA_TRUE && video->external_video)
+     {
+        tdm_err = tdm_output_get_mode(video->output, &mode);
+        if (tdm_err != TDM_ERROR_NONE)
+          return EINA_FALSE;
+
+        if (mode == NULL)
+          return EINA_FALSE;
+
+        screen.w = mode->hdisplay;
+        screen.h = mode->vdisplay;
+     }
+   else
+     {
+        E_Zone *zone;
+        E_Client *topmost;
+
+        topmost = find_topmost_parent_get(video->ec);
+        EINA_SAFETY_ON_NULL_RETURN_VAL(topmost, EINA_FALSE);
+
+        zone = e_comp_zone_xy_get(topmost->x, topmost->y);
+        EINA_SAFETY_ON_NULL_RETURN_VAL(zone, EINA_FALSE);
+
+        screen.w = zone->w;
+        screen.h = zone->h;
+     }
+
+   e_comp_wl_video_buffer_size_get(video->ec, &input_r.w, &input_r.h);
+   // when topmost is not mapped, input size can be abnormal.
+   // in this case, it will be render by topmost showing.
+   if (!eina_rectangle_intersection(&video->geo.input_r, &input_r) || (video->geo.input_r.w <= 10 || video->geo.input_r.h <= 10))
+     {
+        VER("input area is empty");
+        return EINA_FALSE;
+     }
+
+   if (video->geo.output_r.x >= 0 && video->geo.output_r.y >= 0 &&
+       (video->geo.output_r.x + video->geo.output_r.w) <= screen.w &&
+       (video->geo.output_r.y + video->geo.output_r.h) <= screen.h)
+     return EINA_TRUE;
+
+   /* TODO: need to improve */
+
+   output_r = video->geo.output_r;
+   if (!eina_rectangle_intersection(&output_r, &screen))
+     {
+        VER("output_r(%d,%d %dx%d) screen(%d,%d %dx%d) => intersect(%d,%d %dx%d)",
+            EINA_RECTANGLE_ARGS(&video->geo.output_r),
+            EINA_RECTANGLE_ARGS(&screen), EINA_RECTANGLE_ARGS(&output_r));
+        return EINA_TRUE;
+     }
+
+   output_r.x -= video->geo.output_r.x;
+   output_r.y -= video->geo.output_r.y;
+
+   if (output_r.w <= 0 || output_r.h <= 0)
+     {
+        VER("output area is empty");
+        return EINA_FALSE;
+     }
+
+   VDB("output(%d,%d %dx%d) input(%d,%d %dx%d)",
+       EINA_RECTANGLE_ARGS(&output_r), EINA_RECTANGLE_ARGS(&input_r));
+
+   _e_video_geometry_cal_to_input_rect(video, &output_r, &input_r);
+
+   VDB("output(%d,%d %dx%d) input(%d,%d %dx%d)",
+       EINA_RECTANGLE_ARGS(&output_r), EINA_RECTANGLE_ARGS(&input_r));
+
+   output_r.x += video->geo.output_r.x;
+   output_r.y += video->geo.output_r.y;
+
+   input_r.x += video->geo.input_r.x;
+   input_r.y += video->geo.input_r.y;
+
+   output_r.x = output_r.x & ~1;
+   output_r.w = (output_r.w + 1) & ~1;
+
+   input_r.x = input_r.x & ~1;
+   input_r.w = (input_r.w + 1) & ~1;
+
+   video->geo.output_r = output_r;
+   video->geo.input_r = input_r;
+
+   _e_video_geometry_cal_physical(video);
+
+   return EINA_TRUE;
+}
+
+static void
+_e_video_format_info_get(E_Video *video)
+{
+   E_Comp_Wl_Buffer *comp_buffer;
+   tbm_surface_h tbm_surf;
+
+   comp_buffer = e_pixmap_resource_get(video->ec->pixmap);
+   EINA_SAFETY_ON_NULL_RETURN(comp_buffer);
+
+   tbm_surf = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, comp_buffer->resource);
+   EINA_SAFETY_ON_NULL_RETURN(tbm_surf);
+
+   video->tbmfmt = tbm_surface_get_format(tbm_surf);
+}
+
+static Eina_Bool
+_e_video_can_commit(E_Video *video)
+{
+   if (e_config->eom_enable == EINA_TRUE)
+     {
+        if (!video->external_video && e_dpms_get(video->drm_output))
+          return EINA_FALSE;
+     }
+   else
+     if (e_dpms_get(video->drm_output))
+       return EINA_FALSE;
+
+   if (!_e_video_is_visible(video))
+     return EINA_FALSE;
+
+   return EINA_TRUE;
+}
+
+static void
+_e_video_commit_handler(tdm_layer *layer, unsigned int sequence,
+                        unsigned int tv_sec, unsigned int tv_usec,
+                        void *user_data)
+{
+   E_Video *video;
+   Eina_List *l;
+   E_Comp_Wl_Video_Buf *vbuf;
+
+   EINA_LIST_FOREACH(video_list, l, video)
+     {
+        if (video == user_data) break;
+     }
+
+   if (!video) return;
+   if (!video->committed_list) return;
+
+   if (_e_video_can_commit(video))
+     {
+        tbm_surface_h displaying_buffer = tdm_layer_get_displaying_buffer(video->layer, NULL);
+
+        EINA_LIST_FOREACH(video->committed_list, l, vbuf)
+          {
+             if (vbuf->tbm_surface == displaying_buffer) break;
+          }
+        if (!vbuf) return;
+     }
+   else
+     vbuf = eina_list_nth(video->committed_list, 0);
+
+   video->committed_list = eina_list_remove(video->committed_list, vbuf);
+
+   /* client can attachs the same wl_buffer twice. */
+   if (video->current_fb && VBUF_IS_VALID(video->current_fb) && vbuf != video->current_fb)
+     {
+        e_comp_wl_video_buffer_set_use(video->current_fb, EINA_FALSE);
+
+        if (video->current_fb->comp_buffer)
+          e_comp_wl_buffer_reference(&video->current_fb->buffer_ref, NULL);
+     }
+
+   video->current_fb = vbuf;
+
+   VDB("current_fb(%d)", MSTAMP(video->current_fb));
+}
+
+static void
+_e_video_vblank_handler(tdm_output *output, unsigned int sequence,
+                        unsigned int tv_sec, unsigned int tv_usec,
+                        void *user_data)
+{
+   E_Video *video;
+   Eina_List *l;
+   E_Comp_Wl_Video_Buf *vbuf;
+
+   EINA_LIST_FOREACH(video_list, l, video)
+     {
+        if (video == user_data) break;
+     }
+
+   if (!video) return;
+
+   video->waiting_vblank = EINA_FALSE;
+
+   if (!video->waiting_list) return;
+
+   vbuf = eina_list_nth(video->waiting_list, 0);
+
+   video->waiting_list = eina_list_remove(video->waiting_list, vbuf);
+   video->committed_list = eina_list_append(video->committed_list, vbuf);
+
+   if (!_e_video_can_commit(video))
+     goto no_commit;
+
+   if (!_e_video_frame_buffer_show(video, vbuf))
+     goto no_commit;
+
+   goto done;
+
+no_commit:
+   _e_video_commit_handler(NULL, 0, 0, 0, video);
+   _e_video_vblank_handler(NULL, 0, 0, 0, video);
+done:
+   return;
+}
+
+static void
+_e_video_video_set_hook(void *data, E_Plane *plane)
+{
+   E_Comp_Wl_Video_Buf *vbuf = NULL;
+   E_Video *video = (E_Video *)data;
+
+   if (video->e_plane != plane) return;
+   if (!video->waiting_video_set) return;
+
+   video->waiting_video_set = EINA_FALSE;
+
+   if (!video->waiting_list) return;
+
+   vbuf = eina_list_nth(video->waiting_list, 0);
+
+   video->waiting_list = eina_list_remove(video->waiting_list, vbuf);
+   video->committed_list = eina_list_append(video->committed_list, vbuf);
+
+   if (!_e_video_can_commit(video))
+     goto no_commit;
+
+   if (!_e_video_frame_buffer_show(video, vbuf))
+     goto no_commit;
+
+   goto done;
+
+no_commit:
+   _e_video_commit_handler(NULL, 0, 0, 0, video);
+   _e_video_vblank_handler(NULL, 0, 0, 0, video);
+done:
+   return;
+}
+
+static Eina_Bool
+_e_video_frame_buffer_show(E_Video *video, E_Comp_Wl_Video_Buf *vbuf)
+{
+   tdm_info_layer info, old_info;
+   tdm_error ret;
+   E_Client *topmost;
+   Tdm_Prop_Value *prop;
+
+   if (!vbuf)
+     {
+        if (video->layer)
+          {
+             VIN("unset layer: hide");
+             _e_video_set_layer(video, EINA_FALSE);
+          }
+        return EINA_TRUE;
+     }
+
+   if (!video->layer)
+     {
+        VIN("set layer: show");
+        if (!_e_video_set_layer(video, EINA_TRUE))
+          {
+             VER("set layer failed");
+             return EINA_FALSE;
+          }
+        // need call tdm property in list
+        Tdm_Prop_Value *prop;
+        EINA_LIST_FREE(video->tdm_prop_list, prop)
+          {
+             VIN("call property(%s), value(%d)", prop->name, (unsigned int)prop->value.u32);
+             tdm_layer_set_property(video->layer, prop->id, prop->value);
+             free(prop);
+          }
+     }
+
+   CLEAR(old_info);
+   ret = tdm_layer_get_info(video->layer, &old_info);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+
+   CLEAR(info);
+   info.src_config.size.h = vbuf->width_from_pitch;
+   info.src_config.size.v = vbuf->height_from_size;
+   info.src_config.pos.x = vbuf->content_r.x;
+   info.src_config.pos.y = vbuf->content_r.y;
+   info.src_config.pos.w = vbuf->content_r.w;
+   info.src_config.pos.h = vbuf->content_r.h;
+   info.dst_pos.x = video->geo.tdm_output_r.x;
+   info.dst_pos.y = video->geo.tdm_output_r.y;
+   info.dst_pos.w = video->geo.tdm_output_r.w;
+   info.dst_pos.h = video->geo.tdm_output_r.h;
+   info.transform = vbuf->content_t;
+
+   if (memcmp(&old_info, &info, sizeof(tdm_info_layer)))
+     {
+        ret = tdm_layer_set_info(video->layer, &info);
+        EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+     }
+
+   ret = tdm_layer_set_buffer(video->layer, vbuf->tbm_surface);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+
+   ret = tdm_layer_commit(video->layer, _e_video_commit_handler, video);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+
+   ret = tdm_output_wait_vblank(video->output, 1, 0, _e_video_vblank_handler, video);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+
+   video->waiting_vblank = EINA_TRUE;
+
+   EINA_LIST_FREE(video->late_tdm_prop_list, prop)
+     {
+        VIN("call property(%s), value(%d)", prop->name, (unsigned int)prop->value.u32);
+        tdm_layer_set_property(video->layer, prop->id, prop->value);
+        free(prop);
+     }
+
+   topmost = find_topmost_parent_get(video->ec);
+   if (topmost && (topmost->argb || topmost->comp_data->sub.below_obj) &&
+       !e_comp_object_mask_has(video->ec->frame))
+     {
+        Eina_Bool do_punch = EINA_TRUE;
+
+        /* FIXME: the mask obj can be drawn at the wrong position in the beginnig
+         * time. It happens caused by window manager policy.
+         */
+        if ((topmost->fullscreen || topmost->maximized) &&
+            (video->geo.output_r.x == 0 || video->geo.output_r.y == 0))
+          {
+             int bw, bh;
+
+             e_pixmap_size_get(topmost->pixmap, &bw, &bh);
+
+             if (bw > 100 && bh > 100 &&
+                 video->geo.output_r.w < 100 && video->geo.output_r.h < 100)
+               {
+                  VIN("don't punch. (%dx%d, %dx%d)",
+                      bw, bh, video->geo.output_r.w, video->geo.output_r.h);
+                  do_punch = EINA_FALSE;
+               }
+          }
+
+        if (do_punch)
+          {
+             e_comp_object_mask_set(video->ec->frame, EINA_TRUE);
+             VIN("punched");
+          }
+     }
+
+   if (video_punch)
+     {
+        e_comp_object_mask_set(video->ec->frame, EINA_TRUE);
+        VIN("punched");
+     }
+
+   VDT("Client(%s):PID(%d) RscID(%d), Buffer(%p, refcnt:%d) is shown."
+       "Geometry details are : buffer size(%dx%d) src(%d,%d, %dx%d)"
+       " dst(%d,%d, %dx%d), transform(%d)",
+       e_client_util_name_get(video->ec) ?: "No Name" , video->ec->netwm.pid,
+       wl_resource_get_id(video->surface), vbuf, vbuf->ref_cnt,
+       info.src_config.size.h, info.src_config.size.v, info.src_config.pos.x,
+       info.src_config.pos.y, info.src_config.pos.w, info.src_config.pos.h,
+       info.dst_pos.x, info.dst_pos.y, info.dst_pos.w, info.dst_pos.h, info.transform);
+
+
+   return EINA_TRUE;
+}
+
+static void
+_e_video_buffer_show(E_Video *video, E_Comp_Wl_Video_Buf *vbuf, unsigned int transform)
+{
+   vbuf->content_t = transform;
+
+   e_comp_wl_video_buffer_set_use(vbuf, EINA_TRUE);
+
+   if (vbuf->comp_buffer)
+     e_comp_wl_buffer_reference(&vbuf->buffer_ref, vbuf->comp_buffer);
+
+   if (!video->waiting_vblank || !video->waiting_video_set)
+     video->committed_list = eina_list_append(video->committed_list, vbuf);
+   else
+     {
+        video->waiting_list = eina_list_append(video->waiting_list, vbuf);
+        VDB("There are waiting fbs more than 1");
+        return;
+     }
+
+   if (!_e_video_can_commit(video))
+     goto no_commit;
+
+   if (!_e_video_frame_buffer_show(video, vbuf))
+     goto no_commit;
+
+   return;
+
+no_commit:
+   _e_video_commit_handler(NULL, 0, 0, 0, video);
+   _e_video_vblank_handler(NULL, 0, 0, 0, video);
+   return;
+}
+
+static void
+_e_video_cb_evas_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+   E_Video *video = data;
+
+   if (_e_video_geometry_cal_map(video))
+     _e_video_render(video, __FUNCTION__);
+}
+
+static void
+_e_video_cb_evas_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   E_Video *video = data;
+
+   if (_e_video_geometry_cal_map(video))
+     _e_video_render(video, __FUNCTION__);
+}
+
+static void
+_e_video_cb_evas_show(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   E_Video *video = data;
+
+   if (e_object_is_del(E_OBJECT(video->ec))) return;
+
+   if (video->need_force_render)
+     {
+        VIN("video forcely rendering..");
+        _e_video_render(video, __FUNCTION__);
+     }
+
+   /* if stand_alone is true, not show */
+   if ((video->ec->comp_data->sub.data && video->ec->comp_data->sub.data->stand_alone) ||
+       (video->ec->comp_data->sub.data && video->follow_topmost_visibility))
+     {
+#if 0 //mute off is managed by client. mute off in server made many issues.
+        if (!video->layer) return;
+
+        if (video->tdm_mute_id != -1)
+          {
+             tdm_value v = {.u32 = 0};
+             VIN("video surface show. mute off (ec:%p)", video->ec);
+             tdm_layer_set_property(video->layer, video->tdm_mute_id, v);
+          }
+#endif
+        return;
+     }
+
+   VIN("evas show (ec:%p)", video->ec);
+   if (video->current_fb)
+     _e_video_frame_buffer_show(video, video->current_fb);
+}
+
+static void
+_e_video_cb_evas_hide(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   E_Video *video = data;
+
+   if (e_object_is_del(E_OBJECT(video->ec))) return;
+
+   /* if stand_alone is true, not hide */
+   if (video->ec->comp_data->sub.data && video->ec->comp_data->sub.data->stand_alone)
+     {
+        if (!video->layer) return;
+
+        if (video->tdm_mute_id != -1)
+          {
+             tdm_value v = {.u32 = 1};
+             VIN("video surface hide. mute on (ec:%p)", video->ec);
+             tdm_layer_set_property(video->layer, video->tdm_mute_id, v);
+          }
+        return;
+     }
+
+   VIN("evas hide (ec:%p)", video->ec);
+   _e_video_frame_buffer_show(video, NULL);
+}
+
+static E_Video*
+_e_video_create(struct wl_resource *video_object, struct wl_resource *surface)
+{
+   E_Video *video;
+   E_Client *ec;
+
+   ec = wl_resource_get_user_data(surface);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ec, NULL);
+
+   video = calloc(1, sizeof *video);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(video, NULL);
+
+   video->video_object = video_object;
+   video->surface = surface;
+   video->output_align = -1;
+   video->pp_align = -1;
+   video->video_align = -1;
+   video->tdm_mute_id = -1;
+
+   VIN("create. ec(%p) wl_surface@%d", ec, wl_resource_get_id(video->surface));
+
+   video_list = eina_list_append(video_list, video);
+
+   _e_video_set(video, ec);
+
+   return video;
+}
+
+static void
+_e_video_set(E_Video *video, E_Client *ec)
+{
+   int ominw = -1, ominh = -1, omaxw = -1, omaxh = -1;
+   int i, count = 0;
+   tdm_display_capability disp_capabilities;
+   tdm_layer_capability lyr_capabilities = 0;
+   const tdm_prop *props;
+   tdm_layer *layer;
+   unsigned int pipe;
+   tdm_error ret;
+
+   if (!video || !ec)
+     return;
+
+   if (video->ec)
+     {
+        VWR("already has ec");
+        return;
+     }
+
+   EINA_SAFETY_ON_TRUE_RETURN(e_object_is_del(E_OBJECT(ec)));
+
+   video->ec = ec;
+   video->window = e_client_util_win_get(ec);
+
+   evas_object_event_callback_add(video->ec->frame, EVAS_CALLBACK_HIDE,
+                                  _e_video_cb_evas_hide, video);
+   evas_object_event_callback_add(video->ec->frame, EVAS_CALLBACK_SHOW,
+                                  _e_video_cb_evas_show, video);
+
+   if (e_config->eom_enable == EINA_TRUE)
+     {
+        video->external_video = e_eom_is_ec_external(ec);
+        if (video->external_video)
+          {
+             /* TODO: support screenmirror for external video */
+             video->drm_output = NULL;
+
+             video->output = e_eom_tdm_output_by_ec_get(ec);
+             EINA_SAFETY_ON_NULL_RETURN(video->output);
+          }
+        else
+          {
+             video->drm_output = _e_video_drm_output_find(video->ec);
+             EINA_SAFETY_ON_NULL_RETURN(video->drm_output);
+
+             /* TODO: find proper output */
+             video->output = _e_video_tdm_output_get(video->drm_output);
+             EINA_SAFETY_ON_NULL_RETURN(video->output);
+          }
+     }
+   else
+     {
+        video->drm_output = _e_video_drm_output_find(video->ec);
+        EINA_SAFETY_ON_NULL_RETURN(video->drm_output);
+
+        /* TODO: find proper output */
+        video->output = _e_video_tdm_output_get(video->drm_output);
+        EINA_SAFETY_ON_NULL_RETURN(video->output);
+     }
+
+   ret = tdm_output_get_pipe(video->output, &pipe);
+   if (ret == TDM_ERROR_NONE)
+     video->e_output = e_output_find_by_index(pipe);
+
+   layer = _e_video_tdm_video_layer_get(video->output);
+   tdm_layer_get_capabilities(layer, &lyr_capabilities);
+
+   if (lyr_capabilities & TDM_LAYER_CAPABILITY_VIDEO)
+     {
+        /* If tdm offers video layers, we will assign a tdm layer when showing */
+        VIN("video client");
+        ec->comp_data->video_client = 1;
+     }
+   else
+     {
+        /* If tdm doesn't offer video layers, we assign a tdm layer now. If failed,
+         * video will be displayed via the UI rendering path.
+         */
+        if (_e_video_set_layer(video, EINA_TRUE))
+          {
+             VIN("video client");
+             ec->comp_data->video_client = 1;
+          }
+        else
+          {
+             VIN("no video client");
+             ec->comp_data->video_client = 0;
+             ec->animatable = 0;
+          }
+     }
+
+   if (video_to_primary)
+     {
+        VIN("show video to primary layer");
+        ec->comp_data->video_client = 0;
+        ec->animatable = 0;
+     }
+
+   tdm_output_get_available_size(video->output, &ominw, &ominh, &omaxw, &omaxh, &video->output_align);
+
+   tdm_display_get_capabilities(e_comp->e_comp_screen->tdisplay, &disp_capabilities);
+
+   if (!(disp_capabilities & TDM_DISPLAY_CAPABILITY_PP))
+     {
+        video->video_align = video->output_align;
+        tizen_video_object_send_size(video->video_object,
+                                     ominw, ominh, omaxw, omaxh, video->output_align);
+     }
+   else
+     {
+        int minw = -1, minh = -1, maxw = -1, maxh = -1;
+        int pminw = -1, pminh = -1, pmaxw = -1, pmaxh = -1;
+
+        tdm_display_get_pp_available_size(e_comp->e_comp_screen->tdisplay, &pminw, &pminh, &pmaxw, &pmaxh, &video->pp_align);
+
+        minw = MAX(ominw, pminw);
+        minh = MAX(ominh, pminh);
+
+        if (omaxw != -1 && pmaxw == -1)
+          maxw = omaxw;
+        else if (omaxw == -1 && pmaxw != -1)
+          maxw = pmaxw;
+        else
+          maxw = MIN(omaxw, pmaxw);
+
+        if (omaxh != -1 && pmaxh == -1)
+          maxw = omaxh;
+        else if (omaxh == -1 && pmaxh != -1)
+          maxw = pmaxh;
+        else
+          maxh = MIN(omaxh, pmaxh);
+
+        if (video->output_align != -1 && video->pp_align == -1)
+          video->video_align = video->output_align;
+        else if (video->output_align == -1 && video->pp_align != -1)
+          video->video_align = video->pp_align;
+        else if (video->output_align == -1 && video->pp_align == -1)
+          video->video_align = video->pp_align;
+        else if (video->output_align > 0 && video->pp_align > 0)
+          video->video_align = lcm(video->output_align, video->pp_align);
+        else
+          ERR("invalid align: %d, %d", video->output_align, video->pp_align);
+
+        tizen_video_object_send_size(video->video_object,
+                                     minw, minh, maxw, maxh, video->video_align);
+        VIN("align width: output(%d) pp(%d) video(%d)",
+            video->output_align, video->pp_align, video->video_align);
+     }
+
+   tdm_layer_get_available_properties(layer, &props, &count);
+   for (i = 0; i < count; i++)
+     {
+        tdm_value value;
+        tdm_layer_get_property(layer, props[i].id, &value);
+        tizen_video_object_send_attribute(video->video_object, props[i].name, value.u32);
+
+        if (!strncmp(props[i].name, "mute", TDM_NAME_LEN))
+          video->tdm_mute_id = props[i].id;
+     }
+}
+
+static void
+_e_video_hide(E_Video *video)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+
+   if (video->current_fb || video->committed_list)
+     _e_video_frame_buffer_show(video, NULL);
+
+   if (video->current_fb)
+     {
+        e_comp_wl_video_buffer_set_use(video->current_fb, EINA_FALSE);
+        video->current_fb = NULL;
+     }
+
+   EINA_LIST_FREE(video->committed_list, vbuf)
+      e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+
+   EINA_LIST_FREE(video->waiting_list, vbuf)
+      e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+}
+
+static void
+_e_video_destroy(E_Video *video)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+   Eina_List *l = NULL, *ll = NULL;
+
+   if (!video)
+     return;
+
+   VIN("destroy");
+
+   if (video->cb_registered)
+     {
+        evas_object_event_callback_del_full(video->ec->frame, EVAS_CALLBACK_RESIZE,
+                                            _e_video_cb_evas_resize, video);
+        evas_object_event_callback_del_full(video->ec->frame, EVAS_CALLBACK_MOVE,
+                                            _e_video_cb_evas_move, video);
+     }
+
+   evas_object_event_callback_del_full(video->ec->frame, EVAS_CALLBACK_HIDE,
+                                       _e_video_cb_evas_hide, video);
+   evas_object_event_callback_del_full(video->ec->frame, EVAS_CALLBACK_SHOW,
+                                       _e_video_cb_evas_show, video);
+
+   wl_resource_set_destructor(video->video_object, NULL);
+
+   _e_video_hide(video);
+
+   /* others */
+   EINA_LIST_FOREACH_SAFE(video->input_buffer_list, l, ll, vbuf)
+     {
+        e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+        e_comp_wl_video_buffer_unref(vbuf);
+     }
+
+   EINA_LIST_FOREACH_SAFE(video->pp_buffer_list, l, ll, vbuf)
+     {
+        tdm_buffer_remove_release_handler(vbuf->tbm_surface,
+                                          _e_video_pp_buffer_cb_release, vbuf);
+        e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+        e_comp_wl_video_buffer_unref(vbuf);
+     }
+
+   if(video->tdm_prop_list)
+     {
+        Tdm_Prop_Value *tdm_prop;
+        EINA_LIST_FREE(video->tdm_prop_list, tdm_prop)
+          {
+             free(tdm_prop);
+          }
+     }
+   if(video->late_tdm_prop_list)
+     {
+        Tdm_Prop_Value *tdm_prop;
+        EINA_LIST_FREE(video->late_tdm_prop_list, tdm_prop)
+          {
+             free(tdm_prop);
+          }
+     }
+
+   if (video->input_buffer_list)
+     NEVER_GET_HERE();
+   if (video->pp_buffer_list)
+     NEVER_GET_HERE();
+   if (video->tdm_prop_list)
+     NEVER_GET_HERE();
+   if (video->late_tdm_prop_list)
+     NEVER_GET_HERE();
+
+   /* destroy converter second */
+   if (video->pp)
+     tdm_pp_destroy(video->pp);
+
+   if (video->layer)
+     {
+        VIN("unset layer: destroy");
+        _e_video_set_layer(video, EINA_FALSE);
+     }
+
+   video_list = eina_list_remove(video_list, video);
+
+   free(video);
+
+#if 0
+   if (e_comp_wl_video_buffer_list_length() > 0)
+     e_comp_wl_video_buffer_list_print(NULL);
+#endif
+}
+
+static Eina_Bool
+_e_video_check_if_pp_needed(E_Video *video)
+{
+   int i, count = 0;
+   const tbm_format *formats;
+   Eina_Bool found = EINA_FALSE;
+   tdm_layer_capability capabilities = 0;
+   tdm_layer *layer = _e_video_tdm_video_layer_get(video->output);
+
+   tdm_layer_get_capabilities(layer, &capabilities);
+
+   /* don't need pp if a layer has TDM_LAYER_CAPABILITY_VIDEO capability*/
+   if (capabilities & TDM_LAYER_CAPABILITY_VIDEO)
+     return EINA_FALSE;
+
+   /* check formats */
+   tdm_layer_get_available_formats(layer, &formats, &count);
+   for (i = 0; i < count; i++)
+     if (formats[i] == video->tbmfmt)
+       {
+          found = EINA_TRUE;
+          break;
+       }
+
+   if (!found)
+     {
+        video->pp_tbmfmt = TBM_FORMAT_ARGB8888;
+        return EINA_TRUE;
+     }
+
+   /* check size */
+   if (capabilities & TDM_LAYER_CAPABILITY_SCANOUT)
+     goto need_pp;
+
+   if (video->geo.input_r.w != video->geo.output_r.w || video->geo.input_r.h != video->geo.output_r.h)
+     if (!(capabilities & TDM_LAYER_CAPABILITY_SCALE))
+       goto need_pp;
+
+   /* check rotate */
+   if (video->geo.transform || e_comp->e_comp_screen->rotation > 0)
+     if (!(capabilities & TDM_LAYER_CAPABILITY_TRANSFORM))
+       goto need_pp;
+
+   return EINA_FALSE;
+
+need_pp:
+   video->pp_tbmfmt = video->tbmfmt;
+   return EINA_TRUE;
+}
+
+static void
+_e_video_pp_buffer_cb_release(tbm_surface_h surface, void *user_data)
+{
+   E_Comp_Wl_Video_Buf *vbuf = (E_Comp_Wl_Video_Buf*)user_data;
+
+   vbuf->in_use = EINA_FALSE;
+}
+
+static void
+_e_video_pp_cb_done(tdm_pp *pp, tbm_surface_h sb, tbm_surface_h db, void *user_data)
+{
+   E_Video *video = (E_Video*)user_data;
+   E_Comp_Wl_Video_Buf *input_buffer, *pp_buffer;
+
+   input_buffer = _e_video_vbuf_find(video->input_buffer_list, sb);
+   if (input_buffer)
+     e_comp_wl_video_buffer_unref(input_buffer);
+
+   pp_buffer = _e_video_vbuf_find(video->pp_buffer_list, db);
+   if (pp_buffer)
+     {
+        e_comp_wl_video_buffer_set_use(pp_buffer, EINA_FALSE);
+        if (!_e_video_is_visible(video)) return;
+
+        _e_video_buffer_show(video, pp_buffer, 0);
+     }
+   else
+     {
+        VER("There is no pp_buffer");
+        // there is no way to set in_use flag.
+        // This will cause issue when server get available pp_buffer.
+     }
+}
+
+static void
+_e_video_render(E_Video *video, const char *func)
+{
+   E_Comp_Wl_Buffer *comp_buffer;
+   E_Comp_Wl_Video_Buf *pp_buffer = NULL;
+   E_Comp_Wl_Video_Buf *input_buffer = NULL;
+   E_Client *topmost;
+
+   EINA_SAFETY_ON_NULL_RETURN(video->ec);
+
+   /* buffer can be NULL when camera/video's mode changed. Do nothing and
+    * keep previous frame in this case.
+    */
+   if (!video->ec->pixmap)
+     return;
+
+   if (!_e_video_is_visible(video))
+     {
+        _e_video_hide(video);
+        return;
+     }
+
+   comp_buffer = e_pixmap_resource_get(video->ec->pixmap);
+   if (!comp_buffer) return;
+
+   _e_video_format_info_get(video);
+
+   /* not interested with other buffer type */
+   if (!wayland_tbm_server_get_surface(NULL, comp_buffer->resource))
+     return;
+
+   topmost = find_topmost_parent_get(video->ec);
+   EINA_SAFETY_ON_NULL_RETURN(topmost);
+
+   if(e_comp_wl_viewport_is_changed(topmost))
+     {
+        VIN("need update viewport: apply topmost");
+        e_comp_wl_viewport_apply(topmost);
+     }
+
+   if (!_e_video_geometry_cal(video))
+     {
+        if(!video->need_force_render && !_e_video_parent_is_viewable(video))
+          {
+             VIN("need force render");
+             video->need_force_render = EINA_TRUE;
+          }
+        return;
+     }
+
+   if (!memcmp(&video->old_geo, &video->geo, sizeof video->geo) &&
+       video->old_comp_buffer == comp_buffer)
+     return;
+
+   video->need_force_render = EINA_FALSE;
+
+   DBG("====================================== (%s)", func);
+   VDB("old: "GEO_FMT" buf(%p)", GEO_ARG(&video->old_geo), video->old_comp_buffer);
+   VDB("new: "GEO_FMT" buf(%p) %c%c%c%c", GEO_ARG(&video->geo), comp_buffer, FOURCC_STR(video->tbmfmt));
+
+   _e_video_input_buffer_valid(video, comp_buffer);
+
+   if (!_e_video_check_if_pp_needed(video))
+     {
+        /* 1. non converting case */
+        input_buffer = _e_video_input_buffer_get(video, comp_buffer, EINA_TRUE);
+        EINA_SAFETY_ON_NULL_GOTO(input_buffer, render_fail);
+
+        _e_video_buffer_show(video, input_buffer, video->geo.tdm_transform);
+
+        video->old_geo = video->geo;
+        video->old_comp_buffer = comp_buffer;
+
+        goto done;
+     }
+
+   /* 2. converting case */
+   if (!video->pp)
+     {
+        video->pp = tdm_display_create_pp(e_comp->e_comp_screen->tdisplay, NULL);
+        EINA_SAFETY_ON_NULL_GOTO(video->pp, render_fail);
+
+        tdm_display_get_pp_available_size(e_comp->e_comp_screen->tdisplay, &video->pp_minw, &video->pp_minh,
+                                          &video->pp_maxw, &video->pp_maxh, &video->pp_align);
+     }
+
+   if ((video->pp_minw > 0 && (video->geo.input_r.w < video->pp_minw || video->geo.tdm_output_r.w < video->pp_minw)) ||
+       (video->pp_minh > 0 && (video->geo.input_r.h < video->pp_minh || video->geo.tdm_output_r.h < video->pp_minh)) ||
+       (video->pp_maxw > 0 && (video->geo.input_r.w > video->pp_maxw || video->geo.tdm_output_r.w > video->pp_maxw)) ||
+       (video->pp_maxh > 0 && (video->geo.input_r.h > video->pp_maxh || video->geo.tdm_output_r.h > video->pp_maxh)))
+     {
+        INF("size(%dx%d, %dx%d) is out of PP range",
+            video->geo.input_r.w, video->geo.input_r.h, video->geo.tdm_output_r.w, video->geo.tdm_output_r.h);
+        goto done;
+     }
+
+   input_buffer = _e_video_input_buffer_get(video, comp_buffer, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_GOTO(input_buffer, render_fail);
+
+   pp_buffer = _e_video_pp_buffer_get(video, video->geo.tdm_output_r.w, video->geo.tdm_output_r.h);
+   EINA_SAFETY_ON_NULL_GOTO(pp_buffer, render_fail);
+
+   if (memcmp(&video->old_geo, &video->geo, sizeof video->geo))
+     {
+        tdm_info_pp info;
+
+        CLEAR(info);
+        info.src_config.size.h = input_buffer->width_from_pitch;
+        info.src_config.size.v = input_buffer->height_from_size;
+        info.src_config.pos.x = video->geo.input_r.x;
+        info.src_config.pos.y = video->geo.input_r.y;
+        info.src_config.pos.w = video->geo.input_r.w;
+        info.src_config.pos.h = video->geo.input_r.h;
+        info.src_config.format = video->tbmfmt;
+        info.dst_config.size.h = pp_buffer->width_from_pitch;
+        info.dst_config.size.v = pp_buffer->height_from_size;
+        info.dst_config.pos.w = video->geo.tdm_output_r.w;
+        info.dst_config.pos.h = video->geo.tdm_output_r.h;
+        info.dst_config.format = video->pp_tbmfmt;
+        info.transform = video->geo.tdm_transform;
+
+        if (tdm_pp_set_info(video->pp, &info))
+          goto render_fail;
+
+        if (tdm_pp_set_done_handler(video->pp, _e_video_pp_cb_done, video))
+          goto render_fail;
+
+        CLEAR(video->pp_r);
+        video->pp_r.w = info.dst_config.pos.w;
+        video->pp_r.h = info.dst_config.pos.h;
+     }
+
+   pp_buffer->content_r = video->pp_r;
+
+   if (tdm_pp_attach(video->pp, input_buffer->tbm_surface, pp_buffer->tbm_surface))
+     goto render_fail;
+
+   e_comp_wl_video_buffer_set_use(pp_buffer, EINA_TRUE);
+
+   e_comp_wl_buffer_reference(&input_buffer->buffer_ref, comp_buffer);
+
+   if (tdm_pp_commit(video->pp))
+     {
+        e_comp_wl_video_buffer_set_use(pp_buffer, EINA_FALSE);
+        goto render_fail;
+     }
+
+   video->old_geo = video->geo;
+   video->old_comp_buffer = comp_buffer;
+
+   goto done;
+
+render_fail:
+   if (input_buffer)
+     e_comp_wl_video_buffer_unref(input_buffer);
+
+done:
+   if (!video->cb_registered)
+     {
+        evas_object_event_callback_add(video->ec->frame, EVAS_CALLBACK_RESIZE,
+                                       _e_video_cb_evas_resize, video);
+        evas_object_event_callback_add(video->ec->frame, EVAS_CALLBACK_MOVE,
+                                       _e_video_cb_evas_move, video);
+        video->cb_registered = EINA_TRUE;
+     }
+   DBG("======================================.");
+}
+
+static Eina_Bool
+_e_video_cb_ec_buffer_change(void *data, int type, void *event)
+{
+   E_Client *ec;
+   E_Event_Client *ev = event;
+   E_Video *video;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ev, ECORE_CALLBACK_PASS_ON);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ev->ec, ECORE_CALLBACK_PASS_ON);
+
+   ec = ev->ec;
+   if (e_object_is_del(E_OBJECT(ec)))
+     return ECORE_CALLBACK_PASS_ON;
+
+   /* not interested with non video_surface,  */
+   video = find_video_with_surface(ec->comp_data->surface);
+   if (!video) return ECORE_CALLBACK_PASS_ON;
+
+   if (!video->ec->comp_data->video_client)
+     return ECORE_CALLBACK_PASS_ON;
+
+   if (e_config->eom_enable == EINA_TRUE)
+     {
+        /* skip external client buffer if its top parent is not current for eom anymore */
+        if (video->external_video && !e_eom_is_ec_external(ec))
+          {
+             VWR("skip external buffer");
+             return ECORE_CALLBACK_PASS_ON;
+          }
+     }
+
+   _e_video_render(video, __FUNCTION__);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_video_cb_ec_remove(void *data, int type, void *event)
+{
+   E_Event_Client *ev = event;
+   E_Client *ec;
+   E_Video *video;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ev, ECORE_CALLBACK_PASS_ON);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ev->ec, ECORE_CALLBACK_PASS_ON);
+
+   ec = ev->ec;
+   if (!ec->comp_data) return ECORE_CALLBACK_PASS_ON;
+
+   video = find_video_with_surface(ec->comp_data->surface);
+   if (!video) return ECORE_CALLBACK_PASS_ON;
+
+   _e_video_destroy(video);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_video_cb_ec_client_show(void *data, int type, void *event)
+{
+   E_Event_Client *ev = event;
+   E_Client *ec;
+   E_Client *video_ec = NULL;
+   E_Video *video = NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ev, ECORE_CALLBACK_PASS_ON);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ev->ec, ECORE_CALLBACK_PASS_ON);
+
+   ec = ev->ec;
+   if (!ec->comp_data) return ECORE_CALLBACK_PASS_ON;
+
+   video_ec = find_video_child_get(ec);
+   if (!video_ec) return ECORE_CALLBACK_PASS_ON;
+
+   video = find_video_with_surface(video_ec->comp_data->surface);
+   if (!video) return ECORE_CALLBACK_PASS_ON;
+
+   VIN("client(0x%08"PRIxPTR") show: find video child(0x%08"PRIxPTR")", (Ecore_Window)e_client_util_win_get(ec), (Ecore_Window)e_client_util_win_get(video_ec));
+   if(video->old_comp_buffer)
+     {
+        VIN("video already rendering..");
+        return ECORE_CALLBACK_PASS_ON;
+     }
+
+   if(ec == find_topmost_parent_get(video->ec))
+     {
+        VIN("video need rendering..");
+        e_comp_wl_viewport_apply(ec);
+        _e_video_render(video, __FUNCTION__);
+     }
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_video_cb_ec_visibility_change(void *data, int type, void *event)
+{
+   E_Event_Remote_Surface_Provider *ev = event;
+   E_Client *ec = ev->ec;
+   E_Video *video;
+   Eina_List *l;
+
+   EINA_LIST_FOREACH(video_list, l, video)
+     {
+        E_Client *offscreen_parent = find_offscreen_parent_get(video->ec);
+        if (!offscreen_parent) continue;
+        if (offscreen_parent != ec) continue;
+        switch (ec->visibility.obscured)
+          {
+           case E_VISIBILITY_FULLY_OBSCURED:
+              _e_video_cb_evas_hide(video, NULL, NULL, NULL);
+              break;
+           case E_VISIBILITY_UNOBSCURED:
+              _e_video_cb_evas_show(video, NULL, NULL, NULL);
+              break;
+           default:
+              VER("Not implemented");
+              return ECORE_CALLBACK_PASS_ON;
+          }
+     }
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_video_cb_topmost_ec_visibility_change(void *data, int type, void *event)
+{
+   E_Event_Client *ev = event;
+   E_Client *ec = ev->ec;
+   E_Video *video;
+   Eina_List *l = NULL;
+
+   EINA_LIST_FOREACH(video_list, l, video)
+     {
+        E_Client *topmost = find_topmost_parent_get(video->ec);
+        if (!topmost) continue;
+        if (topmost == video->ec) continue;
+        if (topmost != ec) continue;
+        if (video->follow_topmost_visibility)
+          {
+             switch (ec->visibility.obscured)
+               {
+                case E_VISIBILITY_FULLY_OBSCURED:
+                   VIN("follow_topmost_visibility: fully_obscured");
+                   _e_video_cb_evas_hide(video, NULL, NULL, NULL);
+                   break;
+                case E_VISIBILITY_UNOBSCURED:
+                   VIN("follow_topmost_visibility: UNOBSCURED");
+                   _e_video_cb_evas_show(video, NULL, NULL, NULL);
+                   break;
+                default:
+                   return ECORE_CALLBACK_PASS_ON;
+               }
+          }
+     }
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static void
+_e_comp_wl_video_object_destroy(struct wl_resource *resource)
+{
+   E_Video *video = wl_resource_get_user_data(resource);
+   EINA_SAFETY_ON_NULL_RETURN(video);
+
+   VDT("Video from Client(%s):PID(%d) is being destroyed, details are: "
+       "Buffer(%p), Video_Format(%c%c%c%c), "
+       "Buffer_Size(%dx%d), Src Rect(%d,%d, %dx%d), Dest Rect(%d,%d, %dx%d),"
+       " Transformed(%d)",
+       e_client_util_name_get(video->ec) ?: "No Name" , video->ec->netwm.pid,
+       video->current_fb, FOURCC_STR(video->tbmfmt),
+       video->geo.input_w, video->geo.input_h, video->geo.input_r.x ,
+       video->geo.input_r.y, video->geo.input_r.w, video->geo.input_r.h,
+       video->geo.output_r.x ,video->geo.output_r.y, video->geo.output_r.w,
+       video->geo.output_r.h, video->geo.transform);
+
+   _e_video_destroy(video);
+}
+
+static void
+_e_comp_wl_video_object_cb_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+   wl_resource_destroy(resource);
+}
+
+static void
+_e_comp_wl_video_object_cb_set_attribute(struct wl_client *client,
+                                         struct wl_resource *resource,
+                                         const char *name,
+                                         int32_t value)
+{
+   E_Video *video;
+   int i, count = 0;
+   const tdm_prop *props;
+   tdm_layer *layer;
+   Eina_Bool available = EINA_FALSE;
+
+   video = wl_resource_get_user_data(resource);
+   EINA_SAFETY_ON_NULL_RETURN(video);
+
+   if(video->ec)
+     VDT("Client(%s):PID(%d) RscID(%d) Attribute:%s, Value:%d",
+         e_client_util_name_get(video->ec) ?: "No Name",
+         video->ec->netwm.pid, wl_resource_get_id(video->surface),
+         name,value);
+
+   // check available property & count
+   layer = _e_video_tdm_video_layer_get(video->output);
+   tdm_layer_get_available_properties(layer, &props, &count);
+
+   for (i = 0; i < count; i++)
+     {
+        if (!strncmp(name, props[i].name, TDM_NAME_LEN))
+          {
+             available = EINA_TRUE;
+             VDB("check property(%s) value(%d)", name, value);
+             break;
+          }
+     }
+
+   if(!available)
+     {
+        VIN("no available property");
+        return;
+     }
+
+   if (!video->layer && video->allowed_attribute)
+     {
+        VIN("set layer: set_attribute");
+        if (!_e_video_set_layer(video, EINA_TRUE))
+          {
+             VER("set layer failed");
+             return;
+          }
+     }
+
+   if (!_e_video_is_visible(video) || !video->layer)
+     {
+        /* if mute off, need to do it after buffer commit */
+        if (!strncmp(props[i].name, "mute", TDM_NAME_LEN) && value == 0)
+          {
+             Tdm_Prop_Value *prop = NULL;
+             const Eina_List *l = NULL;
+
+             EINA_LIST_FOREACH(video->late_tdm_prop_list, l, prop)
+               {
+                  if (!strncmp(name, prop->name, TDM_NAME_LEN))
+                    {
+                       prop->value.u32 = value;
+                       VDB("update property(%s) value(%d)", prop->name, value);
+                       return;
+                    }
+               }
+
+             prop = calloc(1, sizeof(Tdm_Prop_Value));
+             if(!prop) return;
+
+             prop->value.u32 = value;
+             prop->id = props[i].id;
+             memcpy(prop->name, props[i].name, sizeof(props[i].name));
+             VIN("Add property(%s) value(%d)", prop->name, value);
+             video->late_tdm_prop_list = eina_list_append(video->late_tdm_prop_list, prop);
+             return;
+          }
+     }
+
+   // check set video layer
+   if(!video->layer)
+     {
+        VIN("no layer: save property value");
+
+        Tdm_Prop_Value *prop = NULL;
+        const Eina_List *l = NULL;
+
+        EINA_LIST_FOREACH(video->tdm_prop_list, l, prop)
+          {
+             if (!strncmp(name, prop->name, TDM_NAME_LEN))
+               {
+                  VDB("find prop data(%s) update value(%d -> %d)", prop->name, (unsigned int)prop->value.u32, (unsigned int)value);
+                  prop->value.u32 = value;
+                  return;
+               }
+          }
+        EINA_LIST_FOREACH(video->late_tdm_prop_list, l, prop)
+          {
+             if (!strncmp(name, prop->name, TDM_NAME_LEN))
+               {
+                  VDB("find prop data(%s) update value(%d -> %d)", prop->name, (unsigned int)prop->value.u32, (unsigned int)value);
+                  prop->value.u32 = value;
+                  return;
+               }
+          }
+
+        prop = calloc(1, sizeof(Tdm_Prop_Value));
+        if(!prop) return;
+        prop->value.u32 = value;
+        prop->id = props[i].id;
+        memcpy(prop->name, props[i].name, sizeof(props[i].name));
+        VIN("Add property(%s) value(%d)", prop->name, value);
+        video->tdm_prop_list = eina_list_append(video->tdm_prop_list, prop);
+     }
+   // if set layer call property
+   else
+     {
+        tdm_value v = {.u32 = value};
+        VIN("set layer: call property(%s), value(%d)", name, value);
+        tdm_layer_set_property(video->layer, props[i].id, v);
+     }
+}
+
+static void
+_e_comp_wl_video_object_cb_follow_topmost_visibility(struct wl_client *client,
+                                                     struct wl_resource *resource)
+{
+   E_Video *video;
+
+   video = wl_resource_get_user_data(resource);
+   EINA_SAFETY_ON_NULL_RETURN(video);
+
+   if(!video->ec || video->follow_topmost_visibility)
+     return;
+
+   VIN("set follow_topmost_visibility");
+
+   video->follow_topmost_visibility= EINA_TRUE;
+
+}
+
+static void
+_e_comp_wl_video_object_cb_unfollow_topmost_visibility(struct wl_client *client,
+                                                       struct wl_resource *resource)
+{
+   E_Video *video;
+
+   video = wl_resource_get_user_data(resource);
+   EINA_SAFETY_ON_NULL_RETURN(video);
+
+   if(!video->ec || !video->follow_topmost_visibility)
+     return;
+
+   VIN("set unfollow_topmost_visibility");
+
+   video->follow_topmost_visibility= EINA_FALSE;
+
+}
+
+static void
+_e_comp_wl_video_object_cb_allowed_attribute(struct wl_client *client,
+                                             struct wl_resource *resource)
+{
+   E_Video *video;
+
+   video = wl_resource_get_user_data(resource);
+   EINA_SAFETY_ON_NULL_RETURN(video);
+
+   if(!video->ec || video->allowed_attribute)
+     return;
+
+   VIN("set allowed_attribute");
+
+   video->allowed_attribute= EINA_TRUE;
+
+}
+
+static void
+_e_comp_wl_video_object_cb_disallowed_attribute(struct wl_client *client,
+                                                struct wl_resource *resource)
+{
+   E_Video *video;
+
+   video = wl_resource_get_user_data(resource);
+   EINA_SAFETY_ON_NULL_RETURN(video);
+
+   if(!video->ec || !video->allowed_attribute)
+     return;
+
+   VIN("set disallowed_attribute");
+
+   video->allowed_attribute= EINA_FALSE;
+
+}
+
+static const struct tizen_video_object_interface _e_comp_wl_video_object_interface =
+{
+   _e_comp_wl_video_object_cb_destroy,
+   _e_comp_wl_video_object_cb_set_attribute,
+   _e_comp_wl_video_object_cb_follow_topmost_visibility,
+   _e_comp_wl_video_object_cb_unfollow_topmost_visibility,
+   _e_comp_wl_video_object_cb_allowed_attribute,
+   _e_comp_wl_video_object_cb_disallowed_attribute,
+};
+
+static void
+_e_comp_wl_video_cb_get_object(struct wl_client *client,
+                               struct wl_resource *resource,
+                               uint32_t id,
+                               struct wl_resource *surface)
+{
+   int version = wl_resource_get_version(resource);
+   E_Video *video = wl_resource_get_user_data(resource);
+   struct wl_resource *res;
+
+   if (video)
+     {
+        wl_resource_post_error(resource,
+                               TIZEN_VIDEO_ERROR_OBJECT_EXISTS,
+                               "a video object for that surface already exists");
+        return;
+     }
+
+   res = wl_resource_create(client, &tizen_video_object_interface, version, id);
+   if (res == NULL)
+     {
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   if (!(video = _e_video_create(res, surface)))
+     {
+        wl_resource_destroy(res);
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   wl_resource_set_implementation(res, &_e_comp_wl_video_object_interface,
+                                  video, _e_comp_wl_video_object_destroy);
+}
+
+static void
+_e_comp_wl_video_cb_get_viewport(struct wl_client *client,
+                                 struct wl_resource *resource,
+                                 uint32_t id,
+                                 struct wl_resource *surface)
+{
+   E_Client *ec;
+
+   if (!(ec = wl_resource_get_user_data(surface))) return;
+   if (!ec->comp_data) return;
+
+   if (ec->comp_data && ec->comp_data->scaler.viewport)
+     {
+        wl_resource_post_error(resource,
+                               TIZEN_VIDEO_ERROR_VIEWPORT_EXISTS,
+                               "a viewport for that subsurface already exists");
+        return;
+     }
+
+   if (!e_comp_wl_viewport_create(resource, id, surface))
+     {
+        ERR("Failed to create viewport for wl_surface@%d",
+            wl_resource_get_id(surface));
+        wl_client_post_no_memory(client);
+        return;
+     }
+}
+
+static const struct tizen_video_interface _e_comp_wl_video_interface =
+{
+   _e_comp_wl_video_cb_get_object,
+   _e_comp_wl_video_cb_get_viewport,
+};
+
+static void
+_e_comp_wl_video_cb_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+   struct wl_resource *res;
+   const tbm_format *formats = NULL;
+   int i, count = 0;
+
+   if (!(res = wl_resource_create(client, &tizen_video_interface, MIN(version, 1), id)))
+     {
+        ERR("Could not create tizen_video_interface resource: %m");
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   wl_resource_set_implementation(res, &_e_comp_wl_video_interface, NULL, NULL);
+
+   /* 1st, use pp information. */
+   if (e_comp_screen_pp_support())
+     {
+        tdm_display_get_pp_available_formats(e_comp->e_comp_screen->tdisplay, &formats, &count);
+        for (i = 0; i < count; i++)
+          tizen_video_send_format(res, formats[i]);
+     }
+   else
+     {
+        tdm_output *output = _e_video_tdm_output_get(NULL);
+        tdm_layer *layer;
+
+        EINA_SAFETY_ON_NULL_RETURN(output);
+
+        layer = _e_video_tdm_video_layer_get(output);
+        EINA_SAFETY_ON_NULL_RETURN(layer);
+
+        tdm_layer_get_available_formats(layer, &formats, &count);
+        for (i = 0; i < count; i++)
+          tizen_video_send_format(res, formats[i]);
+     }
+}
+
+static Eina_List *video_hdlrs;
+
+static void
+_e_comp_wl_vbuf_print(void *data, const char *log_path)
+{
+   e_comp_wl_video_buffer_list_print(log_path);
+}
+
+static void
+_e_comp_wl_video_to_primary(void *data, const char *log_path)
+{
+   video_to_primary = !video_to_primary;
+}
+
+static void
+_e_comp_wl_video_punch(void *data, const char *log_path)
+{
+   video_punch = !video_punch;
+}
+
+EINTERN int
+e_comp_wl_video_init(void)
+{
+   e_comp->wl_comp_data->available_hw_accel.underlay = EINA_TRUE;
+   DBG("enable HW underlay");
+
+   e_comp->wl_comp_data->available_hw_accel.scaler = EINA_TRUE;
+   DBG("enable HW scaler");
+
+   if (!e_comp_wl) return 0;
+   if (!e_comp_wl->wl.disp) return 0;
+   if (e_comp->wl_comp_data->video.global) return 1;
+
+   e_info_server_hook_set("vbuf", _e_comp_wl_vbuf_print, NULL);
+   e_info_server_hook_set("video-to-primary", _e_comp_wl_video_to_primary, NULL);
+   e_info_server_hook_set("video-punch", _e_comp_wl_video_punch, NULL);
+
+   _video_detail_log_dom = eina_log_domain_register("e-comp-wl-video", EINA_COLOR_BLUE);
+   if (_video_detail_log_dom < 0)
+     {
+        ERR("Failed eina_log_domain_register()..!\n");
+        return 0;
+     }
+
+   e_comp->wl_comp_data->video.global =
+      wl_global_create(e_comp_wl->wl.disp, &tizen_video_interface, 1, NULL, _e_comp_wl_video_cb_bind);
+
+   /* try to add tizen_video to wayland globals */
+   if (!e_comp->wl_comp_data->video.global)
+     {
+        ERR("Could not add tizen_video to wayland globals");
+        return 0;
+     }
+
+   E_LIST_HANDLER_APPEND(video_hdlrs, E_EVENT_CLIENT_BUFFER_CHANGE,
+                         _e_video_cb_ec_buffer_change, NULL);
+   E_LIST_HANDLER_APPEND(video_hdlrs, E_EVENT_CLIENT_REMOVE,
+                         _e_video_cb_ec_remove, NULL);
+   E_LIST_HANDLER_APPEND(video_hdlrs, E_EVENT_CLIENT_SHOW,
+                         _e_video_cb_ec_client_show, NULL);
+   E_LIST_HANDLER_APPEND(video_hdlrs, E_EVENT_REMOTE_SURFACE_PROVIDER_VISIBILITY_CHANGE,
+                         _e_video_cb_ec_visibility_change, NULL);
+   E_LIST_HANDLER_APPEND(video_hdlrs, E_EVENT_CLIENT_VISIBILITY_CHANGE,
+                         _e_video_cb_topmost_ec_visibility_change, NULL);
+
+   return 1;
+}
+
+EINTERN void
+e_comp_wl_video_shutdown(void)
+{
+   e_comp->wl_comp_data->available_hw_accel.underlay = EINA_FALSE;
+   e_comp->wl_comp_data->available_hw_accel.scaler = EINA_FALSE;
+
+   E_FREE_LIST(video_hdlrs, ecore_event_handler_del);
+
+   e_info_server_hook_set("vbuf", NULL, NULL);
+   e_info_server_hook_set("video-dst-change", NULL, NULL);
+   e_info_server_hook_set("video-to-primary", NULL, NULL);
+   e_info_server_hook_set("video-punch", NULL, NULL);
+
+   eina_log_domain_unregister(_video_detail_log_dom);
+   _video_detail_log_dom = -1;
+}
+
+EINTERN tdm_layer *
+e_comp_wl_video_layer_get(tdm_output *output)
+{
+   E_Video *video;
+   Eina_List *l;
+
+   if (!output)
+     return NULL;
+
+   EINA_LIST_FOREACH(video_list, l, video)
+     {
+        if (video->output == output)
+          return video->layer;
+     }
+
+   return NULL;
+}
+
+static tdm_output*
+_e_video_tdm_output_get(Ecore_Drm_Output *output)
+{
+   Ecore_Drm_Device *dev;
+   Ecore_Drm_Output *o;
+   Eina_List *devs;
+   Eina_List *l, *ll;
+   int pipe = 0;
+
+   if (!output)
+     {
+        int i, count = 0;
+        tdm_display_get_output_count(e_comp->e_comp_screen->tdisplay, &count);
+        for (i = 0; i < count; i++)
+          {
+             tdm_output *toutput = tdm_display_get_output(e_comp->e_comp_screen->tdisplay, i, NULL);
+             tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
+
+             if (!toutput)
+               continue;
+
+             tdm_output_get_conn_status(toutput, &status);
+             if (status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+               {
+                  tdm_output_type type;
+                  tdm_output_get_output_type(toutput, &type);
+                  INF("found tdm output: type(%d)", type);
+                  return toutput;
+               }
+          }
+
+        ERR("not found tdm output");
+
+        return NULL;
+     }
+
+   devs = eina_list_clone(ecore_drm_devices_get());
+   EINA_LIST_FOREACH(devs, l, dev)
+     {
+        pipe = 0;
+        EINA_LIST_FOREACH(dev->outputs, ll, o)
+          {
+             if (o == output)
+               goto found;
+             pipe++;
+          }
+     }
+found:
+   eina_list_free(devs);
+
+   return tdm_display_get_output(e_comp->e_comp_screen->tdisplay, pipe, NULL);
+}
+
+static tdm_layer*
+_e_video_tdm_video_layer_get(tdm_output *output)
+{
+   int i, count = 0;
+#ifdef CHECKING_PRIMARY_ZPOS
+   int primary_idx = 0, primary_zpos = 0;
+   tdm_layer *primary_layer;
+#endif
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
+
+   tdm_output_get_layer_count(output, &count);
+   for (i = 0; i < count; i++)
+     {
+        tdm_layer *layer = tdm_output_get_layer(output, i, NULL);
+        tdm_layer_capability capabilities = 0;
+        EINA_SAFETY_ON_NULL_RETURN_VAL(layer, NULL);
+
+        tdm_layer_get_capabilities(layer, &capabilities);
+        if (capabilities & TDM_LAYER_CAPABILITY_VIDEO)
+          return layer;
+     }
+
+#ifdef CHECKING_PRIMARY_ZPOS
+   tdm_output_get_primary_index(output, &primary_idx);
+   primary_layer = tdm_output_get_layer(output, primary_idx, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(primary_layer, NULL);
+   tdm_layer_get_zpos(primary_layer, &primary_zpos);
+#endif
+
+   for (i = 0; i < count; i++)
+     {
+        tdm_layer *layer = tdm_output_get_layer(output, i, NULL);
+        tdm_layer_capability capabilities = 0;
+        EINA_SAFETY_ON_NULL_RETURN_VAL(layer, NULL);
+
+        tdm_layer_get_capabilities(layer, &capabilities);
+        if (capabilities & TDM_LAYER_CAPABILITY_OVERLAY)
+          {
+#ifdef CHECKING_PRIMARY_ZPOS
+             int zpos = 0;
+             tdm_layer_get_zpos(layer, &zpos);
+             if (zpos >= primary_zpos) continue;
+#endif
+             return layer;
+          }
+     }
+
+   return NULL;
+}
+
+static tdm_layer*
+_e_video_tdm_avaiable_video_layer_get(tdm_output *output)
+{
+   Eina_Bool has_video_layer = EINA_FALSE;
+   int i, count = 0;
+#ifdef CHECKING_PRIMARY_ZPOS
+   int primary_idx = 0, primary_zpos = 0;
+   tdm_layer *primary_layer;
+#endif
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
+
+   /* check video layers first */
+   tdm_output_get_layer_count(output, &count);
+   for (i = 0; i < count; i++)
+     {
+        tdm_layer *layer = tdm_output_get_layer(output, i, NULL);
+        tdm_layer_capability capabilities = 0;
+        EINA_SAFETY_ON_NULL_RETURN_VAL(layer, NULL);
+
+        tdm_layer_get_capabilities(layer, &capabilities);
+        if (capabilities & TDM_LAYER_CAPABILITY_VIDEO)
+          {
+             has_video_layer = EINA_TRUE;
+             if (!_e_video_tdm_get_layer_usable(layer)) continue;
+             return layer;
+          }
+     }
+
+   /* if a output has video layers, it means that there is no available video layer for video */
+   if (has_video_layer)
+     return NULL;
+
+   /* check graphic layers second */
+#ifdef CHECKING_PRIMARY_ZPOS
+   tdm_output_get_primary_index(output, &primary_idx);
+   primary_layer = tdm_output_get_layer(output, primary_idx, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(primary_layer, NULL);
+   tdm_layer_get_zpos(primary_layer, &primary_zpos);
+#endif
+
+   for (i = 0; i < count; i++)
+     {
+        tdm_layer *layer = tdm_output_get_layer(output, i, NULL);
+        tdm_layer_capability capabilities = 0;
+        EINA_SAFETY_ON_NULL_RETURN_VAL(layer, NULL);
+
+        tdm_layer_get_capabilities(layer, &capabilities);
+        if (capabilities & TDM_LAYER_CAPABILITY_OVERLAY)
+          {
+#ifdef CHECKING_PRIMARY_ZPOS
+             int zpos = 0;
+             tdm_layer_get_zpos(layer, &zpos);
+             if (zpos >= primary_zpos) continue;
+#endif
+             if (!_e_video_tdm_get_layer_usable(layer)) continue;
+             return layer;
+          }
+     }
+
+   return NULL;
+}
+
+static void
+_e_video_tdm_set_layer_usable(tdm_layer *layer, Eina_Bool usable)
+{
+   if (usable)
+     video_layers = eina_list_remove(video_layers, layer);
+   else
+     {
+        tdm_layer *used_layer;
+        Eina_List *l = NULL;
+        EINA_LIST_FOREACH(video_layers, l, used_layer)
+           if (used_layer == layer) return;
+        video_layers = eina_list_append(video_layers, layer);
+     }
+}
+
+static Eina_Bool
+_e_video_tdm_get_layer_usable(tdm_layer *layer)
+{
+   tdm_layer *used_layer;
+   Eina_List *l = NULL;
+   EINA_LIST_FOREACH(video_layers, l, used_layer)
+      if (used_layer == layer)
+        return EINA_FALSE;
+   return EINA_TRUE;
+}
diff --git a/src/bin/e_comp_wl_video.h b/src/bin/e_comp_wl_video.h
new file mode 100644 (file)
index 0000000..8b7703e
--- /dev/null
@@ -0,0 +1,22 @@
+#ifdef E_TYPEDEFS
+
+#else
+#ifndef E_COMP_WL_VIDEO_H
+#define E_COMP_WL_VIDEO_H
+
+#define E_COMP_WL
+
+typedef struct _E_Video E_Video;
+
+EINTERN int e_comp_wl_video_init(void);
+EINTERN void e_comp_wl_video_shutdown(void);
+
+EINTERN tdm_layer* e_comp_wl_video_layer_get(tdm_output *output);
+
+#define C(b,m)              (((b) >> (m)) & 0xFF)
+#define FOURCC_STR(id)      C(id,0), C(id,8), C(id,16), C(id,24)
+#define IS_RGB(f)           ((f) == TBM_FORMAT_XRGB8888 || (f) == TBM_FORMAT_ARGB8888)
+#define ROUNDUP(s,c)        (((s) + (c-1)) & ~(c-1))
+
+#endif
+#endif
diff --git a/src/bin/e_comp_wl_video_buffer.c b/src/bin/e_comp_wl_video_buffer.c
new file mode 100644 (file)
index 0000000..c3c6fb4
--- /dev/null
@@ -0,0 +1,965 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "e.h"
+#include <sys/mman.h>
+#include <Ecore_Drm.h>
+#include <pixman.h>
+#include <tdm_helper.h>
+
+//#define DEBUG_LIFECYCLE
+
+#define BER(fmt,arg...)   ERR("%d: "fmt, vbuf ? vbuf->stamp : 0, ##arg)
+#define BWR(fmt,arg...)   WRN("%d: "fmt, vbuf ? vbuf->stamp : 0, ##arg)
+#define BIN(fmt,arg...)   INF("%d: "fmt, vbuf ? vbuf->stamp : 0, ##arg)
+#define BDB(fmt,arg...)   DBG("%d: "fmt, vbuf ? vbuf->stamp : 0, ##arg)
+
+#define VBUF_RETURN_IF_FAIL(cond) \
+   {if (!(cond)) { BER("'%s' failed", #cond); return; }}
+#define VBUF_RETURN_VAL_IF_FAIL(cond, val) \
+   {if (!(cond)) { BER("'%s' failed", #cond); return val; }}
+
+#ifdef DEBUG_LIFECYCLE
+#undef BDB
+#define BDB BIN
+#endif
+
+typedef struct _VBufFreeFuncInfo
+{
+   VBuf_Free_Func func;
+   void *data;
+} VBufFreeFuncInfo;
+
+static void _e_comp_wl_video_buffer_cb_destroy(struct wl_listener *listener, void *data);
+static void _e_comp_wl_video_buffer_free(E_Comp_Wl_Video_Buf *vbuf);
+#define e_comp_wl_video_buffer_free(b) _e_comp_wl_video_buffer_free(b)
+
+static Eina_List *vbuf_lists;
+
+static E_Comp_Wl_Video_Buf*
+_find_vbuf(uint stamp)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+   Eina_List *l;
+
+   if (!vbuf_lists)
+     return NULL;
+
+   EINA_LIST_FOREACH(vbuf_lists, l, vbuf)
+     {
+        if (vbuf->stamp == stamp)
+          return vbuf;
+     }
+
+   return NULL;
+}
+
+static Eina_Bool
+_e_comp_wl_video_buffer_access_data_begin(E_Comp_Wl_Video_Buf *vbuf)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, EINA_FALSE);
+
+   vbuf->ptrs[0] = vbuf->ptrs[1] = vbuf->ptrs[2] = NULL;
+
+   if (vbuf->type == TYPE_SHM)
+     {
+        struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get(vbuf->resource);
+        EINA_SAFETY_ON_NULL_RETURN_VAL(shm_buffer, EINA_FALSE);
+        vbuf->ptrs[0] = wl_shm_buffer_get_data(shm_buffer);
+        EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf->ptrs[0], EINA_FALSE);
+        return EINA_TRUE;
+     }
+   else if (vbuf->type == TYPE_TBM)
+     {
+        int i, j;
+        tbm_bo bos[4] = {0,};
+
+        for (i = 0; i < 3; i++)
+          {
+             tbm_bo_handle bo_handles;
+
+             bos[i] = tbm_surface_internal_get_bo(vbuf->tbm_surface, i);
+             if (!bos[i]) continue;
+
+             bo_handles = tbm_bo_map(bos[i], TBM_DEVICE_CPU, TBM_OPTION_READ);
+             if (!bo_handles.ptr)
+               {
+                  for (j = 0; j < i; j++)
+                    tbm_bo_unmap(bos[j]);
+                  return EINA_FALSE;
+               }
+
+             vbuf->ptrs[i] = bo_handles.ptr;
+          }
+
+        EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf->ptrs[0], EINA_FALSE);
+
+        switch(vbuf->tbmfmt)
+          {
+           case TBM_FORMAT_YVU420:
+           case TBM_FORMAT_YUV420:
+              if (!vbuf->ptrs[1])
+                vbuf->ptrs[1] = vbuf->ptrs[0];
+              if (!vbuf->ptrs[2])
+                vbuf->ptrs[2] = vbuf->ptrs[1];
+              break;
+           case TBM_FORMAT_NV12:
+           case TBM_FORMAT_NV21:
+              if (!vbuf->ptrs[1])
+                vbuf->ptrs[1] = vbuf->ptrs[0];
+              break;
+           default:
+              break;
+          }
+
+        return EINA_TRUE;
+     }
+
+   return EINA_FALSE;
+}
+
+static void
+_e_comp_wl_video_buffer_access_data_end(E_Comp_Wl_Video_Buf *vbuf)
+{
+   EINA_SAFETY_ON_NULL_RETURN(vbuf);
+
+   if (vbuf->type == TYPE_SHM)
+     {
+        vbuf->ptrs[0] = NULL;
+     }
+   else if (vbuf->type == TYPE_TBM)
+     {
+        int i;
+        tbm_bo bos[4] = {0,};
+
+        for (i = 0; i < 3; i++)
+          {
+             bos[i] = tbm_surface_internal_get_bo(vbuf->tbm_surface, i);
+             if (!bos[i]) continue;
+
+             tbm_bo_unmap(bos[i]);
+             vbuf->ptrs[i] = NULL;
+          }
+     }
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_comp_wl_video_buffer_create_res(struct wl_resource *resource)
+{
+   E_Comp_Wl_Video_Buf *vbuf = NULL;
+   struct wl_shm_buffer *shm_buffer;
+   tbm_surface_h tbm_surface;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(resource, NULL);
+
+   vbuf = calloc(1, sizeof(E_Comp_Wl_Video_Buf));
+   EINA_SAFETY_ON_FALSE_GOTO(vbuf != NULL, create_fail);
+
+   vbuf->ref_cnt = 1;
+   vbuf->stamp = e_comp_wl_video_buffer_get_mills();
+   while (_find_vbuf(vbuf->stamp))
+     vbuf->stamp++;
+
+   vbuf->resource = resource;
+
+   if ((shm_buffer = wl_shm_buffer_get(resource)))
+     {
+        uint32_t tbmfmt = wl_shm_buffer_get_format(shm_buffer);
+
+        vbuf->type = TYPE_SHM;
+
+        if (tbmfmt == WL_SHM_FORMAT_ARGB8888)
+          vbuf->tbmfmt = TBM_FORMAT_ARGB8888;
+        else if (tbmfmt == WL_SHM_FORMAT_XRGB8888)
+          vbuf->tbmfmt = TBM_FORMAT_XRGB8888;
+        else
+          vbuf->tbmfmt = tbmfmt;
+
+        vbuf->width = wl_shm_buffer_get_width(shm_buffer);
+        vbuf->height = wl_shm_buffer_get_height(shm_buffer);
+        vbuf->pitches[0] = wl_shm_buffer_get_stride(shm_buffer);
+
+        vbuf->width_from_pitch = vbuf->width;
+        vbuf->height_from_size = vbuf->height;;
+     }
+   else if ((tbm_surface = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, resource)))
+     {
+        int i;
+
+        vbuf->type = TYPE_TBM;
+        vbuf->tbm_surface = tbm_surface;
+        tbm_surface_internal_ref(tbm_surface);
+
+        vbuf->tbmfmt = tbm_surface_get_format(tbm_surface);
+        vbuf->width = tbm_surface_get_width(tbm_surface);
+        vbuf->height = tbm_surface_get_height(tbm_surface);
+
+        for (i = 0; i < 3; i++)
+          {
+             uint32_t size = 0, offset = 0, pitch = 0;
+             tbm_bo bo;
+
+             bo = tbm_surface_internal_get_bo(tbm_surface, i);
+             if (bo)
+               {
+                  vbuf->handles[i] = tbm_bo_get_handle(bo, TBM_DEVICE_DEFAULT).u32;
+                  EINA_SAFETY_ON_FALSE_GOTO(vbuf->handles[i] > 0, create_fail);
+
+                  vbuf->names[i] = tbm_bo_export(bo);
+                  EINA_SAFETY_ON_FALSE_GOTO(vbuf->names[i] > 0, create_fail);
+               }
+
+             tbm_surface_internal_get_plane_data(tbm_surface, i, &size, &offset, &pitch);
+             vbuf->pitches[i] = pitch;
+             vbuf->offsets[i] = offset;
+          }
+
+        tdm_helper_get_buffer_full_size(tbm_surface, &vbuf->width_from_pitch, &vbuf->height_from_size);
+     }
+   else
+     {
+        ERR("unknown buffer resource");
+        goto create_fail;
+     }
+
+   vbuf_lists = eina_list_append(vbuf_lists, vbuf);
+
+   BDB("type(%d) %dx%d(%dx%d), %c%c%c%c, name(%d,%d,%d) hnd(%d,%d,%d), pitch(%d,%d,%d), offset(%d,%d,%d)",
+       vbuf->type, vbuf->width_from_pitch, vbuf->height_from_size,
+       vbuf->width, vbuf->height, FOURCC_STR(vbuf->tbmfmt),
+       vbuf->names[0], vbuf->names[1], vbuf->names[2],
+       vbuf->handles[0], vbuf->handles[1], vbuf->handles[2],
+       vbuf->pitches[0], vbuf->pitches[1], vbuf->pitches[2],
+       vbuf->offsets[0], vbuf->offsets[1], vbuf->offsets[2]);
+
+   return vbuf;
+
+create_fail:
+   _e_comp_wl_video_buffer_free(vbuf);
+
+   return NULL;
+}
+
+EINTERN E_Comp_Wl_Video_Buf*
+e_comp_wl_video_buffer_create(struct wl_resource *resource)
+{
+   E_Comp_Wl_Video_Buf *vbuf = _e_comp_wl_video_buffer_create_res(resource);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+   vbuf->destroy_listener.notify = _e_comp_wl_video_buffer_cb_destroy;
+   wl_resource_add_destroy_listener(resource, &vbuf->destroy_listener);
+
+   return vbuf;
+}
+
+EINTERN E_Comp_Wl_Video_Buf*
+e_comp_wl_video_buffer_create_comp(E_Comp_Wl_Buffer *comp_buffer)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(comp_buffer, NULL);
+
+   vbuf = _e_comp_wl_video_buffer_create_res(comp_buffer->resource);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+   vbuf->comp_buffer = comp_buffer;
+
+   vbuf->destroy_listener.notify = _e_comp_wl_video_buffer_cb_destroy;
+   wl_resource_add_destroy_listener(comp_buffer->resource, &vbuf->destroy_listener);
+
+   return vbuf;
+}
+
+EINTERN E_Comp_Wl_Video_Buf*
+e_comp_wl_video_buffer_create_tbm(tbm_surface_h tbm_surface)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+   int i;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surface, NULL);
+
+   vbuf = calloc(1, sizeof(E_Comp_Wl_Video_Buf));
+   EINA_SAFETY_ON_FALSE_GOTO(vbuf != NULL, create_fail);
+
+   vbuf->ref_cnt = 1;
+   vbuf->stamp = e_comp_wl_video_buffer_get_mills();
+   while (_find_vbuf(vbuf->stamp))
+     vbuf->stamp++;
+
+   vbuf->type = TYPE_TBM;
+   vbuf->tbm_surface = tbm_surface;
+   tbm_surface_internal_ref(tbm_surface);
+
+   vbuf->tbmfmt = tbm_surface_get_format(tbm_surface);
+   vbuf->width = tbm_surface_get_width(tbm_surface);
+   vbuf->height = tbm_surface_get_height(tbm_surface);
+
+   for (i = 0; i < 3; i++)
+     {
+        uint32_t size = 0, offset = 0, pitch = 0;
+        tbm_bo bo;
+
+        bo = tbm_surface_internal_get_bo(tbm_surface, i);
+        if (bo)
+          {
+             vbuf->handles[i] = tbm_bo_get_handle(bo, TBM_DEVICE_DEFAULT).u32;
+             EINA_SAFETY_ON_FALSE_GOTO(vbuf->handles[i] > 0, create_fail);
+
+             vbuf->names[i] = tbm_bo_export(bo);
+             EINA_SAFETY_ON_FALSE_GOTO(vbuf->names[i] > 0, create_fail);
+          }
+
+        tbm_surface_internal_get_plane_data(tbm_surface, i, &size, &offset, &pitch);
+        vbuf->pitches[i] = pitch;
+        vbuf->offsets[i] = offset;
+     }
+
+   tdm_helper_get_buffer_full_size(tbm_surface, &vbuf->width_from_pitch, &vbuf->height_from_size);
+
+   vbuf_lists = eina_list_append(vbuf_lists, vbuf);
+
+   BDB("type(%d) %dx%d(%dx%d), %c%c%c%c, name(%d,%d,%d) hnd(%d,%d,%d), pitch(%d,%d,%d), offset(%d,%d,%d)",
+       vbuf->type, vbuf->width_from_pitch, vbuf->height_from_size,
+       vbuf->width, vbuf->height, FOURCC_STR(vbuf->tbmfmt),
+       vbuf->names[0], vbuf->names[1], vbuf->names[2],
+       vbuf->handles[0], vbuf->handles[1], vbuf->handles[2],
+       vbuf->pitches[0], vbuf->pitches[1], vbuf->pitches[2],
+       vbuf->offsets[0], vbuf->offsets[1], vbuf->offsets[2]);
+
+   return vbuf;
+
+create_fail:
+   _e_comp_wl_video_buffer_free(vbuf);
+
+   return NULL;
+}
+
+EINTERN E_Comp_Wl_Video_Buf*
+e_comp_wl_video_buffer_alloc(int width, int height, tbm_format tbmfmt, Eina_Bool scanout)
+{
+   E_Comp_Wl_Video_Buf *vbuf = NULL;
+   tbm_surface_h tbm_surface = NULL;
+   int i;
+
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(width > 0, NULL);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(height > 0, NULL);
+
+   vbuf = calloc(1, sizeof(E_Comp_Wl_Video_Buf));
+   EINA_SAFETY_ON_FALSE_GOTO(vbuf != NULL, alloc_fail);
+
+   vbuf->ref_cnt = 1;
+   vbuf->stamp = e_comp_wl_video_buffer_get_mills();
+   while (_find_vbuf(vbuf->stamp))
+     vbuf->stamp++;
+
+   if (scanout)
+     tbm_surface = tbm_surface_internal_create_with_flags(width, height, tbmfmt, TBM_BO_SCANOUT);
+   else
+     tbm_surface = tbm_surface_internal_create_with_flags(width, height, tbmfmt, TBM_BO_DEFAULT);
+   EINA_SAFETY_ON_NULL_GOTO(tbm_surface, alloc_fail);
+
+   vbuf->type = TYPE_TBM;
+   vbuf->tbm_surface = tbm_surface;
+   tbm_surface_internal_ref(tbm_surface);
+
+   vbuf->tbmfmt = tbmfmt;
+   vbuf->width = width;
+   vbuf->height = height;
+
+   for (i = 0; i < 3; i++)
+     {
+        uint32_t size = 0, offset = 0, pitch = 0;
+        tbm_bo bo;
+
+        bo = tbm_surface_internal_get_bo(tbm_surface, i);
+        if (bo)
+          {
+             vbuf->handles[i] = tbm_bo_get_handle(bo, TBM_DEVICE_DEFAULT).u32;
+             EINA_SAFETY_ON_FALSE_GOTO(vbuf->handles[i] > 0, alloc_fail);
+
+             vbuf->names[i] = tbm_bo_export(bo);
+             EINA_SAFETY_ON_FALSE_GOTO(vbuf->names[i] > 0, alloc_fail);
+          }
+
+        tbm_surface_internal_get_plane_data(tbm_surface, i, &size, &offset, &pitch);
+        vbuf->pitches[i] = pitch;
+        vbuf->offsets[i] = offset;
+     }
+
+   tdm_helper_get_buffer_full_size(tbm_surface, &vbuf->width_from_pitch, &vbuf->height_from_size);
+
+   tbm_surface_internal_unref(tbm_surface);
+
+   vbuf_lists = eina_list_append(vbuf_lists, vbuf);
+
+   BDB("type(%d) %dx%d(%dx%d) %c%c%c%c nm(%d,%d,%d) hnd(%d,%d,%d) pitch(%d,%d,%d) offset(%d,%d,%d)",
+       vbuf->type, vbuf->width_from_pitch, vbuf->height_from_size,
+       vbuf->width, vbuf->height, FOURCC_STR(vbuf->tbmfmt),
+       vbuf->names[0], vbuf->names[1], vbuf->names[2],
+       vbuf->handles[0], vbuf->handles[1], vbuf->handles[2],
+       vbuf->pitches[0], vbuf->pitches[1], vbuf->pitches[2],
+       vbuf->offsets[0], vbuf->offsets[1], vbuf->offsets[2]);
+
+   return vbuf;
+
+alloc_fail:
+   if (tbm_surface)
+     tbm_surface_internal_unref(tbm_surface);
+   _e_comp_wl_video_buffer_free(vbuf);
+   return NULL;
+}
+
+EINTERN E_Comp_Wl_Video_Buf*
+e_comp_wl_video_buffer_ref(E_Comp_Wl_Video_Buf *vbuf)
+{
+   if (!vbuf)
+     return NULL;
+
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(VBUF_IS_VALID(vbuf), NULL);
+
+   vbuf->ref_cnt++;
+   BDB("count(%d) ref", vbuf->ref_cnt);
+
+   return vbuf;
+}
+
+EINTERN void
+e_comp_wl_video_buffer_unref(E_Comp_Wl_Video_Buf *vbuf)
+{
+   if (!vbuf)
+     return;
+
+   VBUF_RETURN_IF_FAIL(e_comp_wl_video_buffer_valid(vbuf));
+
+   vbuf->ref_cnt--;
+   BDB("count(%d) unref", vbuf->ref_cnt);
+
+   if (!vbuf->buffer_destroying && vbuf->ref_cnt == 0)
+     _e_comp_wl_video_buffer_free(vbuf);
+}
+
+static void
+_e_comp_wl_video_buffer_free(E_Comp_Wl_Video_Buf *vbuf)
+{
+   VBufFreeFuncInfo *info;
+   Eina_List *l, *ll;
+
+   if (!vbuf)
+     return;
+
+   VBUF_RETURN_IF_FAIL(e_comp_wl_video_buffer_valid(vbuf));
+
+   BDB("vbuf(%p) tbm_surface(%p) freed", vbuf, vbuf->tbm_surface);
+
+   vbuf->buffer_destroying = EINA_TRUE;
+
+   if (vbuf->destroy_listener.notify)
+     {
+        wl_list_remove(&vbuf->destroy_listener.link);
+        vbuf->destroy_listener.notify = NULL;
+     }
+
+   EINA_LIST_FOREACH_SAFE(vbuf->free_funcs, l, ll, info)
+     {
+        /* call before tmb_bo_unref */
+        vbuf->free_funcs = eina_list_remove_list(vbuf->free_funcs, l);
+        if (info->func)
+          info->func(vbuf, info->data);
+        free(info);
+     }
+
+#if 0
+   /* DO not check ref_count here. Even if ref_count is not 0, vbuf can be
+    * be destroyed by wl_buffer_destroy forcely. video or screenmirror should add
+    * the vbuf free function and handle the destroying vbuf situation.
+    */
+   if (!vbuf->buffer_destroying)
+     VBUF_RETURN_IF_FAIL(vbuf->ref_cnt == 0);
+#endif
+
+   /* make sure all operation is done */
+   VBUF_RETURN_IF_FAIL(vbuf->in_use == EINA_FALSE);
+
+   if (vbuf->type == TYPE_TBM && vbuf->tbm_surface)
+     {
+        tbm_surface_internal_unref(vbuf->tbm_surface);
+        vbuf->tbm_surface = NULL;
+     }
+
+   vbuf_lists = eina_list_remove(vbuf_lists, vbuf);
+
+   vbuf->stamp = 0;
+
+   free(vbuf);
+}
+
+static void
+_e_comp_wl_video_buffer_cb_destroy(struct wl_listener *listener, void *data)
+{
+   E_Comp_Wl_Video_Buf *vbuf = container_of(listener, E_Comp_Wl_Video_Buf, destroy_listener);
+
+   if (!vbuf) return;
+
+   vbuf->comp_buffer = NULL;
+
+   if (vbuf->buffer_destroying == EINA_FALSE)
+     {
+        vbuf->destroy_listener.notify = NULL;
+        _e_comp_wl_video_buffer_free(vbuf);
+     }
+}
+
+EINTERN void
+e_comp_wl_video_buffer_clear(E_Comp_Wl_Video_Buf *vbuf)
+{
+   EINA_SAFETY_ON_NULL_RETURN(vbuf);
+
+   if (!_e_comp_wl_video_buffer_access_data_begin(vbuf))
+     {
+        BER("can't access ptr");
+        return;
+     }
+
+   switch(vbuf->tbmfmt)
+     {
+      case TBM_FORMAT_ARGB8888:
+      case TBM_FORMAT_XRGB8888:
+         memset(vbuf->ptrs[0], 0, vbuf->pitches[0] * vbuf->height);
+         break;
+      case TBM_FORMAT_YVU420:
+      case TBM_FORMAT_YUV420:
+         memset((char*)vbuf->ptrs[0] + vbuf->offsets[0], 0x10, vbuf->pitches[0] * vbuf->height);
+         memset((char*)vbuf->ptrs[1] + vbuf->offsets[1], 0x80, vbuf->pitches[1] * (vbuf->height >> 1));
+         memset((char*)vbuf->ptrs[2] + vbuf->offsets[2], 0x80, vbuf->pitches[2] * (vbuf->height >> 1));
+         break;
+      case TBM_FORMAT_NV12:
+      case TBM_FORMAT_NV21:
+         memset((char*)vbuf->ptrs[0] + vbuf->offsets[0], 0x10, vbuf->pitches[0] * vbuf->height);
+         memset((char*)vbuf->ptrs[1] + vbuf->offsets[1], 0x80, vbuf->pitches[1] * (vbuf->height >> 1));
+         break;
+      case TBM_FORMAT_YUYV:
+           {
+              int *ibuf = (int*)vbuf->ptrs[0];
+              int i, size = vbuf->pitches[0] * vbuf->height / 4;
+
+              for (i = 0 ; i < size ; i++)
+                ibuf[i] = 0x10801080;
+           }
+         break;
+      case TBM_FORMAT_UYVY:
+           {
+              int *ibuf = (int*)vbuf->ptrs[0];
+              int i, size = vbuf->pitches[0] * vbuf->height / 4;
+
+              for (i = 0 ; i < size ; i++)
+                ibuf[i] = 0x80108010; /* YUYV -> 0xVYUY */
+           }
+         break;
+      default:
+         BWR("can't clear %c%c%c%c buffer", FOURCC_STR(vbuf->tbmfmt));
+         break;
+     }
+
+   _e_comp_wl_video_buffer_access_data_end(vbuf);
+}
+
+EINTERN Eina_Bool
+e_comp_wl_video_buffer_valid(E_Comp_Wl_Video_Buf *vbuf)
+{
+   E_Comp_Wl_Video_Buf *temp;
+   Eina_List *l;
+
+   VBUF_RETURN_VAL_IF_FAIL(vbuf != NULL, EINA_FALSE);
+   VBUF_RETURN_VAL_IF_FAIL(vbuf->stamp != 0, EINA_FALSE);
+
+   EINA_LIST_FOREACH(vbuf_lists, l, temp)
+     {
+        if (temp->stamp == vbuf->stamp)
+          return EINA_TRUE;
+     }
+
+   BDB("vbuf(%p) invalid", vbuf);
+
+   return EINA_FALSE;
+}
+
+static VBufFreeFuncInfo*
+_e_comp_wl_video_buffer_free_func_find(E_Comp_Wl_Video_Buf *vbuf, VBuf_Free_Func func, void *data)
+{
+   VBufFreeFuncInfo *info;
+   Eina_List *l;
+
+   EINA_LIST_FOREACH(vbuf->free_funcs, l, info)
+     {
+        if (info->func == func && info->data == data)
+          return info;
+     }
+
+   return NULL;
+}
+
+EINTERN void
+e_comp_wl_video_buffer_free_func_add(E_Comp_Wl_Video_Buf *vbuf, VBuf_Free_Func func, void *data)
+{
+   VBufFreeFuncInfo *info;
+
+   EINA_SAFETY_ON_FALSE_RETURN(VBUF_IS_VALID(vbuf));
+   EINA_SAFETY_ON_NULL_RETURN(func);
+
+   info = _e_comp_wl_video_buffer_free_func_find(vbuf, func, data);
+   if (info)
+     return;
+
+   info = calloc(1, sizeof(VBufFreeFuncInfo));
+   EINA_SAFETY_ON_NULL_RETURN(info);
+
+   info->func = func;
+   info->data = data;
+
+   vbuf->free_funcs = eina_list_append(vbuf->free_funcs, info);
+}
+
+EINTERN void
+e_comp_wl_video_buffer_free_func_del(E_Comp_Wl_Video_Buf *vbuf, VBuf_Free_Func func, void *data)
+{
+   VBufFreeFuncInfo *info;
+
+   EINA_SAFETY_ON_FALSE_RETURN(VBUF_IS_VALID(vbuf));
+   EINA_SAFETY_ON_NULL_RETURN(func);
+
+   info = _e_comp_wl_video_buffer_free_func_find(vbuf, func, data);
+   if (!info)
+     return;
+
+   vbuf->free_funcs = eina_list_remove(vbuf->free_funcs, info);
+
+   free(info);
+}
+
+static pixman_format_code_t
+_e_comp_wl_video_buffer_pixman_format_get(E_Comp_Wl_Video_Buf *vbuf)
+{
+   switch(vbuf->tbmfmt)
+     {
+      case TBM_FORMAT_ARGB8888: return PIXMAN_a8r8g8b8;
+      case TBM_FORMAT_XRGB8888: return PIXMAN_x8r8g8b8;
+      default:                  return 0;
+     }
+   return 0;
+}
+
+EINTERN void
+e_comp_wl_video_buffer_convert(E_Comp_Wl_Video_Buf *srcbuf, E_Comp_Wl_Video_Buf *dstbuf,
+                               int sx, int sy, int sw, int sh,
+                               int dx, int dy, int dw, int dh,
+                               Eina_Bool over, int rotate, int hflip, int vflip)
+{
+   pixman_image_t *src_img = NULL, *dst_img = NULL;
+   pixman_format_code_t src_format, dst_format;
+   double scale_x, scale_y;
+   int rotate_step;
+   pixman_transform_t t;
+   struct pixman_f_transform ft;
+   pixman_op_t op;
+   int src_stride, dst_stride;
+   int buf_width;
+
+   EINA_SAFETY_ON_FALSE_RETURN(VBUF_IS_VALID(srcbuf));
+   EINA_SAFETY_ON_FALSE_RETURN(VBUF_IS_VALID(dstbuf));
+
+   if (!_e_comp_wl_video_buffer_access_data_begin(srcbuf))
+     return;
+   if (!_e_comp_wl_video_buffer_access_data_begin(dstbuf))
+     {
+        _e_comp_wl_video_buffer_access_data_end(srcbuf);
+        return;
+     }
+
+   /* not handle buffers which have 2 more gem handles */
+   EINA_SAFETY_ON_NULL_GOTO(srcbuf->ptrs[0], cant_convert);
+   EINA_SAFETY_ON_NULL_GOTO(dstbuf->ptrs[0], cant_convert);
+   EINA_SAFETY_ON_FALSE_RETURN(!srcbuf->ptrs[1]);
+   EINA_SAFETY_ON_FALSE_RETURN(!dstbuf->ptrs[1]);
+
+   src_format = _e_comp_wl_video_buffer_pixman_format_get(srcbuf);
+   EINA_SAFETY_ON_FALSE_GOTO(src_format > 0, cant_convert);
+   dst_format = _e_comp_wl_video_buffer_pixman_format_get(dstbuf);
+   EINA_SAFETY_ON_FALSE_GOTO(dst_format > 0, cant_convert);
+
+   buf_width = IS_RGB(srcbuf->tbmfmt)?(srcbuf->pitches[0]/4):srcbuf->pitches[0];
+   src_stride = IS_RGB(srcbuf->tbmfmt)?(srcbuf->pitches[0]):buf_width * (PIXMAN_FORMAT_BPP(src_format) / 8);
+   src_img = pixman_image_create_bits(src_format, buf_width, srcbuf->height,
+                                      (uint32_t*)srcbuf->ptrs[0], src_stride);
+   EINA_SAFETY_ON_NULL_GOTO(src_img, cant_convert);
+
+   buf_width = IS_RGB(dstbuf->tbmfmt)?(dstbuf->pitches[0]/4):dstbuf->pitches[0];
+   dst_stride = IS_RGB(srcbuf->tbmfmt)?(dstbuf->pitches[0]):buf_width * (PIXMAN_FORMAT_BPP(dst_format) / 8);
+   dst_img = pixman_image_create_bits(dst_format, buf_width, dstbuf->height,
+                                      (uint32_t*)dstbuf->ptrs[0], dst_stride);
+   EINA_SAFETY_ON_NULL_GOTO(dst_img, cant_convert);
+
+   pixman_f_transform_init_identity(&ft);
+
+   if (hflip)
+     {
+        pixman_f_transform_scale(&ft, NULL, -1, 1);
+        pixman_f_transform_translate(&ft, NULL, dw, 0);
+     }
+
+   if (vflip)
+     {
+        pixman_f_transform_scale(&ft, NULL, 1, -1);
+        pixman_f_transform_translate(&ft, NULL, 0, dh);
+     }
+
+   rotate_step = (rotate + 360) / 90 % 4;
+   if (rotate_step > 0)
+     {
+        int c, s, tx = 0, ty = 0;
+        switch (rotate_step)
+          {
+           case 1:
+              c = 0, s = -1, tx = -dw;
+              break;
+           case 2:
+              c = -1, s = 0, tx = -dw, ty = -dh;
+              break;
+           case 3:
+              c = 0, s = 1, ty = -dh;
+              break;
+          }
+        pixman_f_transform_translate(&ft, NULL, tx, ty);
+        pixman_f_transform_rotate(&ft, NULL, c, s);
+     }
+
+   if (rotate_step % 2 == 0)
+     {
+        scale_x = (double)sw / dw;
+        scale_y = (double)sh / dh;
+     }
+   else
+     {
+        scale_x = (double)sw / dh;
+        scale_y = (double)sh / dw;
+     }
+
+   pixman_f_transform_scale(&ft, NULL, scale_x, scale_y);
+   pixman_f_transform_translate(&ft, NULL, sx, sy);
+   pixman_transform_from_pixman_f_transform(&t, &ft);
+   pixman_image_set_transform(src_img, &t);
+
+   if (!over) op = PIXMAN_OP_SRC;
+   else op = PIXMAN_OP_OVER;
+
+   pixman_image_composite(op, src_img, NULL, dst_img, 0, 0, 0, 0,
+                          dx, dy, dw, dh);
+cant_convert:
+   if (src_img) pixman_image_unref(src_img);
+   if (dst_img) pixman_image_unref(dst_img);
+
+   _e_comp_wl_video_buffer_access_data_end(srcbuf);
+   _e_comp_wl_video_buffer_access_data_end(dstbuf);
+}
+
+EINTERN Eina_Bool
+e_comp_wl_video_buffer_copy(E_Comp_Wl_Video_Buf *srcbuf, E_Comp_Wl_Video_Buf *dstbuf)
+{
+   int i, j, c_height;
+   unsigned char *s, *d;
+
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(VBUF_IS_VALID(srcbuf), EINA_FALSE);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(VBUF_IS_VALID(dstbuf), EINA_FALSE);
+
+   if (!_e_comp_wl_video_buffer_access_data_begin(srcbuf))
+     return EINA_FALSE;
+   if (!_e_comp_wl_video_buffer_access_data_begin(dstbuf))
+     {
+        _e_comp_wl_video_buffer_access_data_end(srcbuf);
+        return EINA_FALSE;
+     }
+
+   switch (srcbuf->tbmfmt)
+     {
+      case TBM_FORMAT_ARGB8888:
+      case TBM_FORMAT_XRGB8888:
+      case TBM_FORMAT_YUV422:
+      case TBM_FORMAT_YVU422:
+         s = (unsigned char*)srcbuf->ptrs[0];
+         d = (unsigned char*)dstbuf->ptrs[0];
+         for (i = 0; i < srcbuf->height; i++)
+           {
+              memcpy(d, s, srcbuf->pitches[0]);
+              s += srcbuf->pitches[0];
+              d += dstbuf->pitches[0];
+           }
+         break;
+      case TBM_FORMAT_YUV420:
+      case TBM_FORMAT_YVU420:
+         for (i = 0; i < 3; i++)
+           {
+              s = (unsigned char*)srcbuf->ptrs[i] + srcbuf->offsets[i];
+              d = (unsigned char*)dstbuf->ptrs[i] + dstbuf->offsets[i];
+              c_height = (i == 0) ? srcbuf->height : srcbuf->height / 2;
+              for (j = 0; j < c_height; j++)
+                {
+                   memcpy(d, s, srcbuf->pitches[i]);
+                   s += srcbuf->pitches[i];
+                   d += dstbuf->pitches[i];
+                }
+           }
+         break;
+      case TBM_FORMAT_NV12:
+      case TBM_FORMAT_NV21:
+         for (i = 0; i < 2; i++)
+           {
+              s = (unsigned char*)srcbuf->ptrs[i] + srcbuf->offsets[i];
+              d = (unsigned char*)dstbuf->ptrs[i] + dstbuf->offsets[i];
+              c_height = (i == 0) ? srcbuf->height : srcbuf->height / 2;
+              for (j = 0; j < c_height; j++)
+                {
+                   memcpy(d, s, srcbuf->pitches[i]);
+                   s += srcbuf->pitches[i];
+                   d += dstbuf->pitches[i];
+                }
+           }
+         break;
+      default:
+         ERR("not implemented for %c%c%c%c", FOURCC_STR(srcbuf->tbmfmt));
+         _e_comp_wl_video_buffer_access_data_end(srcbuf);
+         _e_comp_wl_video_buffer_access_data_end(dstbuf);
+
+         return EINA_FALSE;
+     }
+
+   _e_comp_wl_video_buffer_access_data_end(srcbuf);
+   _e_comp_wl_video_buffer_access_data_end(dstbuf);
+
+   return EINA_TRUE;
+}
+
+EINTERN uint
+e_comp_wl_video_buffer_get_mills(void)
+{
+   struct timespec tp;
+
+   clock_gettime(CLOCK_MONOTONIC, &tp);
+
+   return (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000L);
+}
+
+EINTERN int
+e_comp_wl_video_buffer_list_length(void)
+{
+   return eina_list_count(vbuf_lists);
+}
+
+EINTERN void
+e_comp_wl_video_buffer_list_print(const char *log_path)
+{
+   E_Comp_Wl_Video_Buf *vbuf;
+   Eina_List *l;
+   FILE *log_fl;
+
+   log_fl = fopen(log_path, "a");
+   if (!log_fl)
+     {
+        ERR("failed: open file(%s)", log_path);
+        return;
+     }
+
+   setvbuf(log_fl, NULL, _IOLBF, 512);
+
+   fprintf(log_fl, "* Video Buffers:\n");
+   fprintf(log_fl, "stamp\tsize\tformat\thandles\tpitches\toffsets\tin_use\n");
+   EINA_LIST_FOREACH(vbuf_lists, l, vbuf)
+     {
+        fprintf(log_fl, "%d\t%dx%d\t%c%c%c%c\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d\n",
+                vbuf->stamp, vbuf->width, vbuf->height, FOURCC_STR(vbuf->tbmfmt),
+                vbuf->handles[0], vbuf->handles[1], vbuf->handles[2],
+                vbuf->pitches[0], vbuf->pitches[1], vbuf->pitches[2],
+                vbuf->offsets[0], vbuf->offsets[1], vbuf->offsets[2],
+                vbuf->in_use);
+     }
+
+   fclose(log_fl);
+}
+
+EINTERN void
+e_comp_wl_video_buffer_size_get(E_Client *ec, int *bw, int *bh)
+{
+   E_Comp_Wl_Buffer *buffer = e_pixmap_resource_get(ec->pixmap);
+
+   if (bw) *bw = 0;
+   if (bh) *bh = 0;
+
+   if (!buffer)
+     {
+        INF("no buffer. using ec(%p) size(%dx%d)", ec, ec->w, ec->h);
+        if (bw) *bw = ec->w;
+        if (bh) *bh = ec->h;
+        return;
+     }
+
+   if (buffer->type == E_COMP_WL_BUFFER_TYPE_VIDEO)
+     {
+        tbm_surface_h tbm_surface = wayland_tbm_server_get_surface(NULL, buffer->resource);
+
+        if (bw) *bw = tbm_surface_get_width(tbm_surface);
+        if (bh) *bh = tbm_surface_get_height(tbm_surface);
+     }
+   else
+     {
+        if (bw) *bw = buffer->w;
+        if (bh) *bh = buffer->h;
+     }
+}
+
+EINTERN void
+e_comp_wl_video_buffer_transform_scale_size_get(E_Client *ec, int *bw, int *bh)
+{
+   E_Comp_Wl_Buffer *buffer = e_pixmap_resource_get(ec->pixmap);
+   E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+   int w, h, transform;
+
+   if (bw) *bw = 0;
+   if (bh) *bh = 0;
+
+   if (!buffer)
+     {
+        INF("no buffer. using ec(%p) size(%dx%d)", ec, ec->w, ec->h);
+        if (bw) *bw = ec->w;
+        if (bh) *bh = ec->h;
+        return;
+     }
+
+   if (buffer->type == E_COMP_WL_BUFFER_TYPE_VIDEO)
+     {
+        tbm_surface_h tbm_surface = wayland_tbm_server_get_surface(NULL, buffer->resource);
+
+        w = tbm_surface_get_width(tbm_surface);
+        h = tbm_surface_get_height(tbm_surface);
+     }
+   else
+     {
+        w = buffer->w;
+        h = buffer->h;
+     }
+
+   transform = e_comp_wl_output_buffer_transform_get(ec);
+
+   switch (transform)
+     {
+      case WL_OUTPUT_TRANSFORM_90:
+      case WL_OUTPUT_TRANSFORM_270:
+      case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+      case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+         if (bw) *bw = h / vp->buffer.scale;
+         if (bh) *bh = w / vp->buffer.scale;
+         break;
+      default:
+         if (bw) *bw = w / vp->buffer.scale;
+         if (bh) *bh = h / vp->buffer.scale;
+         break;
+     }
+}
diff --git a/src/bin/e_comp_wl_video_buffer.h b/src/bin/e_comp_wl_video_buffer.h
new file mode 100644 (file)
index 0000000..a0c5446
--- /dev/null
@@ -0,0 +1,99 @@
+#ifdef E_TYPEDEFS
+
+typedef enum _E_Comp_Wl_Video_Buf_Type
+{
+   TYPE_SHM,
+   TYPE_TBM,
+} E_Comp_Wl_Video_Buf_Type;
+
+typedef struct _E_Comp_Wl_Video_Buf E_Comp_Wl_Video_Buf;
+
+#else
+#ifndef E_COMP_WL_VIDEO_BUFFER_H
+#define E_COMP_WL_VIDEO_BUFFER_H
+
+#include "e_comp_wl_tbm.h"
+#include <wayland-tbm-server.h>
+#include <tizen-extension-server-protocol.h>
+#include <tdm.h>
+
+struct _E_Comp_Wl_Video_Buf
+{
+   /* to manage lifecycle */
+   uint ref_cnt;
+
+   /* to check valid */
+   uint stamp;
+
+   /* to manage wl_resource */
+   struct wl_resource *resource;
+   struct wl_listener destroy_listener;
+
+   Eina_Bool buffer_destroying;
+
+   E_Comp_Wl_Video_Buf_Type type;
+   tbm_surface_h tbm_surface;
+
+   /* pitch contains the full buffer width.
+    * width indicates the content area width.
+    */
+   tbm_format tbmfmt;
+   int width;
+   int height;
+   uint handles[4];
+   uint pitches[4];
+   uint offsets[4];
+   int names[4];
+   void *ptrs[4];
+
+   int width_from_pitch;
+   int height_from_size;
+
+   /* to avoid reading & write at same time */
+   Eina_Bool in_use;
+
+   Eina_List *free_funcs;
+
+   /* for wl_buffer.release event */
+   E_Comp_Wl_Buffer *comp_buffer;
+   E_Comp_Wl_Buffer_Ref buffer_ref;
+   Eina_Rectangle content_r;        /* content rect */
+   unsigned int content_t;          /* content transform */
+};
+
+EINTERN E_Comp_Wl_Video_Buf* e_comp_wl_video_buffer_create(struct wl_resource *resource);
+EINTERN E_Comp_Wl_Video_Buf* e_comp_wl_video_buffer_create_comp(E_Comp_Wl_Buffer *comp_buffer);
+EINTERN E_Comp_Wl_Video_Buf* e_comp_wl_video_buffer_create_tbm(tbm_surface_h tbm_surface);
+EINTERN E_Comp_Wl_Video_Buf* e_comp_wl_video_buffer_alloc(int width, int height, tbm_format tbmfmt, Eina_Bool scanout);
+EINTERN E_Comp_Wl_Video_Buf* e_comp_wl_video_buffer_ref(E_Comp_Wl_Video_Buf *vbuf);
+EINTERN void          e_comp_wl_video_buffer_unref(E_Comp_Wl_Video_Buf *vbuf);
+EINTERN Eina_Bool     e_comp_wl_video_buffer_valid(E_Comp_Wl_Video_Buf *vbuf);
+
+#define VBUF_IS_VALID(b)   e_comp_wl_video_buffer_valid(b)
+#define MSTAMP(b)          ((b)?(b)->stamp:0)
+
+#define e_comp_wl_video_buffer_set_use(b, v)    \
+   do { \
+      if (b) b->in_use = v; \
+   } while (0)
+
+typedef void (*VBuf_Free_Func) (E_Comp_Wl_Video_Buf *vbuf, void *data);
+EINTERN void e_comp_wl_video_buffer_free_func_add(E_Comp_Wl_Video_Buf *vbuf, VBuf_Free_Func func, void *data);
+EINTERN void e_comp_wl_video_buffer_free_func_del(E_Comp_Wl_Video_Buf *vbuf, VBuf_Free_Func func, void *data);
+
+EINTERN void e_comp_wl_video_buffer_clear(E_Comp_Wl_Video_Buf *vbuf);
+EINTERN Eina_Bool e_comp_wl_video_buffer_copy(E_Comp_Wl_Video_Buf *srcbuf, E_Comp_Wl_Video_Buf *dstbuf);
+EINTERN void e_comp_wl_video_buffer_convert(E_Comp_Wl_Video_Buf *srcbuf, E_Comp_Wl_Video_Buf *dstbuf,
+                                            int sx, int sy, int sw, int sh,
+                                            int dx, int dy, int dw, int dh,
+                                            Eina_Bool over, int rotate, int hflip, int vflip);
+
+EINTERN uint e_comp_wl_video_buffer_get_mills(void);
+EINTERN int  e_comp_wl_video_buffer_list_length(void);
+EINTERN void e_comp_wl_video_buffer_list_print(const char *log_path);
+
+EINTERN void e_comp_wl_video_buffer_size_get(E_Client *ec, int *bw, int *bh);
+EINTERN void e_comp_wl_video_buffer_transform_scale_size_get(E_Client *ec, int *bw, int *bh);
+
+#endif
+#endif
diff --git a/src/bin/e_comp_wl_viewport.c b/src/bin/e_comp_wl_viewport.c
new file mode 100644 (file)
index 0000000..7b12180
--- /dev/null
@@ -0,0 +1,1858 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "e.h"
+#include <wayland-server.h>
+#include <wayland-tbm-server.h>
+
+#define PER(fmt,arg...)   ERR("window(0x%08"PRIxPTR") ec(%p) epc(%p): "fmt, \
+                              viewport->window, viewport->ec, viewport->epc, ##arg)
+#define PWR(fmt,arg...)   WRN("window(0x%08"PRIxPTR") ec(%p) epc(%p): "fmt, \
+                              viewport->window, viewport->ec, viewport->epc, ##arg)
+#define PIN(fmt,arg...)   INF("window(0x%08"PRIxPTR") ec(%p) epc(%p): "fmt, \
+                              viewport->window, viewport->ec, viewport->epc, ##arg)
+#define PDB(fmt,arg...)   DBG("window(0x%08"PRIxPTR") ec(%p) epc(%p): "fmt, \
+                              viewport->window, viewport->ec, viewport->epc, ##arg)
+
+#undef SWAP
+#define SWAP(a, b)  ({double t; t = a; a = b; b = t;})
+
+typedef enum
+{
+   DESTINATION_TYPE_NONE,
+   DESTINATION_TYPE_RECT,
+   DESTINATION_TYPE_RATIO,
+   DESTINATION_TYPE_MODE,
+} E_Viewport_Destination_Type;
+
+typedef struct _E_Viewport
+{
+   struct wl_resource *resource;
+
+   E_Client *ec;
+   E_Client *epc;
+   Ecore_Window window;
+
+   Ecore_Event_Handler *topmost_rotate_hdl;
+
+   struct wl_listener surface_destroy_listener;
+   struct wl_listener surface_apply_viewport_listener;
+
+   Eina_Bool changed;
+
+   unsigned int transform;
+
+   Eina_Rectangle source;
+   Eina_Rectangle cropped_source;
+
+   E_Viewport_Destination_Type type;
+   struct
+     {
+        Eina_Rectangle rect;
+
+        struct
+          {
+             double x, y, w, h;
+          } ratio;
+
+        struct
+          {
+             struct wl_resource *resource;
+
+             enum tizen_destination_mode_type type;
+
+             double ratio_h;
+             double ratio_v;
+
+             double scale_h;
+             double scale_v;
+
+             int offset_x;
+             int offset_y;
+             int offset_w;
+             int offset_h;
+
+             double align_h;
+             double align_v;
+          } mode;
+     } destination;
+
+   Eina_Bool query_parent_size;
+   Eina_Rectangle parent_size;
+
+   Eina_Bool follow_parent_transform;
+
+   E_Client_Hook *client_hook_del;
+   E_Client_Hook *client_hook_move;
+   E_Client_Hook *client_hook_resize;
+
+   E_Comp_Wl_Hook *subsurf_hook_create;
+} E_Viewport;
+
+static E_Viewport* _e_comp_wl_viewport_get_viewport(struct wl_resource *resource);
+static void _e_comp_wl_viewport_cb_parent_resize(void *data, Evas *e, Evas_Object *obj, void *event_info);
+static void _e_comp_wl_viewport_parent_check(E_Viewport *viewport);
+
+static E_Client*
+_topmost_parent_get(E_Client *ec)
+{
+   E_Client *parent = NULL;
+
+   if (!ec->comp_data || !ec->comp_data->sub.data)
+     return ec;
+
+   parent = ec->comp_data->sub.data->parent;
+   while (parent)
+     {
+        if (!parent->comp_data || !parent->comp_data->sub.data)
+          return parent;
+
+        parent = parent->comp_data->sub.data->parent;
+     }
+
+   return ec;
+}
+
+static void
+_destroy_viewport(E_Viewport *viewport)
+{
+   E_Client *ec;
+
+   if (!viewport) return;
+
+   ec = viewport->ec;
+
+   ecore_event_handler_del(viewport->topmost_rotate_hdl);
+
+   if (viewport->epc && viewport->query_parent_size)
+     {
+        evas_object_event_callback_del_full(viewport->epc->frame, EVAS_CALLBACK_RESIZE,
+                                            _e_comp_wl_viewport_cb_parent_resize, viewport);
+        viewport->epc = NULL;
+     }
+
+   e_client_hook_del(viewport->client_hook_del);
+   e_client_hook_del(viewport->client_hook_move);
+   e_client_hook_del(viewport->client_hook_resize);
+
+   e_comp_wl_hook_del(viewport->subsurf_hook_create);
+
+   wl_list_remove(&viewport->surface_destroy_listener.link);
+   wl_list_remove(&viewport->surface_apply_viewport_listener.link);
+
+   wl_resource_set_user_data(viewport->resource, NULL);
+
+   if (viewport->destination.mode.resource)
+     wl_resource_set_user_data(viewport->destination.mode.resource, NULL);
+
+   if (ec->comp_data && ec->comp_data->scaler.viewport)
+     {
+        ec->comp_data->scaler.viewport = NULL;
+        ec->comp_data->scaler.buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
+        ec->comp_data->scaler.buffer_viewport.surface.width = -1;
+        ec->comp_data->scaler.buffer_viewport.changed = 1;
+        ec->comp_data->pending.buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
+        ec->comp_data->pending.buffer_viewport.surface.width = -1;
+        ec->comp_data->pending.buffer_viewport.changed = 1;
+     }
+
+   PIN("tizen_viewport@%d destroy. viewport(%p)", wl_resource_get_id(viewport->resource), viewport);
+
+   free(viewport);
+}
+
+static void
+_subsurface_cb_create(void *data, E_Client *ec)
+{
+   E_Viewport *viewport = data;
+
+   if (EINA_UNLIKELY(!ec)) return;
+   if (viewport->ec != ec) return;
+
+   _e_comp_wl_viewport_parent_check(viewport);
+}
+
+static void
+_client_cb_del(void *data, E_Client *ec)
+{
+   E_Viewport *viewport = data;
+
+   if (viewport->epc != ec) return;
+
+   evas_object_event_callback_del(viewport->epc->frame, EVAS_CALLBACK_RESIZE,
+                                  _e_comp_wl_viewport_cb_parent_resize);
+   PIN("epc del");
+   viewport->epc = NULL;
+}
+
+static void
+_client_cb_move(void *data, E_Client *ec)
+{
+   E_Viewport *viewport = data;
+   E_Client *topmost = _topmost_parent_get(ec);
+
+   if (ec != topmost && ec != viewport->epc) return;
+
+   PDB("move start: topmost(%p)", topmost);
+   e_comp_wl_viewport_apply(topmost);
+   PDB("move end");
+}
+
+static void
+_client_cb_resize(void *data, E_Client *ec)
+{
+   E_Viewport *viewport = data;
+   E_Client *topmost = _topmost_parent_get(ec);
+
+   if (ec != topmost && ec != viewport->epc) return;
+
+   PDB("resize start: topmost(%p)", topmost);
+   e_comp_wl_viewport_apply(topmost);
+   PDB("resize end");
+}
+
+static void
+_e_comp_wl_viewport_parent_check(E_Viewport *viewport)
+{
+   E_Client *ec = viewport->ec;
+   E_Client *new_parent;
+
+   if (e_object_is_del(E_OBJECT(ec)) || !ec->comp_data) return;
+
+   new_parent = (ec->comp_data->sub.data) ? ec->comp_data->sub.data->parent : NULL;
+
+   if (viewport->epc == new_parent) return;
+
+   if (viewport->epc)
+     evas_object_event_callback_del(viewport->epc->frame, EVAS_CALLBACK_RESIZE,
+                                    _e_comp_wl_viewport_cb_parent_resize);
+
+   viewport->epc = new_parent;
+
+   PIN("epc(%p)", viewport->epc);
+
+   if (!viewport->epc) return;
+
+   if (viewport->query_parent_size)
+     evas_object_event_callback_add(viewport->epc->frame, EVAS_CALLBACK_RESIZE,
+                                    _e_comp_wl_viewport_cb_parent_resize, viewport);
+}
+
+static void
+_e_comp_wl_viewport_set_changed(E_Viewport *viewport)
+{
+   E_Client *ec;
+   E_Client *subc;
+   Eina_List *l;
+   E_Viewport *sub_viewport;
+
+   if (!viewport) return;
+
+   viewport->changed = EINA_TRUE;
+
+   ec = viewport->ec;
+   EINA_LIST_FOREACH(ec->comp_data->sub.list, l, subc)
+     {
+        if (!subc || !subc->comp_data || e_object_is_del(E_OBJECT(subc)))
+          continue;
+
+        sub_viewport = _e_comp_wl_viewport_get_viewport(subc->comp_data->scaler.viewport);
+        _e_comp_wl_viewport_set_changed(sub_viewport);
+     }
+
+   EINA_LIST_FOREACH(ec->comp_data->sub.below_list, l, subc)
+     {
+        if (!subc || !subc->comp_data || e_object_is_del(E_OBJECT(subc)))
+          continue;
+
+        sub_viewport = _e_comp_wl_viewport_get_viewport(subc->comp_data->scaler.viewport);
+        _e_comp_wl_viewport_set_changed(sub_viewport);
+     }
+}
+
+static void
+_e_comp_wl_destination_mode_destroy(struct wl_resource *resource)
+{
+   E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+   if (!viewport) return;
+
+   if (viewport->type == DESTINATION_TYPE_MODE)
+     viewport->type = DESTINATION_TYPE_NONE;
+
+   _e_comp_wl_viewport_set_changed(viewport);
+
+   PIN("destination.mode destroy");
+}
+
+static void
+_e_comp_wl_destination_mode_cb_destroy(struct wl_client *client,
+                                       struct wl_resource *resource)
+{
+   wl_resource_destroy(resource);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_follow_parent_transform(struct wl_client *client EINA_UNUSED,
+                                                       struct wl_resource *resource)
+{
+   E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+   if (!viewport) return;
+
+   if (viewport->follow_parent_transform)
+     return;
+
+   PIN("follow_parent_transform");
+
+   viewport->follow_parent_transform = EINA_TRUE;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_unfollow_parent_transform(struct wl_client *client EINA_UNUSED,
+                                                         struct wl_resource *resource)
+{
+   E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+   if (!viewport) return;
+
+   if (!viewport->follow_parent_transform)
+     return;
+
+   PIN("unfollow_parent_transform");
+
+   viewport->follow_parent_transform = EINA_FALSE;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_set(struct wl_client *client,
+                                   struct wl_resource *resource,
+                                   uint32_t type)
+{
+   E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+   if (!viewport) return;
+
+   if (type > TIZEN_DESTINATION_MODE_TYPE_ORIGIN_OR_LETTER)
+     {
+        PER("invalid param: type(%d)", type);
+        return;
+     }
+
+   if (type != TIZEN_DESTINATION_MODE_TYPE_NONE)
+     viewport->type = DESTINATION_TYPE_MODE;
+
+   if (viewport->destination.mode.type == type)
+     return;
+
+   PIN("type(%d)", type);
+
+   viewport->destination.mode.type = type;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_set_ratio(struct wl_client *client,
+                                         struct wl_resource *resource,
+                                         wl_fixed_t horizontal,
+                                         wl_fixed_t vertical)
+{
+   E_Viewport *viewport = wl_resource_get_user_data(resource);
+   double ratio_h, ratio_v;
+
+   if (!viewport) return;
+
+   ratio_h = wl_fixed_to_double(horizontal);
+   ratio_v = wl_fixed_to_double(vertical);
+
+   if (ratio_h == -1.0)
+     {
+        PDB("reset destinatino ratio");
+        viewport->destination.mode.ratio_h = ratio_h;
+        _e_comp_wl_viewport_set_changed(viewport);
+        return;
+     }
+
+   if (ratio_h <= 0 || ratio_v <= 0)
+     {
+        PER("invalid param: ratio_h(%.2f) ratio_v(%.2f)", ratio_h, ratio_v);
+        return;
+     }
+
+   if (viewport->destination.mode.ratio_h == ratio_h &&
+       viewport->destination.mode.ratio_v == ratio_v)
+     return;
+
+   PIN("ratio_h(%.2f) ratio_v(%.2f)", ratio_h, ratio_v);
+
+   viewport->destination.mode.ratio_h = ratio_h;
+   viewport->destination.mode.ratio_v = ratio_v;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_set_scale(struct wl_client *client,
+                                         struct wl_resource *resource,
+                                         wl_fixed_t horizontal,
+                                         wl_fixed_t vertical)
+{
+   E_Viewport *viewport = wl_resource_get_user_data(resource);
+   double scale_h, scale_v;
+
+   if (!viewport) return;
+
+   scale_h = wl_fixed_to_double(horizontal);
+   scale_v = wl_fixed_to_double(vertical);
+
+   if (scale_h == -1.0)
+     {
+        PDB("reset destinatino scale");
+        viewport->destination.mode.scale_h = scale_h;
+        _e_comp_wl_viewport_set_changed(viewport);
+        return;
+     }
+
+   if (scale_h <= 0 || scale_v <= 0)
+     {
+        PER("invalid param: scale_h(%.2f) scale_v(%.2f)", scale_h, scale_v);
+        return;
+     }
+
+   if (viewport->destination.mode.scale_h == scale_h &&
+       viewport->destination.mode.scale_v == scale_v)
+     return;
+
+   PIN("scale_h(%.2f) scale_v(%.2f)", scale_h, scale_v);
+
+   viewport->destination.mode.scale_h = scale_h;
+   viewport->destination.mode.scale_v = scale_v;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_set_align(struct wl_client *client,
+                                         struct wl_resource *resource,
+                                         wl_fixed_t horizontal,
+                                         wl_fixed_t vertical)
+{
+   E_Viewport *viewport = wl_resource_get_user_data(resource);
+   double align_h, align_v;
+
+   if (!viewport) return;
+
+   align_h = wl_fixed_to_double(horizontal);
+   align_v = wl_fixed_to_double(vertical);
+
+   if (align_h == -1.0)
+     {
+        PDB("reset destinatino align");
+        viewport->destination.mode.align_h = align_h;
+        _e_comp_wl_viewport_set_changed(viewport);
+        return;
+     }
+
+   if (align_h < 0.0)
+     align_h = 0.0;
+   else if (align_h > 1.0)
+     align_h = 1.0;
+
+   if (align_v < 0.0)
+     align_v = 0.0;
+   else if (align_v > 1.0)
+     align_v = 1.0;
+
+   if (viewport->destination.mode.align_h == align_h &&
+       viewport->destination.mode.align_v == align_v)
+     return;
+
+   PIN("align_h(%.2f) align_v(%.2f)", align_h, align_v);
+
+   viewport->destination.mode.align_h = align_h;
+   viewport->destination.mode.align_v = align_v;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_set_offset(struct wl_client *client,
+                                          struct wl_resource *resource,
+                                          int32_t x,
+                                          int32_t y,
+                                          int32_t w,
+                                          int32_t h)
+{
+   E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+   if (!viewport) return;
+
+   if (viewport->destination.mode.offset_x == x &&
+       viewport->destination.mode.offset_y == y &&
+       viewport->destination.mode.offset_w == w &&
+       viewport->destination.mode.offset_h == h)
+     return;
+
+   PIN("offset_x(%d) offset_y(%d) offset_w(%d) offset_h(%d)", x, y, w, h);
+
+   viewport->destination.mode.offset_x = x;
+   viewport->destination.mode.offset_y = y;
+   viewport->destination.mode.offset_w = w;
+   viewport->destination.mode.offset_h = h;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static const struct tizen_destination_mode_interface _e_comp_wl_destination_mode_interface =
+{
+   _e_comp_wl_destination_mode_cb_destroy,
+   _e_comp_wl_destination_mode_cb_follow_parent_transform,
+   _e_comp_wl_destination_mode_cb_unfollow_parent_transform,
+   _e_comp_wl_destination_mode_cb_set,
+   _e_comp_wl_destination_mode_cb_set_ratio,
+   _e_comp_wl_destination_mode_cb_set_scale,
+   _e_comp_wl_destination_mode_cb_set_align,
+   _e_comp_wl_destination_mode_cb_set_offset,
+};
+
+static void
+_e_comp_wl_viewport_cb_parent_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   E_Viewport *viewport = data;
+   Evas_Coord old_w, old_h;
+
+   if (e_object_is_del(E_OBJECT(viewport->epc))) return;
+
+   old_w = viewport->parent_size.w;
+   old_h = viewport->parent_size.h;
+
+   evas_object_geometry_get(viewport->epc->frame,
+                            &viewport->parent_size.x, &viewport->parent_size.y,
+                            &viewport->parent_size.w, &viewport->parent_size.h);
+
+   if (old_w != viewport->parent_size.w || old_h != viewport->parent_size.h)
+     tizen_viewport_send_parent_size(viewport->resource,
+                                     viewport->parent_size.w,
+                                     viewport->parent_size.h);
+}
+
+static void
+_e_comp_wl_viewport_destroy(struct wl_resource *resource)
+{
+   E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+
+   if (!viewport) return;
+
+   _destroy_viewport(viewport);
+}
+
+static void
+_e_comp_wl_viewport_cb_destroy(struct wl_client *client EINA_UNUSED,
+                               struct wl_resource *resource)
+{
+   wl_resource_destroy(resource);
+}
+
+static void
+_e_comp_wl_viewport_cb_set_transform(struct wl_client *client EINA_UNUSED,
+                                     struct wl_resource *resource,
+                                     uint32_t transform)
+{
+   E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+
+   if (!viewport) return;
+
+   if (transform > WL_OUTPUT_TRANSFORM_FLIPPED_270)
+     {
+        PER("invalid param: transform(%d)", transform);
+        return;
+     }
+
+   _e_comp_wl_viewport_parent_check(viewport);
+
+   if (viewport->transform == transform)
+     return;
+
+   PIN("transform(%d)", transform);
+
+   viewport->transform = transform;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_viewport_cb_set_source(struct wl_client *client EINA_UNUSED,
+                                  struct wl_resource *resource,
+                                  uint32_t x,
+                                  uint32_t y,
+                                  uint32_t width,
+                                  uint32_t height)
+{
+   E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+
+   if (!viewport) return;
+
+   _e_comp_wl_viewport_parent_check(viewport);
+
+   if (viewport->source.x == x && viewport->source.y == y &&
+       viewport->source.w == width && viewport->source.h == height)
+     return;
+
+   viewport->source.x = x;
+   viewport->source.y = y;
+   viewport->source.w = width;
+   viewport->source.h = height;
+   viewport->cropped_source = viewport->source;
+   _e_comp_wl_viewport_set_changed(viewport);
+
+   PIN("source(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(&viewport->source));
+}
+
+static void
+_e_comp_wl_viewport_cb_set_destination(struct wl_client *client EINA_UNUSED,
+                                       struct wl_resource *resource,
+                                       int32_t x,
+                                       int32_t y,
+                                       uint32_t width,
+                                       uint32_t height)
+{
+   E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+
+   if (!viewport) return;
+
+   if (width == 0 || height == 0)
+     {
+        PER("invalid param: destination.rect(%d,%d %dx%d)", x, y, width, height);
+        return;
+     }
+
+   _e_comp_wl_viewport_parent_check(viewport);
+
+   viewport->type = DESTINATION_TYPE_RECT;
+
+   if (viewport->destination.rect.x == x && viewport->destination.rect.y == y &&
+       viewport->destination.rect.w == width && viewport->destination.rect.h == height)
+     return;
+
+   PIN("destination.rect(%d,%d %dx%d)", x, y, width, height);
+
+   viewport->destination.rect.x = x;
+   viewport->destination.rect.y = y;
+   viewport->destination.rect.w = width;
+   viewport->destination.rect.h = height;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_viewport_cb_set_destination_ratio(struct wl_client *client EINA_UNUSED,
+                                             struct wl_resource *resource,
+                                             wl_fixed_t x,
+                                             wl_fixed_t y,
+                                             wl_fixed_t width,
+                                             wl_fixed_t height)
+{
+   E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+   double ratio_x, ratio_y, ratio_w, ratio_h;
+
+   if (!viewport) return;
+
+   if (viewport->type == DESTINATION_TYPE_MODE)
+     {
+        PER("couldn't set viewport destination ratio. tizen_viewport@%d has the mode",
+            wl_resource_get_id(resource));
+        return;
+     }
+
+   _e_comp_wl_viewport_parent_check(viewport);
+
+   ratio_x = (viewport->epc) ? wl_fixed_to_double(x) : 0;
+   ratio_y = (viewport->epc) ? wl_fixed_to_double(y) : 0;
+   ratio_w = wl_fixed_to_double(width);
+   ratio_h = wl_fixed_to_double(height);
+
+   if (ratio_x < 0 || ratio_x >= 1 || ratio_y < 0 || ratio_y >= 1 || ratio_w <= 0 || ratio_h <= 0)
+     {
+        PER("invalid param: destination.ratio(%.2f,%.2f %.2fx%.2f)", ratio_x, ratio_y, ratio_w, ratio_h);
+        return;
+     }
+
+   viewport->type = DESTINATION_TYPE_RATIO;
+
+   if (viewport->destination.ratio.x == ratio_x && viewport->destination.ratio.y == ratio_y &&
+       viewport->destination.ratio.w == ratio_w && viewport->destination.ratio.h == ratio_h)
+     return;
+
+   PIN("destination.ratio(%.2f,%.2f %.2fx%.2f)", ratio_x, ratio_y, ratio_w, ratio_h);
+
+   viewport->destination.ratio.x = ratio_x;
+   viewport->destination.ratio.y = ratio_y;
+   viewport->destination.ratio.w = ratio_w;
+   viewport->destination.ratio.h = ratio_h;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+static void
+_e_comp_wl_viewport_cb_get_destination_mode(struct wl_client *client,
+                                            struct wl_resource *resource,
+                                            uint32_t id)
+{
+   int version = wl_resource_get_version(resource);
+   E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+   struct wl_resource *res;
+
+   if (!viewport)
+     {
+        wl_resource_post_error(resource,
+                               WL_DISPLAY_ERROR_INVALID_OBJECT,
+                               "tizen_viewport@%d is invalid",
+                               wl_resource_get_id(resource));
+        return;
+     }
+
+   _e_comp_wl_viewport_parent_check(viewport);
+
+   if (!(res = wl_resource_create(client, &tizen_destination_mode_interface, version, id)))
+     {
+        PER("Failed to create destination_mode resource");
+        wl_resource_post_no_memory(resource);
+        return;
+     }
+
+   memset(&viewport->destination.mode, 0, sizeof viewport->destination.mode);
+
+   PIN("destination.mode created");
+
+   viewport->destination.mode.resource = res;
+   viewport->destination.mode.type = TIZEN_DESTINATION_MODE_TYPE_NONE;
+   viewport->destination.mode.ratio_h = -1.0;
+   viewport->destination.mode.scale_h = -1.0;
+   viewport->destination.mode.align_h = -1.0;
+
+   /* set resource implementation */
+   wl_resource_set_implementation(res, &_e_comp_wl_destination_mode_interface,
+                                  viewport, _e_comp_wl_destination_mode_destroy);
+
+}
+
+static void
+_e_comp_wl_viewport_cb_query_parent_size(struct wl_client *client,
+                                         struct wl_resource *resource)
+{
+   E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+   Evas_Coord w = 0, h = 0;
+
+   if (!viewport) return;
+
+   _e_comp_wl_viewport_parent_check(viewport);
+
+   if (viewport->epc)
+     {
+        evas_object_geometry_get(viewport->epc->frame,
+                                 &viewport->parent_size.x, &viewport->parent_size.y,
+                                 &viewport->parent_size.w, &viewport->parent_size.h);
+        w = viewport->parent_size.w;
+        h = viewport->parent_size.h;
+
+        if (!viewport->query_parent_size)
+          {
+             viewport->query_parent_size = EINA_TRUE;
+             evas_object_event_callback_add(viewport->epc->frame, EVAS_CALLBACK_RESIZE,
+                                            _e_comp_wl_viewport_cb_parent_resize, viewport);
+          }
+     }
+   else
+     {
+        E_Zone *zone = e_comp_zone_xy_get(viewport->ec->x, viewport->ec->y);
+        if (zone)
+          {
+             w = zone->w;
+             h = zone->h;
+          }
+        else
+          PWR("out of zone");
+     }
+
+   tizen_viewport_send_parent_size(resource, w, h);
+}
+
+static void
+_e_comp_wl_viewport_cb_follow_parent_transform(struct wl_client *client EINA_UNUSED,
+                                               struct wl_resource *resource)
+{
+   E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+   if (!viewport) return;
+
+   _e_comp_wl_viewport_parent_check(viewport);
+
+   if (viewport->follow_parent_transform)
+     return;
+
+   PIN("follow_parent_transform");
+
+   viewport->follow_parent_transform = EINA_TRUE;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_viewport_cb_unfollow_parent_transform(struct wl_client *client EINA_UNUSED,
+                                                 struct wl_resource *resource)
+{
+   E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+   if (!viewport) return;
+
+   _e_comp_wl_viewport_parent_check(viewport);
+
+   if (!viewport->follow_parent_transform)
+     return;
+
+   PIN("unfollow_parent_transform");
+
+   viewport->follow_parent_transform = EINA_FALSE;
+   _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static const struct tizen_viewport_interface _e_comp_wl_viewport_interface =
+{
+   _e_comp_wl_viewport_cb_destroy,
+   _e_comp_wl_viewport_cb_set_transform,
+   _e_comp_wl_viewport_cb_set_source,
+   _e_comp_wl_viewport_cb_set_destination,
+   _e_comp_wl_viewport_cb_set_destination_ratio,
+   _e_comp_wl_viewport_cb_get_destination_mode,
+   _e_comp_wl_viewport_cb_query_parent_size,
+   _e_comp_wl_viewport_cb_follow_parent_transform,
+   _e_comp_wl_viewport_cb_unfollow_parent_transform,
+};
+
+static void
+_source_transform_coord(int width, int height, int trans, int scale, float ox, float oy, float *tx, float *ty)
+{
+   switch (trans)
+     {
+      case WL_OUTPUT_TRANSFORM_NORMAL:
+      default:
+         *tx = ox, *ty = oy;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED:
+         *tx = width - ox, *ty = oy;
+         break;
+      case WL_OUTPUT_TRANSFORM_90:
+         *tx = oy, *ty = width - ox;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+         *tx = height - oy, *ty = width - ox;
+         break;
+      case WL_OUTPUT_TRANSFORM_180:
+         *tx = width - ox, *ty = height - oy;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+         *tx = ox, *ty = height - oy;
+         break;
+      case WL_OUTPUT_TRANSFORM_270:
+         *tx = height - oy, *ty = ox;
+         break;
+      case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+         *tx = oy, *ty = ox;
+         break;
+     }
+
+   *tx /= scale;
+   *ty /= scale;
+}
+
+static void
+_source_transform_to_surface(int width, int height, int trans, int scale,
+                             Eina_Rectangle *orig, Eina_Rectangle *tran)
+{
+   float x1, x2, y1, y2;
+
+   _source_transform_coord(width, height, trans, scale, orig->x, orig->y, &x1, &y1);
+   _source_transform_coord(width, height, trans, scale, orig->x + orig->w, orig->y + orig->h, &x2, &y2);
+
+   tran->x = (x1 <= x2) ? x1 : x2;
+   tran->w = (x1 <= x2) ? x2 - x1 : x1 - x2;
+   tran->y = (y1 <= y2) ? y1 : y2;
+   tran->h = (y1 <= y2) ? y2 - y1 : y1 - y2;
+}
+
+static void
+_destination_mode_calculate_letter_box(int pw, int ph, int sw, int sh,
+                                       double rh, double rv,
+                                       Eina_Rectangle *rect)
+{
+   int fit_width;
+   int fit_height;
+   double fw, fh, ssw, ssh, max;
+
+   ssw = sw;
+   if (rh != -1.0)
+     ssh = (double)sw * rv / rh;
+   else
+     ssh = sh;
+
+   fw = ssw / pw;
+   fh = ssh / ph;
+   max = MAX(fw, fh);
+
+   fit_width = ssw / max;
+   fit_height = ssh / max;
+
+   rect->x = (pw - fit_width) / 2;
+   rect->y = (ph - fit_height) / 2;
+   rect->w = fit_width;
+   rect->h = fit_height;
+}
+
+static void
+_destination_mode_calculate_origin(int pw, int ph, int sw, int sh,
+                                   double rh, double rv,
+                                   Eina_Rectangle *rect)
+{
+   rect->x = (pw - sw) / 2;
+   rect->y = (ph - sh) / 2;
+   rect->w = sw;
+   rect->h = sh;
+}
+
+static void
+_destination_mode_calculate_full(int pw, int ph, int sw, int sh,
+                                 double rh, double rv,
+                                 Eina_Rectangle *rect)
+{
+   rect->x = rect->y = 0;
+   rect->w = pw;
+   rect->h = ph;
+}
+
+static void
+_destination_mode_calculate_cropped_full(int pw, int ph, int sw, int sh,
+                                         double rh, double rv,
+                                         Eina_Rectangle *rect)
+{
+   int fit_width;
+   int fit_height;
+   double fw, fh, ssw, ssh, min;
+
+   ssw = sw;
+   if (rh != -1.0)
+     ssh = (double)sw * rv / rh;
+   else
+     ssh = sh;
+
+   fw = ssw / pw;
+   fh = ssh / ph;
+   min = MIN(fw, fh);
+
+   fit_width = ssw / min;
+   fit_height = ssh / min;
+
+   rect->x = (pw - fit_width) / 2;
+   rect->y = (ph - fit_height) / 2;
+   rect->w = fit_width;
+   rect->h = fit_height;
+}
+
+static void
+_destination_mode_calculate_origin_or_letter(int pw, int ph, int sw, int sh,
+                                             double rh, double rv,
+                                             Eina_Rectangle *rect)
+{
+   if (sw < pw && sh < ph)
+     _destination_mode_calculate_origin(pw, ph, sw, sh, rh, rv, rect);
+   else
+     _destination_mode_calculate_letter_box(pw, ph, sw, sh, rh, rv, rect);
+}
+
+/* we have to consider the output transform. if epc is toplevel, and if
+ * output transform is 3, and if vpp->buffer.transform is 3, then actual
+ * epc's transform is 0.
+ */
+static int
+_get_parent_transform(E_Viewport *viewport)
+{
+   E_Client *epc = viewport->epc;
+   E_Client *topmost;
+   unsigned int ptran, pflip;
+   int ptransform;
+
+   if (!epc->comp_data || e_object_is_del(E_OBJECT(epc)))
+     return 0;
+
+   ptransform = e_comp_wl_output_buffer_transform_get(epc);
+
+   topmost = _topmost_parent_get(epc);
+
+   if (ptransform != 0 && epc == topmost)
+     {
+        E_Comp_Wl_Output *output = e_comp_wl_output_find(topmost);
+
+        EINA_SAFETY_ON_NULL_RETURN_VAL(output, 0);
+
+        ptran = ptransform & 0x3;
+        pflip = ptransform & 0x4;
+        ptransform = pflip + (4 + ptran - output->transform) % 4;
+     }
+
+   return ptransform;
+}
+
+static Eina_Bool
+_destination_mode_calculate_destination(E_Viewport *viewport, Eina_Rectangle *prect, Eina_Rectangle *rect)
+{
+   E_Client *ec = viewport->ec;
+   int sw = 0, sh = 0, transform;
+   double rh = -1.0, rv = -1.0;
+
+   if (viewport->source.w != -1)
+     {
+        sw = viewport->source.w;
+        sh = viewport->source.h;
+     }
+   else
+     e_comp_wl_video_buffer_size_get(ec, &sw, &sh);
+
+   transform = e_comp_wl_output_buffer_transform_get(ec);
+
+   if (transform % 2)
+     SWAP(sw, sh);
+
+   PDB("parent(%dx%d) src(%dx%d)", prect->w, prect->h, sw, sh);
+
+   /* ratio -> type -> scale -> offset -> align */
+   if (viewport->destination.mode.ratio_h != -1.0)
+     {
+        if (transform % 2)
+          {
+             rh = viewport->destination.mode.ratio_v;
+             rv = viewport->destination.mode.ratio_h;
+          }
+        else
+          {
+             rh = viewport->destination.mode.ratio_h;
+             rv = viewport->destination.mode.ratio_v;
+          }
+     }
+
+   PDB("%dx%d %dx%d %.2fx%.2f (%d,%d %dx%d)", prect->w, prect->h, sw, sh, rh, rv, EINA_RECTANGLE_ARGS(rect));
+
+   switch(viewport->destination.mode.type)
+     {
+      case TIZEN_DESTINATION_MODE_TYPE_LETTER_BOX:
+         _destination_mode_calculate_letter_box(prect->w, prect->h, sw, sh, rh, rv, rect);
+         break;
+      case TIZEN_DESTINATION_MODE_TYPE_ORIGIN:
+         _destination_mode_calculate_origin(prect->w, prect->h, sw, sh, rh, rv, rect);
+         break;
+      case TIZEN_DESTINATION_MODE_TYPE_FULL:
+         _destination_mode_calculate_full(prect->w, prect->h, sw, sh, rh, rv, rect);
+         break;
+      case TIZEN_DESTINATION_MODE_TYPE_CROPPED_FULL:
+         _destination_mode_calculate_cropped_full(prect->w, prect->h, sw, sh, rh, rv, rect);
+         break;
+      case TIZEN_DESTINATION_MODE_TYPE_ORIGIN_OR_LETTER:
+         _destination_mode_calculate_origin_or_letter(prect->w, prect->h, sw, sh, rh, rv, rect);
+         break;
+      case TIZEN_DESTINATION_MODE_TYPE_NONE:
+      default:
+         PER("no destination mode for tizen_viewport@%d", wl_resource_get_id(viewport->resource));
+         return EINA_FALSE;
+     }
+
+   PDB("(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(rect));
+
+   if (viewport->destination.mode.scale_h != -1.0)
+     {
+        int new_x, new_y, new_w, new_h;
+        double h = viewport->destination.mode.scale_h;
+        double v = viewport->destination.mode.scale_v;
+
+        if (transform % 2)
+          SWAP(h, v);
+
+        new_w = rect->w * h;
+        new_h = rect->h * v;
+        new_x = rect->x + (rect->w - new_w) / 2;
+        new_y = rect->y + (rect->h - new_h) / 2;
+        rect->x = new_x;
+        rect->y = new_y;
+        rect->w = new_w;
+        rect->h = new_h;
+     }
+
+   PDB("(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(rect));
+
+   if (viewport->destination.mode.align_h != -1.0)
+     {
+        E_Client *epc = viewport->epc;
+        double h = viewport->destination.mode.align_h;
+        double v = viewport->destination.mode.align_v;
+        int dx, dy;
+
+        if (epc)
+          {
+             int ptransform;
+
+             if (!epc->comp_data || e_object_is_del(E_OBJECT(epc)))
+               return EINA_FALSE;
+
+             ptransform = e_comp_wl_output_buffer_transform_get(epc);
+
+             PDB("parent's transform(%d)", ptransform);
+
+             switch (ptransform)
+               {
+                default:
+                case WL_OUTPUT_TRANSFORM_NORMAL:
+                   break;
+                case WL_OUTPUT_TRANSFORM_90:
+                   SWAP(h, v);
+                   v = 1.0 - v;
+                   break;
+                case WL_OUTPUT_TRANSFORM_180:
+                   h = 1.0 - h;
+                   v = 1.0 - v;
+                   break;
+                case WL_OUTPUT_TRANSFORM_270:
+                   SWAP(h, v);
+                   h = 1.0 - h;
+                   break;
+                case WL_OUTPUT_TRANSFORM_FLIPPED:
+                   h = 1.0 - h;
+                   break;
+                case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+                   SWAP(h, v);
+                   h = 1.0 - h;
+                   v = 1.0 - v;
+                   break;
+                   break;
+                case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+                   v = 1.0 - v;
+                   break;
+                case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+                   SWAP(h, v);
+                   break;
+               }
+          }
+
+        dx = (prect->w - rect->w) * (h - 0.5);
+        dy = (prect->h - rect->h) * (v - 0.5);
+
+        rect->x += dx;
+        rect->y += dy;
+     }
+
+   PDB("(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(rect));
+
+   if (viewport->destination.mode.offset_x != 0 ||
+       viewport->destination.mode.offset_y != 0 ||
+       viewport->destination.mode.offset_w != 0 ||
+       viewport->destination.mode.offset_h != 0)
+     {
+        int x = viewport->destination.mode.offset_x;
+        int y = viewport->destination.mode.offset_y;
+        int w = viewport->destination.mode.offset_w;
+        int h = viewport->destination.mode.offset_h;
+
+        if (transform % 2)
+          {
+             SWAP(x, y);
+             SWAP(w, h);
+          }
+
+        rect->x += x;
+        rect->y += y;
+        rect->w += w;
+        rect->h += h;
+     }
+
+   PDB("mode destination(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(rect));
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_comp_wl_viewport_calculate_destination(E_Viewport *viewport, Eina_Rectangle *prect, Eina_Rectangle *rect)
+{
+   switch (viewport->type)
+     {
+      case DESTINATION_TYPE_RECT:
+         rect->x = viewport->destination.rect.x;
+         rect->y = viewport->destination.rect.y;
+         rect->w = viewport->destination.rect.w;
+         rect->h = viewport->destination.rect.h;
+         break;
+      case DESTINATION_TYPE_RATIO:
+         rect->x = viewport->destination.ratio.x * prect->w;
+         rect->y = viewport->destination.ratio.y * prect->h;
+         rect->w = viewport->destination.ratio.w * prect->w;
+         rect->h = viewport->destination.ratio.h * prect->h;
+         break;
+      case DESTINATION_TYPE_MODE:
+      case DESTINATION_TYPE_NONE:
+      default:
+         PER("wrong destination type: %d", viewport->type);
+         return EINA_FALSE;
+     }
+
+   if (viewport->epc)
+     {
+        int ptransform = _get_parent_transform(viewport);
+
+        if (ptransform > 0)
+          {
+             if (ptransform % 2)
+               _source_transform_to_surface(prect->h, prect->w, ptransform, 1, rect, rect);
+             else
+               _source_transform_to_surface(prect->w, prect->h, ptransform, 1, rect, rect);
+          }
+     }
+
+   PDB("destination(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(rect));
+
+   return EINA_TRUE;
+}
+
+static void
+_e_comp_wl_viewport_crop_by_parent(E_Viewport *viewport, Eina_Rectangle *parent, Eina_Rectangle *dst)
+{
+   E_Comp_Wl_Buffer_Viewport *vp = &viewport->ec->comp_data->scaler.buffer_viewport;
+   Eina_Rectangle crop;
+   double rx, ry, rw, rh;
+   int bw, bh;
+
+   PDB("dst(%d,%d %dx%d) parent(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(dst), EINA_RECTANGLE_ARGS(parent));
+
+   crop = *dst;
+
+   if (!eina_rectangle_intersection(&crop, parent))
+     {
+        *dst = crop;
+        PDB("dst(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(dst));
+        return;
+     }
+
+   if (crop.w == dst->w && crop.h == dst->h)
+     {
+        if (viewport->source.w == -1)
+          {
+             e_comp_wl_video_buffer_size_get(viewport->ec, &bw, &bh);
+
+             viewport->cropped_source.x = viewport->cropped_source.y = 0;
+             viewport->cropped_source.w = bw;
+             viewport->cropped_source.h = bh;
+          }
+        else
+          viewport->cropped_source = viewport->source;
+
+        PDB("src(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(&viewport->cropped_source));
+
+        return;
+     }
+
+   PDB("dst(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(dst));
+
+   crop.x -= dst->x;
+   crop.y -= dst->y;
+
+   rx = (double)crop.x / dst->w;
+   ry = (double)crop.y / dst->h;
+   rw = (double)crop.w / dst->w;
+   rh = (double)crop.h / dst->h;
+
+   crop.x += dst->x;
+   crop.y += dst->y;
+   *dst = crop;
+
+   PDB("  => (%d,%d %dx%d)", EINA_RECTANGLE_ARGS(dst));
+
+   e_comp_wl_video_buffer_size_get(viewport->ec, &bw, &bh);
+
+   if (viewport->source.w == -1)
+     {
+        viewport->cropped_source.x = viewport->cropped_source.y = 0;
+        viewport->cropped_source.w = bw;
+        viewport->cropped_source.h = bh;
+     }
+   else
+     viewport->cropped_source = viewport->source;
+
+   PDB("src(%d,%d %dx%d) ratio(%.2f,%.2f,%.2f,%.2f)",
+       EINA_RECTANGLE_ARGS(&viewport->cropped_source), rx, ry, rw, rh);
+
+   viewport->cropped_source.x += viewport->cropped_source.w * rx;
+   viewport->cropped_source.y += viewport->cropped_source.h * ry;
+   viewport->cropped_source.w = viewport->cropped_source.w * rw;
+   viewport->cropped_source.h = viewport->cropped_source.h * rh;
+
+   _source_transform_to_surface(bw, bh,
+                                e_comp_wl_output_buffer_transform_get(viewport->ec), 1,
+                                &viewport->cropped_source, &viewport->cropped_source);
+
+   vp->buffer.src_x = wl_fixed_from_int(viewport->cropped_source.x);
+   vp->buffer.src_y = wl_fixed_from_int(viewport->cropped_source.y);
+   vp->buffer.src_width = wl_fixed_from_int(viewport->cropped_source.w);
+   vp->buffer.src_height = wl_fixed_from_int(viewport->cropped_source.h);
+
+   PDB("  => (%d,%d %dx%d)", EINA_RECTANGLE_ARGS(&viewport->cropped_source));
+}
+
+static Eina_Bool
+_e_comp_wl_viewport_apply_transform(E_Viewport *viewport, int *rtransform)
+{
+   E_Client *ec = viewport->ec;
+   E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+   Eina_Bool changed = EINA_FALSE;
+   unsigned int new_transform;
+
+   new_transform = viewport->transform;
+
+   if (viewport->follow_parent_transform && viewport->epc)
+     {
+        E_Client *epc = viewport->epc;
+        unsigned int pwtran, ptran, pflip, ctran, cflip;
+        int ptransform;
+
+        if (!epc->comp_data || e_object_is_del(E_OBJECT(epc)))
+          {
+             *rtransform = vp->buffer.transform;
+             return EINA_FALSE;
+          }
+
+        ptransform = _get_parent_transform(viewport);
+        PDB("parent's transform(%d) rot.ang.curr(%d)", ptransform, epc->e.state.rot.ang.curr/90);
+
+        pwtran = ((epc->e.state.rot.ang.curr + 360) % 360) / 90;
+
+        ptran = ((pwtran & 0x3) + (ptransform & 0x3)) & 0x3;
+        pflip = (ptransform & 0x4);
+
+        ctran = (viewport->transform & 0x3);
+        cflip = (viewport->transform & 0x4);
+
+        new_transform = ((ptran + ctran) & 0x3) + ((pflip + cflip) & 0x4);
+     }
+
+   if (new_transform != vp->buffer.transform)
+     {
+        vp->buffer.transform = new_transform;
+        vp->changed = changed = EINA_TRUE;
+
+        ec->comp_data->pending.buffer_viewport = *vp;
+        if (ec->comp_data->sub.data)
+          ec->comp_data->sub.data->cached.buffer_viewport = *vp;
+     }
+
+   if (changed)
+     PIN("apply transform: %d type(%d) follow(%d) changed(%d)",
+         vp->buffer.transform, viewport->type,
+         viewport->follow_parent_transform, changed);
+
+   *rtransform = vp->buffer.transform;
+
+   return changed;
+}
+
+static Eina_Bool
+_e_comp_wl_viewport_apply_destination(E_Viewport *viewport, Eina_Rectangle *rrect)
+{
+   E_Client *ec = viewport->ec;
+   E_Comp_Wl_Buffer_Viewport *vp;
+   Eina_Rectangle dst = {0,}, prect;
+   Eina_Bool changed = EINA_FALSE;
+
+   vp = &ec->comp_data->scaler.buffer_viewport;
+   if (!viewport->epc)
+     {
+        E_Zone *zone = e_comp_zone_xy_get(ec->x, ec->y);
+
+        EINA_SAFETY_ON_FALSE_RETURN_VAL(zone != NULL, EINA_FALSE);
+
+        prect.x = prect.y = 0;
+        prect.w = zone->w;
+        prect.h = zone->h;
+     }
+   else
+     {
+        E_Client *epc = viewport->epc;
+        E_Comp_Wl_Buffer_Viewport *vpp;
+
+        if (!epc->comp_data || e_object_is_del(E_OBJECT(epc)))
+          return EINA_FALSE;
+
+        vpp = &epc->comp_data->scaler.buffer_viewport;
+        prect.x = prect.y = 0;
+        if (vpp->surface.width != -1)
+          {
+             prect.w = vpp->surface.width;
+             prect.h = vpp->surface.height;
+          }
+        else
+          e_comp_wl_video_buffer_transform_scale_size_get(epc, &prect.w, &prect.h);
+     }
+
+   if (!(prect.w > 0 && prect.h > 0))
+     {
+        PWR("prect.w > 0 && prect.h > 0 is false");
+        return EINA_FALSE;
+     }
+
+   switch (viewport->type)
+     {
+      case DESTINATION_TYPE_RECT:
+      case DESTINATION_TYPE_RATIO:
+         if (!_e_comp_wl_viewport_calculate_destination(viewport, &prect, &dst))
+           return EINA_FALSE;
+         break;
+      case DESTINATION_TYPE_MODE:
+         if (!_destination_mode_calculate_destination(viewport, &prect, &dst))
+           return EINA_FALSE;
+         break;
+      case DESTINATION_TYPE_NONE:
+      default:
+         PER("no destination for tizen_viewport@%d", wl_resource_get_id(viewport->resource));
+         return EINA_FALSE;
+     }
+
+   _e_comp_wl_viewport_crop_by_parent(viewport, &prect, &dst);
+
+   /* The values of below x, y, w, h are specified in the transform 0 and in the parent */
+   if (ec->comp_data->sub.data)
+     {
+        if (ec->comp_data->sub.data->position.x != dst.x ||
+            ec->comp_data->sub.data->position.y != dst.y)
+          {
+             ec->comp_data->sub.data->position.x = dst.x;
+             ec->comp_data->sub.data->position.y = dst.y;
+             ec->comp_data->sub.data->position.set = EINA_TRUE;
+             vp->changed = changed = EINA_TRUE;
+          }
+     }
+   else
+     {
+        /* if toplevel surface, the x,y pos is decided by shell surface */
+        dst.x = ec->x;
+        dst.y = ec->y;
+     }
+
+   if (vp->surface.width != dst.w || vp->surface.height != dst.h)
+     {
+        vp->surface.width = dst.w;
+        vp->surface.height = dst.h;
+        vp->changed = changed = EINA_TRUE;
+
+        ec->comp_data->pending.buffer_viewport = *vp;
+        if (ec->comp_data->sub.data)
+          ec->comp_data->sub.data->cached.buffer_viewport = *vp;
+     }
+
+   *rrect = dst;
+
+   if (changed)
+     PIN("apply destination: %d,%d %dx%d changed(%d)", EINA_RECTANGLE_ARGS(&dst), changed);
+
+   return changed;
+}
+
+static Eina_Bool
+_e_comp_wl_viewport_apply_source(E_Viewport *viewport)
+{
+   E_Client *ec = viewport->ec;
+   E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+   Eina_Rectangle rect = {0,};
+   int bw = 0, bh = 0;
+   wl_fixed_t fx, fy, fw, fh;
+   Eina_Bool changed = EINA_FALSE;
+
+   if (viewport->cropped_source.w == -1)
+     return EINA_FALSE;
+
+   e_comp_wl_video_buffer_size_get(ec, &bw, &bh);
+
+   rect.w = bw;
+   rect.h = bh;
+
+   if (!eina_rectangle_intersection(&rect, &viewport->cropped_source))
+     {
+        PWR("source area is empty");
+        return EINA_FALSE;
+     }
+
+   _source_transform_to_surface(bw, bh,
+                                e_comp_wl_output_buffer_transform_get(ec), 1,
+                                &rect, &rect);
+
+   fx = wl_fixed_from_int(rect.x);
+   fy = wl_fixed_from_int(rect.y);
+   fw = wl_fixed_from_int(rect.w);
+   fh = wl_fixed_from_int(rect.h);
+
+   if (vp->buffer.src_x != fx || vp->buffer.src_y != fy ||
+       vp->buffer.src_width != fw || vp->buffer.src_height != fh)
+     {
+        vp->buffer.src_x = wl_fixed_from_int(rect.x);
+        vp->buffer.src_y = wl_fixed_from_int(rect.y);
+        vp->buffer.src_width = wl_fixed_from_int(rect.w);
+        vp->buffer.src_height = wl_fixed_from_int(rect.h);
+        vp->changed = changed = EINA_TRUE;
+
+        ec->comp_data->pending.buffer_viewport = *vp;
+        if (ec->comp_data->sub.data)
+          ec->comp_data->sub.data->cached.buffer_viewport = *vp;
+     }
+
+   if (changed)
+     PDB("apply source: %d,%d %dx%d orig(%d,%d %dx%d) changed(%d)",
+         EINA_RECTANGLE_ARGS(&rect), EINA_RECTANGLE_ARGS(&viewport->cropped_source), changed);
+
+   return changed;
+}
+
+EINTERN Eina_Bool
+e_comp_wl_viewport_apply(E_Client *ec)
+{
+   E_Viewport *viewport;
+   E_Client *subc;
+   Eina_List *l;
+
+   if (!ec || !ec->comp_data || e_object_is_del(E_OBJECT(ec)))
+     return EINA_FALSE;
+
+   viewport = _e_comp_wl_viewport_get_viewport(ec->comp_data->scaler.viewport);
+
+   if (viewport)
+     _e_comp_wl_viewport_parent_check(viewport);
+
+   if (viewport && ec->comp_data->buffer_ref.buffer)
+     {
+        E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+        Eina_Bool changed = EINA_FALSE, src_changed = EINA_FALSE;
+        Eina_Rectangle rrect = {0,};
+        int rtransform = 0;
+
+        /* evas map follow screen coordinates. so all information including the
+         * transform and destination also should follow screen coordinates.
+         */
+        changed |= _e_comp_wl_viewport_apply_transform(viewport, &rtransform);
+        changed |= _e_comp_wl_viewport_apply_destination(viewport, &rrect);
+        src_changed |= _e_comp_wl_viewport_apply_source(viewport);
+
+        viewport->changed = EINA_FALSE;
+
+        PDB("changed(%d) src_changed(%d)", changed, src_changed);
+
+        if (changed || src_changed)
+          {
+             e_comp_wl_map_size_cal_from_buffer(viewport->ec);
+             e_comp_wl_map_size_cal_from_viewport(viewport->ec);
+             e_comp_wl_map_apply(viewport->ec);
+
+             if (changed)
+               {
+                  PIN("send destination_changed: transform(%d) x(%d) y(%d) w(%d) h(%d)",
+                      rtransform, rrect.x, rrect.y, rrect.w, rrect.h);
+                  tizen_viewport_send_destination_changed(viewport->resource, rtransform,
+                                                          rrect.x, rrect.y, rrect.w, rrect.h);
+               }
+
+             vp->changed = EINA_TRUE;
+          }
+     }
+   else if (viewport)
+     PDB("%p buffer", ec->comp_data->buffer_ref.buffer);
+
+   EINA_LIST_FOREACH(ec->comp_data->sub.list, l, subc)
+      e_comp_wl_viewport_apply(subc);
+
+   EINA_LIST_FOREACH(ec->comp_data->sub.below_list, l, subc)
+      e_comp_wl_viewport_apply(subc);
+
+   return EINA_TRUE;
+}
+
+EINTERN Eina_Bool
+e_comp_wl_viewport_is_changed(E_Client *ec)
+{
+   E_Viewport *viewport;
+   E_Client *subc;
+   Eina_List *l;
+
+   if (!ec || !ec->comp_data || e_object_is_del(E_OBJECT(ec)))
+     return EINA_FALSE;
+
+   viewport = _e_comp_wl_viewport_get_viewport(ec->comp_data->scaler.viewport);
+   if(viewport && viewport->changed)
+     return EINA_TRUE;
+
+   EINA_LIST_FOREACH(ec->comp_data->sub.list, l, subc)
+      if (e_comp_wl_viewport_is_changed(subc))
+        return EINA_TRUE;
+
+   EINA_LIST_FOREACH(ec->comp_data->sub.below_list, l, subc)
+      if (e_comp_wl_viewport_is_changed(subc))
+        return EINA_TRUE;
+
+   return EINA_FALSE;
+}
+
+static void
+_e_comp_wl_viewport_cb_surface_destroy(struct wl_listener *listener, void *data)
+{
+   E_Viewport *viewport = container_of(listener, E_Viewport, surface_destroy_listener);
+
+   _destroy_viewport(viewport);
+}
+
+static void
+_e_comp_wl_viewport_cb_apply_viewport(struct wl_listener *listener, void *data)
+{
+   E_Viewport *viewport = container_of(listener, E_Viewport, surface_apply_viewport_listener);
+   E_Client *ec = viewport->ec;
+   E_Client *topmost = _topmost_parent_get(ec);
+   E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+
+   if (vp->changed)
+     _e_comp_wl_viewport_set_changed(viewport);
+
+   _e_comp_wl_viewport_parent_check(viewport);
+
+   if (!viewport->changed) return;
+   if (!ec->comp_data->buffer_ref.buffer) return;
+   if (viewport->epc && !viewport->epc->comp_data->buffer_ref.buffer) return;
+
+   PDB("apply: topmost(%p)", topmost);
+
+   if (!e_comp_wl_viewport_apply(topmost))
+     {
+        PER("failed to apply tizen_viewport");
+        return;
+     }
+}
+
+static Eina_Bool
+_e_comp_wl_viewport_cb_topmost_rotate(void *data, int type, void *event)
+{
+   E_Viewport *viewport = data;
+   E_Client *ec = viewport->ec;
+   E_Client *topmost = _topmost_parent_get(ec);
+   E_Event_Client *ev = event;
+
+   if (topmost != ev->ec)
+     return ECORE_CALLBACK_PASS_ON;
+
+   PDB("rorate start: topmost(%p)", topmost);
+   e_comp_wl_viewport_apply(topmost);
+   PDB("rorate end");
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+EINTERN Eina_Bool
+e_comp_wl_viewport_create(struct wl_resource *resource,
+                          uint32_t id,
+                          struct wl_resource *surface)
+{
+   E_Client *ec = wl_resource_get_user_data(surface);
+   int version = wl_resource_get_version(resource);
+   struct wl_client *client;
+   struct wl_resource *res;
+   E_Viewport *viewport;
+
+   if (!ec || !ec->comp_data || e_object_is_del(E_OBJECT(ec)) || !ec->comp_data->surface)
+     {
+        ERR("wrong resource %d", wl_resource_get_id(surface));
+        return EINA_FALSE;
+     }
+
+   if (ec->comp_data->scaler.viewport)
+     {
+        ERR("wl_surface@%d already has a viewport",
+            wl_resource_get_id(surface));
+        return EINA_FALSE;
+     }
+
+   if (!(client = wl_resource_get_client(surface)))
+     {
+        ERR("Could not get client from wl_surface@%d",
+            wl_resource_get_id(surface));
+        return EINA_FALSE;
+     }
+
+   viewport = calloc(1, sizeof *viewport);
+   if (!viewport)
+     {
+        ERR("failed to alloc a viewport");
+        return EINA_FALSE;
+     }
+
+   res = wl_resource_create(client, &tizen_viewport_interface, version, id);
+   if (!res)
+     {
+        ERR("Could not create tizen_viewport_interface resource");
+        free(viewport);
+        return EINA_FALSE;
+     }
+
+   viewport->resource = res;
+   viewport->ec = ec;
+   viewport->window = e_client_util_win_get(ec);
+
+   _e_comp_wl_viewport_parent_check(viewport);
+
+   viewport->client_hook_del = e_client_hook_add(E_CLIENT_HOOK_DEL, _client_cb_del, viewport);
+   viewport->client_hook_move = e_client_hook_add(E_CLIENT_HOOK_MOVE_UPDATE, _client_cb_move, viewport);
+   viewport->client_hook_resize = e_client_hook_add(E_CLIENT_HOOK_RESIZE_UPDATE, _client_cb_resize, viewport);
+
+   viewport->subsurf_hook_create = e_comp_wl_hook_add(E_COMP_WL_HOOK_SUBSURFACE_CREATE, _subsurface_cb_create, viewport);
+
+   viewport->topmost_rotate_hdl =
+      ecore_event_handler_add(E_EVENT_CLIENT_ROTATION_CHANGE_END,
+                              _e_comp_wl_viewport_cb_topmost_rotate, viewport);
+
+   viewport->source.w = -1;
+   viewport->cropped_source.w = -1;
+
+   viewport->surface_apply_viewport_listener.notify = _e_comp_wl_viewport_cb_apply_viewport;
+   wl_signal_add(&ec->comp_data->apply_viewport_signal, &viewport->surface_apply_viewport_listener);
+
+   /* Use scaler variable because tizen_viewport is the alternative of wl_viewport */
+   ec->comp_data->scaler.viewport = res;
+   wl_resource_set_implementation(res, &_e_comp_wl_viewport_interface,
+                                  viewport, _e_comp_wl_viewport_destroy);
+
+   viewport->surface_destroy_listener.notify = _e_comp_wl_viewport_cb_surface_destroy;
+   wl_resource_add_destroy_listener(ec->comp_data->surface, &viewport->surface_destroy_listener);
+
+   PIN("tizen_viewport@%d viewport(%p) created", id, viewport);
+
+   return EINA_TRUE;
+}
+
+static E_Viewport*
+_e_comp_wl_viewport_get_viewport(struct wl_resource *resource)
+{
+   if (!resource)
+     return NULL;
+
+   if (wl_resource_instance_of(resource, &tizen_viewport_interface, &_e_comp_wl_viewport_interface))
+     return wl_resource_get_user_data(resource);
+
+   return NULL;
+}
+
+static void
+_e_comp_wl_viewport_print(void *data, const char *log_path)
+{
+   FILE *log_fl;
+   Evas_Object *o;
+   const char *dest_type_str[] = { "None", "Rect", "Ratio", "Mode" };
+   const char *mode_str[] = { "None", "LetterBox", "Origin", "Full", "Cropped_Full", "Origin_or_Letter" };
+   int first_line = 1;
+   log_fl = fopen(log_path, "a");
+   if (!log_fl)
+     {
+        ERR("failed: open file(%s)", log_path);
+        return;
+     }
+
+   setvbuf(log_fl, NULL, _IOLBF, 512);
+
+   // append clients.
+   for (o = evas_object_top_get(e_comp->evas); o; o = evas_object_below_get(o))
+     {
+        E_Client *ec;
+        E_Viewport *viewport;
+        Ecore_Window win = 0;
+        const char *name = NULL;
+
+        ec = evas_object_data_get(o, "E_Client");
+        if (!ec || e_object_is_del(E_OBJECT(ec)) || !ec->comp_data) continue;
+
+        viewport = _e_comp_wl_viewport_get_viewport(ec->comp_data->scaler.viewport);
+        if (!viewport) continue;
+
+        if (first_line)
+          {
+             fprintf(log_fl, "\n[ viewport information ]\n");
+             first_line = 0;
+          }
+
+        win = e_client_util_win_get(ec);
+        name = e_client_util_name_get(ec);
+        if (!name)
+          name = "NO NAME";
+
+        fprintf(log_fl, "* WinID: 0x%08"PRIxPTR" '%s'", win, name);
+        if (viewport->epc)
+          {
+             win = e_client_util_win_get(viewport->epc);
+             fprintf(log_fl, " (parentID: 0x%08"PRIxPTR")\n", win);
+          }
+        else
+          fprintf(log_fl, "\n");
+        if (viewport->transform > 0)
+          fprintf(log_fl, "\t  transform: %d%s\n",
+                  (viewport->transform & 0x3) * 90 % 360,
+                  (viewport->transform & 0x4) ? "(flipped)" : "");
+        if (viewport->follow_parent_transform)
+          fprintf(log_fl, "\t     follow: parent's transform\n");
+        if (viewport->source.w != -1)
+          fprintf(log_fl, "\t     source: %dx%d+%d+%d\n",
+                  viewport->source.w, viewport->source.h,
+                  viewport->source.x, viewport->source.y);
+        fprintf(log_fl, "\t       type: %s\n", dest_type_str[viewport->type]);
+        if (viewport->type == DESTINATION_TYPE_RECT)
+          fprintf(log_fl, "\t  dest_rect: %dx%d+%d+%d\n",
+                  viewport->destination.rect.w, viewport->destination.rect.h,
+                  viewport->destination.rect.x, viewport->destination.rect.y);
+        else if (viewport->type == DESTINATION_TYPE_RATIO)
+          fprintf(log_fl, "\t dest_ratio: %.2fx%.2f+%.2f+%.2f\n",
+                  viewport->destination.ratio.w, viewport->destination.ratio.h,
+                  viewport->destination.ratio.x, viewport->destination.ratio.y);
+        else if (viewport->type == DESTINATION_TYPE_MODE)
+          {
+             fprintf(log_fl, "\t       mode: %s\n",
+                     mode_str[viewport->destination.mode.type]);
+             if (viewport->destination.mode.ratio_h != -1.0)
+               fprintf(log_fl, "\t\t    ratio: H(%.2f) V(%.2f)\n",
+                       viewport->destination.mode.ratio_h,
+                       viewport->destination.mode.ratio_v);
+             if (viewport->destination.mode.scale_h != -1.0)
+               fprintf(log_fl, "\t\t    scale: H(%.2f) V(%.2f)\n",
+                       viewport->destination.mode.scale_h,
+                       viewport->destination.mode.scale_v);
+             if (viewport->destination.mode.align_h != -1.0)
+               fprintf(log_fl, "\t\t    align: H(%.2f) V(%.2f)\n",
+                       viewport->destination.mode.align_h,
+                       viewport->destination.mode.align_v);
+             if (viewport->destination.mode.offset_w != 0 ||
+                 viewport->destination.mode.offset_h != 0 ||
+                 viewport->destination.mode.offset_x != 0 ||
+                 viewport->destination.mode.offset_y != 0)
+               fprintf(log_fl, "\t\t   offset: W(%d) H(%d) (%d) Y(%d)\n",
+                       viewport->destination.mode.offset_w, viewport->destination.mode.offset_h,
+                       viewport->destination.mode.offset_x, viewport->destination.mode.offset_y);
+          }
+
+        fprintf(log_fl, "\n");
+     }
+
+   fflush(log_fl);
+   fclose(log_fl);
+}
+
+
+EINTERN int
+e_comp_wl_viewport_init(void)
+{
+   if (!e_comp_wl) return 0;
+   if (!e_comp_wl->wl.disp) return 0;
+
+   e_info_server_hook_set("viewport", _e_comp_wl_viewport_print, NULL);
+
+   return 1;
+}
+
+EINTERN void
+e_comp_wl_viewport_shutdown(void)
+{
+   e_info_server_hook_set("viewport", NULL, NULL);
+}
diff --git a/src/bin/e_comp_wl_viewport.h b/src/bin/e_comp_wl_viewport.h
new file mode 100644 (file)
index 0000000..86b530b
--- /dev/null
@@ -0,0 +1,17 @@
+#ifdef E_TYPEDEFS
+
+#else
+#ifndef E_COMP_WL_VIEWPORT_H
+#define E_COMP_WL_VIEWPORT_H
+
+EINTERN int e_comp_wl_viewport_init(void);
+EINTERN void e_comp_wl_viewport_shutdown(void);
+
+EINTERN Eina_Bool e_comp_wl_viewport_create(struct wl_resource *resource,
+                                            uint32_t id,
+                                            struct wl_resource *surface);
+EINTERN Eina_Bool e_comp_wl_viewport_apply(E_Client *ec);
+EINTERN Eina_Bool e_comp_wl_viewport_is_changed(E_Client *ec);
+
+#endif
+#endif
index 1855835..5bad109 100644 (file)
@@ -257,6 +257,7 @@ _e_config_edd_init(Eina_Bool old)
 #endif
    E_CONFIG_VAL(D, T, screen_rotation_pre, UINT);
    E_CONFIG_VAL(D, T, screen_rotation_setting, UINT);
+   E_CONFIG_VAL(D, T, eom_enable, UCHAR);
    E_CONFIG_VAL(D, T, use_cursor_timer, INT);
    E_CONFIG_VAL(D, T, cursor_timer_interval, INT);
    E_CONFIG_LIST(D, T, client_types, _e_config_client_type_edd);
@@ -482,6 +483,7 @@ e_config_load(void)
    E_CONFIG_LIMIT(e_config->keyboard.repeat_rate, -1, 1000); // 1 second
    E_CONFIG_LIMIT(e_config->screen_rotation_pre, 0, 270);
    E_CONFIG_LIMIT(e_config->screen_rotation_setting, 0, 270);
+   E_CONFIG_LIMIT(e_config->eom_enable, 0, 1);
    E_CONFIG_LIMIT(e_config->use_cursor_timer, 0, 1);
    E_CONFIG_LIMIT(e_config->sleep_for_dri, 0, 1);
    E_CONFIG_LIMIT(e_config->create_wm_ready, 0, 1);
index b47e0c9..7b83e6a 100644 (file)
@@ -149,6 +149,7 @@ struct _E_Config
 #endif
    unsigned int screen_rotation_pre;
    unsigned int screen_rotation_setting;
+   Eina_Bool eom_enable;
    int use_cursor_timer;
    int cursor_timer_interval;
    Eina_List *client_types;
index 6f594e4..ce99a1d 100644 (file)
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #include "e.h"
+#include <Ecore_Drm.h>
 
-static Ecore_Event_Handler *_e_dpms_handler_config_mode = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_fullscreen = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_unfullscreen = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_remove = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_iconify = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_uniconify = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_desk_set = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_desk_show = NULL;
-
-#define STANDBY 5
-#define SUSPEND 6
-#define OFF 7
-
-E_API void
-e_dpms_update(void)
+typedef enum _E_Dpms_Mode
 {
-   /* do nothing */
-   ;
-}
+   E_DPMS_MODE_ON      = 0,
+   E_DPMS_MODE_STANDBY = 1,
+   E_DPMS_MODE_SUSPEND = 2,
+   E_DPMS_MODE_OFF     = 3
+} E_Dpms_Mode;
+
+#define BUS "org.enlightenment.wm"
+#define PATH "/org/enlightenment/wm"
+#define INTERFACE "org.enlightenment.wm.dpms"
+
+static Ecore_Drm_Output *dpms_output;
+static unsigned int dpms_value;
 
-E_API void
-e_dpms_force_update(void)
+static Eldbus_Connection *conn;
+static Eldbus_Service_Interface *iface;
+
+static Eldbus_Message *
+_e_dpms_set_cb(const Eldbus_Service_Interface *iface, const Eldbus_Message *msg)
 {
-   int enabled;
+   Eldbus_Message *reply = eldbus_message_method_return_new(msg);
+   unsigned int uint32 = -1;
+   int result = -1;
+
+   DBG("got DPMS request");
 
-   enabled = 0;
-   if (!enabled) return;
+   if (eldbus_message_arguments_get(msg, "u", &uint32) && uint32 < 4)
+     {
+        Ecore_Drm_Device *dev;
+        Ecore_Drm_Output *output;
+        Eina_List *devs, *l, *ll;
+        E_Zone *zone;
+        Eina_List *zl;
+
+        INF("DPMS value: %d", uint32);
+
+        devs = eina_list_clone(ecore_drm_devices_get());
+        EINA_LIST_FOREACH(devs, l, dev)
+           EINA_LIST_FOREACH(dev->outputs, ll, output)
+             {
+                int x;
+                ecore_drm_output_position_get(output, &x, NULL);
+
+                EINA_LIST_FOREACH(e_comp->zones, zl, zone)
+                  {
+                     if (uint32 == E_DPMS_MODE_ON)
+                       e_zone_display_state_set(zone, E_ZONE_DISPLAY_STATE_ON);
+                     else if (uint32 == E_DPMS_MODE_OFF)
+                       e_zone_display_state_set(zone, E_ZONE_DISPLAY_STATE_OFF);
+                  }
+
+                /* only for main output */
+                if (x != 0)
+                  continue;
+
+                DBG("set DPMS");
+
+                dpms_output = output;
+                dpms_value = uint32;
+                ecore_drm_output_dpms_set(output, uint32);
+             }
+
+        result = uint32;
+        if (devs) eina_list_free(devs);
+     }
+
+   eldbus_message_arguments_append(reply, "i", result);
+
+   return reply;
 }
 
-static Eina_Bool
-_e_dpms_handler_config_mode_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
+static Eldbus_Message *
+_e_dpms_get_cb(const Eldbus_Service_Interface *iface, const Eldbus_Message *msg)
 {
-   e_dpms_update();
-   return ECORE_CALLBACK_PASS_ON;
+   Eldbus_Message *reply = eldbus_message_method_return_new(msg);
+
+   DBG("got DPMS 'get' request");
+
+   eldbus_message_arguments_append(reply, "i", dpms_value);
+
+   return reply;
 }
 
-static Eina_Bool
-_e_dpms_handler_border_fullscreen_check_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
+static const Eldbus_Method methods[] =
 {
-   if (e_config->no_dpms_on_fullscreen) e_dpms_update();
-   return ECORE_CALLBACK_PASS_ON;
-}
+   {"set", ELDBUS_ARGS({"u", "uint32"}), ELDBUS_ARGS({"i", "int32"}), _e_dpms_set_cb, 0},
+   {"get", NULL, ELDBUS_ARGS({"i", "int32"}), _e_dpms_get_cb, 0},
+   {}
+};
 
-static Eina_Bool
-_e_dpms_handler_border_desk_set_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
+static const Eldbus_Service_Interface_Desc iface_desc = {
+   INTERFACE, methods, NULL, NULL, NULL, NULL
+};
+
+static void
+_e_dpms_name_request_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED)
 {
-   if (e_config->no_dpms_on_fullscreen) e_dpms_update();
-   return ECORE_CALLBACK_PASS_ON;
+   unsigned int reply;
+
+   if (eldbus_message_error_get(msg, NULL, NULL))
+     {
+        printf("error on on_name_request\n");
+        return;
+     }
+
+   if (!eldbus_message_arguments_get(msg, "u", &reply))
+     {
+        printf("error geting arguments on on_name_request\n");
+        return;
+     }
 }
 
 static Eina_Bool
-_e_dpms_handler_desk_show_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
+_e_dpms_dbus_init(void *data)
 {
-   if (e_config->no_dpms_on_fullscreen) e_dpms_update();
-   return ECORE_CALLBACK_PASS_ON;
-}
+   if (conn)
+     return ECORE_CALLBACK_CANCEL;
 
-EINTERN int
-e_dpms_init(void)
-{
-   _e_dpms_handler_config_mode = ecore_event_handler_add
-       (E_EVENT_CONFIG_MODE_CHANGED, _e_dpms_handler_config_mode_cb, NULL);
+   if (!conn)
+     conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM);
 
-   _e_dpms_handler_border_fullscreen = ecore_event_handler_add
-       (E_EVENT_CLIENT_FULLSCREEN, _e_dpms_handler_border_fullscreen_check_cb, NULL);
+   if (!conn)
+     {
+        ERR("eldbus_connection_get fail..");
+        ecore_timer_add(1.0, _e_dpms_dbus_init, NULL);
+        return ECORE_CALLBACK_CANCEL;
+     }
 
-   _e_dpms_handler_border_unfullscreen = ecore_event_handler_add
-       (E_EVENT_CLIENT_UNFULLSCREEN, _e_dpms_handler_border_fullscreen_check_cb, NULL);
+   INF("eldbus_connection_get success..");
 
-   _e_dpms_handler_border_remove = ecore_event_handler_add
-       (E_EVENT_CLIENT_REMOVE, _e_dpms_handler_border_fullscreen_check_cb, NULL);
+   iface = eldbus_service_interface_register(conn, PATH, &iface_desc);
+   EINA_SAFETY_ON_NULL_GOTO(iface, failed);
 
-   _e_dpms_handler_border_iconify = ecore_event_handler_add
-       (E_EVENT_CLIENT_ICONIFY, _e_dpms_handler_border_fullscreen_check_cb, NULL);
+   eldbus_name_request(conn, BUS, ELDBUS_NAME_REQUEST_FLAG_DO_NOT_QUEUE,
+                       _e_dpms_name_request_cb, NULL);
 
-   _e_dpms_handler_border_uniconify = ecore_event_handler_add
-       (E_EVENT_CLIENT_UNICONIFY, _e_dpms_handler_border_fullscreen_check_cb, NULL);
+   return ECORE_CALLBACK_CANCEL;
+failed:
+   if (conn)
+     {
+        eldbus_name_release(conn, BUS, NULL, NULL);
+        eldbus_connection_unref(conn);
+        conn = NULL;
+     }
 
-   _e_dpms_handler_border_desk_set = ecore_event_handler_add
-       (E_EVENT_CLIENT_DESK_SET, _e_dpms_handler_border_desk_set_cb, NULL);
+   return ECORE_CALLBACK_CANCEL;
+}
+
+EINTERN int
+e_dpms_init(void)
+{
+   if (eldbus_init() == 0) return 0;
 
-   _e_dpms_handler_desk_show = ecore_event_handler_add
-       (E_EVENT_DESK_SHOW, _e_dpms_handler_desk_show_cb, NULL);
+   _e_dpms_dbus_init(NULL);
 
    return 1;
 }
@@ -90,53 +167,28 @@ e_dpms_init(void)
 EINTERN int
 e_dpms_shutdown(void)
 {
-   if (_e_dpms_handler_config_mode)
-     {
-        ecore_event_handler_del(_e_dpms_handler_config_mode);
-        _e_dpms_handler_config_mode = NULL;
-     }
-
-   if (_e_dpms_handler_border_fullscreen)
-     {
-        ecore_event_handler_del(_e_dpms_handler_border_fullscreen);
-        _e_dpms_handler_border_fullscreen = NULL;
-     }
-
-   if (_e_dpms_handler_border_unfullscreen)
+   if (iface)
      {
-        ecore_event_handler_del(_e_dpms_handler_border_unfullscreen);
-        _e_dpms_handler_border_unfullscreen = NULL;
+        eldbus_service_interface_unregister(iface);
+        iface = NULL;
      }
-
-   if (_e_dpms_handler_border_remove)
+   if (conn)
      {
-        ecore_event_handler_del(_e_dpms_handler_border_remove);
-        _e_dpms_handler_border_remove = NULL;
+        eldbus_name_release(conn, BUS, NULL, NULL);
+        eldbus_connection_unref(conn);
+        conn = NULL;
      }
 
-   if (_e_dpms_handler_border_iconify)
-     {
-        ecore_event_handler_del(_e_dpms_handler_border_iconify);
-        _e_dpms_handler_border_iconify = NULL;
-     }
+   eldbus_shutdown();
 
-   if (_e_dpms_handler_border_uniconify)
-     {
-        ecore_event_handler_del(_e_dpms_handler_border_uniconify);
-        _e_dpms_handler_border_uniconify = NULL;
-     }
-
-   if (_e_dpms_handler_border_desk_set)
-     {
-        ecore_event_handler_del(_e_dpms_handler_border_desk_set);
-        _e_dpms_handler_border_desk_set = NULL;
-     }
+   return 1;
+}
 
-   if (_e_dpms_handler_desk_show)
-     {
-        ecore_event_handler_del(_e_dpms_handler_desk_show);
-        _e_dpms_handler_desk_show = NULL;
-     }
+EINTERN unsigned int
+e_dpms_get(Ecore_Drm_Output *output)
+{
+   if (dpms_output == output)
+     return dpms_value;
 
-   return 1;
+   return DRM_MODE_DPMS_ON;
 }
index 37ec666..bdacdce 100644 (file)
@@ -1,13 +1,15 @@
 #ifdef E_TYPEDEFS
+
 #else
 #ifndef E_DPMS_H
 #define E_DPMS_H
 
+#include <Ecore_Drm.h>
+
 EINTERN int e_dpms_init(void);
 EINTERN int e_dpms_shutdown(void);
 
-E_API void e_dpms_update(void);
-E_API void e_dpms_force_update(void);
+EINTERN unsigned int e_dpms_get(Ecore_Drm_Output *output);
 
 #endif
 #endif
diff --git a/src/bin/e_eom.c b/src/bin/e_eom.c
new file mode 100644 (file)
index 0000000..c914f59
--- /dev/null
@@ -0,0 +1,2657 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "e.h"
+#include <xdg-shell-server-protocol.h>
+#include <eom-server-protocol.h>
+#include <Ecore_Drm.h>
+#include <tdm.h>
+#include <eom.h>
+#include <tbm_bufmgr.h>
+#include <tbm_surface.h>
+#include <wayland-tbm-server.h>
+#ifdef FRAMES
+#include <time.h>
+#endif
+
+/*
+#define EOM_DUMP_MIRROR_BUFFERS
+#define EOM_DUMP_PRESENTATION_BUFFERS
+*/
+
+#define ALEN(array) (sizeof(array) / sizeof(array)[0])
+
+#define EOMER(msg, ARG...) ERR("[eom module][ERR] " msg, ##ARG)
+#define EOMWR(msg, ARG...) WRN("[eom module][WRN] " msg, ##ARG)
+#define EOMIN(msg, ARG...) INF("[eom module][INF] " msg, ##ARG)
+#define EOMDB(msg, ARG...) DBG("[eom module][DBG] " msg, ##ARG)
+#define EOMDBG(msg, ARG...) ;
+
+#define EOM_NUM_ATTR 3
+#define EOM_CONNECT_CHECK_TIMEOUT 7.0
+#define EOM_DELAY_CHECK_TIMEOUT 4.0
+#define EOM_ROTATE_DELAY_TIMEOUT 0.4
+
+typedef struct _E_Eom E_Eom, *E_EomPtr;
+typedef struct _E_Eom_Out_Mode E_EomOutMode, *E_EomOutModePtr;
+typedef struct _E_Eom_Output E_EomOutput, *E_EomOutputPtr;
+typedef struct _E_Eom_Client E_EomClient, *E_EomClientPtr;
+typedef struct _E_Eom_Comp_Object_Intercept_Hook_Data E_EomCompObjectInterceptHookData;
+typedef struct _E_Eom_Output_Buffer E_EomOutputBuffer, *E_EomOutputBufferPtr;
+typedef struct _E_Eom_Buffer E_EomBuffer, *E_EomBufferPtr;
+typedef void(*E_EomEndShowingEventPtr)(E_EomOutputPtr eom_output, tbm_surface_h srfc, void * user_data);
+
+typedef enum
+{
+   NONE,
+   MIRROR,
+   PRESENTATION,
+   WAIT_PRESENTATION,    /* It is used for delayed runnig of Presentation mode */
+} E_EomOutputState;
+
+typedef enum
+{
+   ROTATE_NONE,
+   ROTATE_INIT,
+   ROTATE_PENDING,
+   ROTATE_CANCEL,
+   ROTATE_DONE
+} E_EomOutputRotate;
+
+struct _E_Eom
+{
+   struct wl_global *global;
+
+   tdm_display *dpy;
+   tbm_bufmgr bufmgr;
+   int fd;
+
+   unsigned int output_count;
+   Eina_List *outputs;
+   Eina_List *clients;
+   Eina_List *handlers;
+   Eina_List *hooks;
+   Eina_List *comp_object_intercept_hooks;
+
+   /* Internal output data */
+   int main_output_state;
+   char *main_output_name;
+   int width;
+   int height;
+   char check_first_boot;
+   Ecore_Timer *timer;
+
+   /* Rotation data */
+   int angle; /* 0, 90, 180, 270 */
+   E_EomOutputRotate rotate_state;
+   E_EomOutput *rotate_output;
+   Ecore_Timer *rotate_timer;
+};
+
+struct _E_Eom_Output
+{
+   unsigned int id;
+   eom_output_type_e type;
+   eom_output_mode_e mode;
+   unsigned int width;
+   unsigned int height;
+   unsigned int phys_width;
+   unsigned int phys_height;
+
+   const char *name;
+
+   tdm_output *output;
+   tdm_layer *layer;
+   tdm_pp *pp;
+
+   E_EomOutputState state;
+   tdm_output_conn_status status;
+   eom_output_attribute_e attribute;
+   eom_output_attribute_state_e attribute_state;
+   enum wl_eom_status connection;
+
+   /* mirror mode data */
+   tbm_surface_queue_h pp_queue;
+   /* dst surface in current pp process*/
+   tbm_surface_h pp_dst_surface;
+   /* src surface in current pp process*/
+   tbm_surface_h pp_src_surface;
+
+   /* output buffers*/
+   Eina_List * pending_buff;       /* can be deleted any time */
+   E_EomOutputBufferPtr wait_buff; /* wait end of commit, can't be deleted */
+   E_EomOutputBufferPtr show_buff; /* current showed buffer, can be deleted only after commit event with different buff */
+
+   /* If attribute has been set while external output is disconnected
+    * then show black screen and wait until EOM client start sending
+    * buffers. After expiring of the delay start mirroring */
+   Ecore_Timer *delay;
+   Ecore_Timer *watchdog;
+};
+
+struct _E_Eom_Client
+{
+   struct wl_resource *resource;
+   Eina_Bool current;
+
+   /* EOM output the client related to */
+   int output_id;
+   /* E_Client the client related to */
+   E_Client *ec;
+};
+
+struct _E_Eom_Output_Buffer
+{
+   E_EomOutputPtr eom_output;
+   tbm_surface_h tbm_surface;
+   E_EomEndShowingEventPtr cb_func;
+   void *cb_user_data;
+};
+
+struct _E_Eom_Buffer
+{
+   E_Comp_Wl_Buffer *wl_buffer;
+   E_Comp_Wl_Buffer_Ref comp_wl_buffer_ref;
+
+   /* double reference to avoid sigterm crash */
+   E_Comp_Wl_Buffer_Ref comp_wl_buffer_ref_2;
+};
+
+struct _E_Eom_Comp_Object_Intercept_Hook_Data
+{
+   E_Client *ec;
+   E_Comp_Object_Intercept_Hook *hook;
+};
+
+/*
+ * EOM Output Attributes
+ * +-----------------+------------+-----------------+------------+
+ * |                 |   normal   | exclusive_share | exclusive  |
+ * +-----------------+------------+-----------------+------------+
+ * | normal          |  possible  |    possible     |  possible  |
+ * +-----------------+------------+-----------------+------------+
+ * | exclusive_share | impossible |    possible     |  possible  |
+ * +-----------------+------------+-----------------+------------+
+ * | exclusive       | impossible |   impossible    | impossible |
+ * +-----------------+------------+-----------------+------------+
+ *
+ * possible   = 1
+ * impossible = 0
+ */
+static int eom_output_attributes[EOM_NUM_ATTR][EOM_NUM_ATTR] =
+{
+   {1, 1, 1},
+   {0, 1, 1},
+   {0, 0, 0},
+};
+
+static const char *eom_conn_types[] =
+{
+   "None", "VGA", "DVI-I", "DVI-D", "DVI-A",
+   "Composite", "S-Video", "LVDS", "Component", "DIN",
+   "DisplayPort", "HDMI-A", "HDMI-B", "TV", "eDP", "Virtual",
+   "DSI",
+};
+
+static E_EomPtr g_eom = NULL;
+
+static void _e_eom_cb_dequeuable(tbm_surface_queue_h queue, void *user_data);
+static void _e_eom_cb_pp(tbm_surface_h surface, void *user_data);
+static E_EomOutputPtr _e_eom_output_get_by_id(int id);
+
+static E_EomOutputBufferPtr
+_e_eom_output_buff_create( E_EomOutputPtr eom_output, tbm_surface_h tbm_surface, E_EomEndShowingEventPtr cb_func, void *cb_user_data)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surface, NULL);
+   E_EomOutputBufferPtr outbuff = E_NEW(E_EomOutputBuffer, 1);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(outbuff, NULL);
+
+   EOMDB("Allocate output buffer:%p", outbuff);
+
+   outbuff->eom_output = eom_output;
+
+   tbm_surface_internal_ref(tbm_surface);
+   outbuff->tbm_surface = tbm_surface;
+
+   outbuff->cb_func = cb_func;
+   outbuff->cb_user_data = cb_user_data;
+
+   return outbuff;
+}
+
+static void
+_e_eom_output_buff_delete( E_EomOutputBufferPtr buff)
+{
+   if (buff)
+     {
+        tbm_surface_internal_unref(buff->tbm_surface);
+        if (buff->cb_func)
+          buff->cb_func(buff->eom_output, buff->tbm_surface, buff->cb_user_data);
+        E_FREE(buff);
+     }
+}
+
+static E_EomBuffer *
+_e_eom_buffer_create(E_Comp_Wl_Buffer *wl_buffer)
+{
+   E_EomBuffer * eom_buffer = E_NEW(E_EomBuffer, 1);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(eom_buffer, NULL);
+
+   eom_buffer->wl_buffer = wl_buffer;
+
+   /* Forbid E sending 'wl_buffer_send_release' event to external clients */
+   e_comp_wl_buffer_reference(&eom_buffer->comp_wl_buffer_ref, wl_buffer);
+
+   /* double reference to avoid sigterm crash */
+   e_comp_wl_buffer_reference(&eom_buffer->comp_wl_buffer_ref_2, wl_buffer);
+
+   EOMDB("E_EomBuffer:%p wl_buffer:%p busy:%d", eom_buffer, wl_buffer, wl_buffer->busy);
+   return eom_buffer;
+}
+
+static void
+_e_eom_buffer_destroy(E_EomBuffer * eom_buffer)
+{
+   EINA_SAFETY_ON_NULL_RETURN(eom_buffer);
+
+   EOMDB("wl_buffer:%p busy:%d", eom_buffer->wl_buffer, eom_buffer->wl_buffer->busy);
+
+   eom_buffer->wl_buffer = NULL;
+
+   e_comp_wl_buffer_reference(&eom_buffer->comp_wl_buffer_ref, NULL);
+
+   /* double reference to avoid sigterm crash */
+   e_comp_wl_buffer_reference(&eom_buffer->comp_wl_buffer_ref_2, NULL);
+
+   E_FREE(eom_buffer);
+}
+
+static inline eom_output_mode_e
+_e_eom_output_state_get_mode(E_EomOutputPtr output)
+{
+   if (output == NULL)
+     return EOM_OUTPUT_MODE_NONE;
+   return output->mode;
+}
+
+static inline void
+_e_eom_output_state_set_mode(E_EomOutputPtr output, eom_output_mode_e mode)
+{
+   if (output == NULL)
+     return;
+   output->mode = mode;
+}
+
+static inline eom_output_attribute_e
+_e_eom_output_state_get_attribute_state(E_EomOutputPtr output)
+{
+   if (output == NULL)
+     return EOM_OUTPUT_ATTRIBUTE_STATE_NONE;
+   return output->attribute_state;
+}
+
+static inline void
+_e_eom_output_attribute_state_set(E_EomOutputPtr output, eom_output_attribute_e attribute_state)
+{
+   if (output == NULL)
+     return;
+   output->attribute_state = attribute_state;
+}
+
+static inline eom_output_attribute_e
+_e_eom_output_state_get_attribute(E_EomOutputPtr output)
+{
+   if (output == NULL)
+     return EOM_OUTPUT_ATTRIBUTE_NONE;
+   return output->attribute;
+}
+
+static inline void
+_e_eom_output_state_set_force_attribute(E_EomOutputPtr output, eom_output_attribute_e attribute)
+{
+   if (output == NULL)
+     return;
+   output->attribute = attribute;
+}
+
+static inline Eina_Bool
+_e_eom_output_state_set_attribute(E_EomOutputPtr output, eom_output_attribute_e attribute)
+{
+   if (output == NULL)
+     return EINA_FALSE;
+
+   if (attribute == EOM_OUTPUT_ATTRIBUTE_NONE || output->attribute == EOM_OUTPUT_ATTRIBUTE_NONE)
+     {
+        output->attribute = attribute;
+        return EINA_TRUE;
+     }
+
+   if (eom_output_attributes[output->attribute - 1][attribute - 1] == 1)
+     {
+        output->attribute = attribute;
+        return EINA_TRUE;
+     }
+
+   return EINA_FALSE;
+}
+
+static inline tdm_output_conn_status
+_e_eom_output_state_get_status(E_EomOutputPtr output)
+{
+   if (output == NULL)
+     return TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
+   return output->status;
+}
+
+static inline void
+_e_eom_output_state_set_status(E_EomOutputPtr output, tdm_output_conn_status status)
+{
+   if (output == NULL)
+     return;
+   output->status = status;
+}
+
+static tdm_layer *
+_e_eom_output_get_layer(E_EomOutputPtr eom_output)
+{
+   int i = 0;
+   int count = 0;
+   tdm_layer *layer = NULL;
+   tdm_error err = TDM_ERROR_NONE;
+   tdm_layer_capability capa;
+   tdm_info_layer layer_info;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(eom_output, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(eom_output->output, NULL);
+
+   err = tdm_output_get_layer_count(eom_output->output, &count);
+   if (err != TDM_ERROR_NONE)
+     {
+        EOMDB ("tdm_output_get_layer_count fail(%d)", err);
+        return NULL;
+     }
+
+   for (i = 0; i < count; i++)
+     {
+        layer = (tdm_layer *)tdm_output_get_layer(eom_output->output, i, &err);
+        EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, NULL);
+
+        err = tdm_layer_get_capabilities(layer, &capa);
+        EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, NULL);
+
+        if (capa & TDM_LAYER_CAPABILITY_PRIMARY)
+          {
+             EOMDB("TDM_LAYER_CAPABILITY_PRIMARY layer found : %d", i);
+             break;
+          }
+     }
+
+   memset(&layer_info, 0x0, sizeof(tdm_info_layer));
+   layer_info.src_config.size.h = eom_output->width;
+   layer_info.src_config.size.v = eom_output->height;
+   layer_info.src_config.pos.x = 0;
+   layer_info.src_config.pos.y = 0;
+   layer_info.src_config.pos.w = eom_output->width;
+   layer_info.src_config.pos.h = eom_output->height;
+   layer_info.src_config.format = TBM_FORMAT_ARGB8888;
+   layer_info.dst_pos.x = 0;
+   layer_info.dst_pos.y = 0;
+   layer_info.dst_pos.w = eom_output->width;
+   layer_info.dst_pos.h = eom_output->height;
+   layer_info.transform = TDM_TRANSFORM_NORMAL;
+
+   err = tdm_layer_set_info(layer, &layer_info);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, NULL);
+
+   return layer;
+}
+
+static tbm_surface_h
+_e_eom_util_get_output_surface(const char *name)
+{
+   Ecore_Drm_Output *primary_output = NULL;
+   Ecore_Drm_Device *dev;
+   const Eina_List *l;
+   tbm_surface_h tbm = NULL;
+   tdm_output *tdm_output_obj = NULL;
+   tdm_layer *layer = NULL;
+   tdm_layer_capability capabilities = 0;
+   tdm_error err = TDM_ERROR_NONE;
+   int count = 0, i = 0;
+
+   EINA_LIST_FOREACH(ecore_drm_devices_get(), l, dev)
+     {
+        primary_output = ecore_drm_device_output_name_find(dev, name);
+        if (primary_output != NULL)
+          break;
+     }
+
+   if (primary_output == NULL)
+     {
+        EOMER("Get primary output.(%s)", name);
+        EINA_LIST_FOREACH(ecore_drm_devices_get(), l, dev)
+          {
+             primary_output = ecore_drm_output_primary_get(dev);
+             if (primary_output != NULL)
+               break;
+          }
+
+        if (primary_output == NULL)
+          {
+             EOMER("Get primary output.(%s)", name);
+             return NULL;
+          }
+     }
+
+   tdm_output_obj = tdm_display_get_output(g_eom->dpy, 0, &err);
+   if (tdm_output_obj == NULL || err != TDM_ERROR_NONE)
+     {
+        EOMER("tdm_display_get_output 0 fail");
+        return NULL;
+     }
+   err = tdm_output_get_layer_count(tdm_output_obj, &count);
+   if (err != TDM_ERROR_NONE)
+     {
+        EOMER("tdm_output_get_layer_count fail");
+        return NULL;
+     }
+
+   for (i = 0; i < count; i++)
+     {
+        layer = tdm_output_get_layer(tdm_output_obj, i, NULL);
+        tdm_layer_get_capabilities(layer, &capabilities);
+        if (capabilities & TDM_LAYER_CAPABILITY_PRIMARY)
+          {
+             tbm = tdm_layer_get_displaying_buffer(layer, &err);
+             if (err != TDM_ERROR_NONE)
+               {
+                  EOMER("tdm_layer_get_displaying_buffer fail");
+                  return NULL;
+               }
+             break;
+          }
+     }
+
+   return tbm;
+}
+
+static Eina_Bool
+_e_eom_pp_is_needed(int src_w, int src_h, int dst_w, int dst_h)
+{
+   if (src_w != dst_w)
+     return EINA_TRUE;
+
+   if (src_h != dst_h)
+     return EINA_TRUE;
+
+   return EINA_FALSE;
+}
+
+static void
+_e_eom_util_calculate_fullsize(int src_h, int src_v, int dst_size_h, int dst_size_v,
+                               int *dst_x, int *dst_y, int *dst_w, int *dst_h)
+{
+   double h_ratio, v_ratio;
+
+   h_ratio = src_h / dst_size_h;
+   v_ratio = src_v / dst_size_v;
+
+   if (h_ratio == v_ratio)
+     {
+        *dst_x = 0;
+        *dst_y = 0;
+        *dst_w = dst_size_h;
+        *dst_h = dst_size_v;
+     }
+   else if (h_ratio < v_ratio)
+     {
+        *dst_y = 0;
+        *dst_h = dst_size_v;
+        *dst_w = dst_size_v * src_h / src_v;
+        *dst_x = (dst_size_h - *dst_w) / 2;
+     }
+   else /* (h_ratio > v_ratio) */
+     {
+        *dst_x = 0;
+        *dst_w = dst_size_h;
+        *dst_h = dst_size_h * src_h / src_v;
+        *dst_y = (dst_size_v - *dst_h) / 2;
+     }
+}
+
+static void
+_e_eom_tbm_buffer_release_mirror_mod(E_EomOutputPtr eom_output, tbm_surface_h surface, void * unused)
+{
+   EOMDB("release eom_output:%p, tbm_surface_h:%p data:%p", eom_output, surface, unused);
+   tbm_surface_queue_release(eom_output->pp_queue, surface);
+}
+
+static void
+_e_eom_cb_layer_commit(tdm_layer *layer EINA_UNUSED, unsigned int sequence EINA_UNUSED,
+                       unsigned int tv_sec EINA_UNUSED, unsigned int tv_usec EINA_UNUSED,
+                       void *user_data)
+{
+   E_EomOutputBufferPtr outbuff = NULL;
+   E_EomOutputPtr eom_output = NULL;
+   tdm_error err = TDM_ERROR_NONE;
+
+   EINA_SAFETY_ON_NULL_RETURN(user_data);
+   outbuff = (E_EomOutputBufferPtr)user_data;
+
+   eom_output = outbuff->eom_output;
+   EINA_SAFETY_ON_NULL_RETURN(eom_output);
+
+   EOMDB("========================>  CM  END     tbm_buff:%p", outbuff->tbm_surface);
+
+   /*it means that eom_output has been canceled*/
+   if(eom_output->wait_buff == NULL)
+     {
+        _e_eom_output_buff_delete(outbuff);
+        return;
+     }
+
+   EINA_SAFETY_ON_FALSE_RETURN(eom_output->wait_buff == outbuff);
+
+   EOMDB("commit finish tbm_surface_h:%p", outbuff->tbm_surface);
+
+   /* check if show buffer is present */
+   if(eom_output->show_buff != NULL)
+     {
+        EOMDB("delete show buffer tbm_surface_h:%p", eom_output->show_buff->tbm_surface);
+        _e_eom_output_buff_delete(eom_output->show_buff);
+        eom_output->show_buff = NULL;
+     }
+
+   /* set wait_buffer as show_buff; */
+   EOMDB("set wait_buffer as show_buff tbm_surface_h:%p", outbuff->tbm_surface);
+   eom_output->wait_buff = NULL;
+   eom_output->show_buff = outbuff;
+
+   /* check if pending buffer is present */
+   outbuff = eina_list_nth(eom_output->pending_buff, 0);
+   if (outbuff != NULL)
+     {
+        eom_output->pending_buff = eina_list_remove(eom_output->pending_buff, outbuff);
+
+        EOMDB("========================>  CM- START   tbm_buff:%p", outbuff->tbm_surface);
+        EOMDB("do commit tdm_output:%p tdm_layer:%p tbm_surface_h:%p", eom_output->output,
+              eom_output->layer, outbuff->tbm_surface);
+        err = tdm_layer_set_buffer(eom_output->layer, outbuff->tbm_surface);
+        EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, error);
+
+        err = tdm_layer_commit(eom_output->layer, _e_eom_cb_layer_commit, outbuff);
+        EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, error);
+
+        eom_output->wait_buff = outbuff;
+     }
+
+   return;
+
+error:
+
+   if (outbuff)
+     {
+        EOMDB("========================>  CM- ENDERR  tbm_buff:%p", outbuff);
+        _e_eom_output_buff_delete(outbuff);
+     }
+}
+
+static Eina_Bool
+_e_eom_output_show(E_EomOutputPtr eom_output, tbm_surface_h tbm_srfc,
+                   E_EomEndShowingEventPtr cb_func, void *cb_user_data)
+{
+   tdm_error err = TDM_ERROR_NONE;
+
+   /* create new output buffer */
+   E_EomOutputBufferPtr outbuff = _e_eom_output_buff_create(eom_output, tbm_srfc, cb_func, cb_user_data);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(outbuff, EINA_FALSE);
+
+   /* check if output free to commit */
+   if (eom_output->wait_buff == NULL) /* do commit */
+     {
+        EOMDB("========================>  CM  START   tbm_buff:%p", tbm_srfc);
+        EOMDB("do commit tdm_output:%p tdm_layer:%p tbm_surface_h:%p", eom_output->output,
+              eom_output->layer, outbuff->tbm_surface);
+        err = tdm_layer_set_buffer(eom_output->layer, outbuff->tbm_surface);
+        EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, error);
+
+        err = tdm_layer_commit(eom_output->layer, _e_eom_cb_layer_commit, outbuff);
+        EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, error2);
+
+        eom_output->wait_buff = outbuff;
+     }
+   else /* add to pending queue */
+     {
+        eom_output->pending_buff = eina_list_append(eom_output->pending_buff , outbuff);
+
+        EOMDB("add to pending list tdm_output:%p tdm_layer:%p tbm_surface_h:%p",
+              eom_output->output, eom_output->layer, outbuff->tbm_surface);
+     }
+
+   return EINA_TRUE;
+
+error2:
+
+   tdm_layer_unset_buffer(eom_output->layer);
+
+error:
+
+   if (outbuff)
+     _e_eom_output_buff_delete(outbuff);
+   EOMDB("========================>  CM  ENDERR  tbm_buff:%p", tbm_srfc);
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_e_eom_pp_info_set(E_EomOutputPtr eom_output, tbm_surface_h src, tbm_surface_h dst)
+{
+   tdm_error err = TDM_ERROR_NONE;
+   tdm_info_pp pp_info;
+   int x = 0, y = 0, w = 0, h = 0;
+
+   memset(&pp_info, 0, sizeof(tdm_info_pp));
+
+   if (g_eom->angle == 0 || g_eom->angle == 180)
+     {
+        _e_eom_util_calculate_fullsize(g_eom->width, g_eom->height,
+                                       eom_output->width, eom_output->height,
+                                       &x, &y, &w, &h);
+     }
+   else if (g_eom->angle == 90 || g_eom->angle == 270)
+     {
+        _e_eom_util_calculate_fullsize(g_eom->height, g_eom->width,
+                                       eom_output->width, eom_output->height,
+                                       &x, &y, &w, &h);
+     }
+
+   EOMDB("PP: angle:%d", g_eom->angle);
+   EOMDB("PP: src:%dx%d, dst:%dx%d", g_eom->width, g_eom->height,
+         eom_output->width, eom_output->height);
+   EOMDB("PP calculation: x:%d, y:%d, w:%d, h:%d", x, y, w, h);
+
+   pp_info.src_config.size.h = g_eom->width;
+   pp_info.src_config.size.v = g_eom->height;
+   pp_info.src_config.pos.x = 0;
+   pp_info.src_config.pos.y = 0;
+   pp_info.src_config.pos.w = g_eom->width;
+   pp_info.src_config.pos.h = g_eom->height;
+   pp_info.src_config.format = TBM_FORMAT_ARGB8888;
+
+   pp_info.dst_config.size.h = eom_output->width;
+   pp_info.dst_config.size.v = eom_output->height;
+   pp_info.dst_config.pos.x = x;
+   pp_info.dst_config.pos.y = y;
+   pp_info.dst_config.pos.w = w;
+   pp_info.dst_config.pos.h = h;
+   pp_info.dst_config.format = TBM_FORMAT_ARGB8888;
+
+   switch (g_eom->angle)
+     {
+      case 0:
+         pp_info.transform = TDM_TRANSFORM_NORMAL;
+         break;
+      case 90:
+         pp_info.transform = TDM_TRANSFORM_90;
+         break;
+      case 180:
+         pp_info.transform = TDM_TRANSFORM_180;
+         break;
+      case 270:
+         pp_info.transform = TDM_TRANSFORM_270;
+         break;
+      default:
+         EOMDB("Never get here");
+         break;
+     }
+
+   pp_info.sync = 0;
+   pp_info.flags = 0;
+
+   err = tdm_pp_set_info(eom_output->pp, &pp_info);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+   return EINA_TRUE;
+}
+
+void _e_eom_clear_surfaces(E_EomOutputPtr eom_output, tbm_surface_queue_h queue)
+{
+   int num, pitch;
+   int i = 0;
+   tbm_bo bo;
+   tbm_bo_handle hndl;
+   tbm_surface_h surface[3];
+   tbm_surface_queue_error_e err = TBM_SURFACE_QUEUE_ERROR_NONE;
+
+   err = tbm_surface_queue_get_surfaces(queue, surface, &num);
+   if (err != TBM_SURFACE_QUEUE_ERROR_NONE)
+     {
+        EOMDB("get surfaces");
+        return;
+     }
+
+   for (i = 0; i < num; i++)
+     {
+        /* XXX: should be cleared all the bos of the surface? */
+        bo = tbm_surface_internal_get_bo(surface[i], 0);
+        if (!bo)
+          {
+             EOMDB("bo get error");
+             return;
+          }
+
+        hndl = tbm_bo_map(bo, TBM_DEVICE_CPU, TBM_OPTION_READ | TBM_OPTION_WRITE);
+        if (!hndl.ptr)
+          {
+             EOMDB("handle get error");
+             return;
+          }
+
+        /* TODO: take correct picth */
+        pitch = eom_output->width * 4;
+        memset(hndl.ptr, 0x00, pitch*eom_output->height);
+
+        tbm_bo_unmap(bo);
+     }
+}
+
+static E_Client *
+_e_eom_top_visible_ec_get()
+{
+   E_Client *ec;
+   Evas_Object *o;
+   E_Comp_Wl_Client_Data *cdata;
+
+   for (o = evas_object_top_get(e_comp->evas); o; o = evas_object_below_get(o))
+     {
+        ec = evas_object_data_get(o, "E_Client");
+
+        /* check e_client and skip e_clients not intersects with zone */
+        if (!ec) continue;
+        if (e_object_is_del(E_OBJECT(ec))) continue;
+        if (e_client_util_ignored_get(ec)) continue;
+        if (ec->iconic) continue;
+        if (ec->visible == 0) continue;
+        if (!(ec->visibility.obscured == 0 || ec->visibility.obscured == 1)) continue;
+        if (!ec->frame) continue;
+        if (!evas_object_visible_get(ec->frame)) continue;
+        /* if ec is subsurface, skip this */
+        cdata = (E_Comp_Wl_Client_Data *)ec->comp_data;
+        if (cdata && cdata->sub.data) continue;
+
+        return ec;
+     }
+
+   return NULL;
+}
+
+static Eina_Bool
+_e_eom_pp_rotate_check()
+{
+   E_Client *ec;
+
+   ec = _e_eom_top_visible_ec_get();
+   if (ec == NULL)
+     return EINA_FALSE;
+
+   if (g_eom->angle != ec->e.state.rot.ang.curr)
+     {
+        g_eom->angle = ec->e.state.rot.ang.curr;
+        EOMDB("rotate check: rotate angle:%d", g_eom->angle);
+        return EINA_TRUE;
+     }
+
+   return EINA_FALSE;
+}
+
+static void
+_e_eom_pp_run(E_EomOutputPtr eom_output, Eina_Bool first_run)
+{
+   tdm_error tdm_err = TDM_ERROR_NONE;
+   tbm_surface_h dst_surface = NULL;
+   tbm_surface_h src_surface = NULL;
+   Eina_Bool ret = EINA_FALSE;
+
+   if (g_eom->main_output_state == 0)
+     return;
+
+   /* If a client has committed its buffer stop mirror mode */
+   if (eom_output->state != MIRROR)
+     {
+        g_eom->rotate_state = ROTATE_NONE;
+        g_eom->rotate_output = NULL;
+        return;
+     }
+
+   if (!eom_output->pp || !eom_output->pp_queue)
+     return;
+
+   if (g_eom->rotate_state == ROTATE_INIT || g_eom->rotate_state == ROTATE_PENDING)
+     {
+        g_eom->rotate_output = eom_output;
+        if (!first_run)
+          return;
+     }
+
+   if (g_eom->rotate_state == ROTATE_CANCEL)
+     g_eom->rotate_state = ROTATE_NONE;
+
+   if (tbm_surface_queue_can_dequeue(eom_output->pp_queue, 0) )
+     {
+        tdm_err = tbm_surface_queue_dequeue(eom_output->pp_queue, &dst_surface);
+        EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, error);
+
+        EOMDB("============================>  PP  START   tbm_buff:%p", dst_surface);
+
+        src_surface = _e_eom_util_get_output_surface(g_eom->main_output_name);
+        tdm_err = TDM_ERROR_OPERATION_FAILED;
+        EINA_SAFETY_ON_NULL_GOTO(src_surface, error);
+
+        /* Is set to TRUE if device has been recently rotated */
+        if (g_eom->rotate_state == ROTATE_DONE || first_run || _e_eom_pp_rotate_check())
+          {
+             g_eom->rotate_state = ROTATE_NONE;
+             g_eom->rotate_output = NULL;
+
+             /* TODO: it has to be implemented in better way */
+             _e_eom_clear_surfaces(eom_output, eom_output->pp_queue);
+
+             ret = _e_eom_pp_info_set(eom_output, src_surface, dst_surface);
+             EINA_SAFETY_ON_FALSE_GOTO(ret == EINA_TRUE, error);
+          }
+
+        tdm_err = tdm_buffer_add_release_handler(dst_surface, _e_eom_cb_pp, eom_output);
+        EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, error);
+
+        eom_output->pp_dst_surface = dst_surface;
+        tbm_surface_internal_ref(dst_surface);
+        eom_output->pp_src_surface = src_surface;
+        tbm_surface_internal_ref(src_surface);
+        tdm_err = tdm_pp_attach(eom_output->pp, src_surface, dst_surface);
+        EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, error);
+
+        tdm_err = tdm_pp_commit(eom_output->pp);
+        EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, error);
+
+        EOMDB("do pp commit tdm_output:%p tbm_surface_h(src:%p dst:%p)", eom_output->output, src_surface, dst_surface);
+     }
+   else
+     {
+        EOMDB("all pp buffers are busy, wait release queue");
+        tbm_surface_queue_add_dequeuable_cb(eom_output->pp_queue, _e_eom_cb_dequeuable, eom_output);
+     }
+
+   return;
+
+error:
+
+   EOMER("failed run pp tdm error: %d", tdm_err);
+
+   if (eom_output->pp_src_surface)
+     {
+        tbm_surface_internal_unref(eom_output->pp_src_surface);
+        eom_output->pp_src_surface = NULL;
+     }
+   if (eom_output->pp_dst_surface)
+     {
+        tbm_surface_internal_unref(eom_output->pp_dst_surface);
+        eom_output->pp_dst_surface = NULL;
+     }
+
+   if (dst_surface)
+     {
+        EOMDB("============================>  PP  ENDERR  tbm_buff:%p", dst_surface);
+        tdm_buffer_remove_release_handler(dst_surface, _e_eom_cb_pp, eom_output);
+        tbm_surface_queue_release(eom_output->pp_queue, dst_surface);
+     }
+}
+
+static void
+_e_eom_cb_pp(tbm_surface_h surface, void *user_data)
+{
+   E_EomOutputPtr eom_output = NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN(user_data);
+   eom_output = (E_EomOutputPtr)user_data;
+
+   tdm_buffer_remove_release_handler(surface, _e_eom_cb_pp, eom_output);
+
+   if (eom_output->pp_src_surface)
+     {
+        tbm_surface_internal_unref(eom_output->pp_src_surface);
+        eom_output->pp_src_surface = NULL;
+     }
+   if (eom_output->pp_dst_surface)
+     {
+        tbm_surface_internal_unref(eom_output->pp_dst_surface);
+        eom_output->pp_dst_surface = NULL;
+     }
+
+   EOMDB("==============================>  PP  END     tbm_buff:%p", surface);
+
+   if (eom_output->pp_queue == NULL)
+     return;
+
+   if (g_eom->main_output_state == 0)
+     {
+        tbm_surface_queue_release(eom_output->pp_queue, surface);
+        return;
+     }
+
+   /* If a client has committed its buffer stop mirror mode */
+   if (eom_output->state != MIRROR)
+     {
+        tbm_surface_queue_release(eom_output->pp_queue, surface);
+        return;
+     }
+
+#ifdef EOM_DUMP_MIRROR_BUFFERS
+   char file[256];
+   static int i;
+   snprintf(file, sizeof file, "%s_%d", "eom_mirror", i++);
+   tbm_surface_internal_dump_buffer(surface, file, i++, 0);
+#endif
+
+   if(!_e_eom_output_show(eom_output, surface, _e_eom_tbm_buffer_release_mirror_mod, NULL))
+     {
+        EOMER("_e_eom_add_buff_to_show fail");
+        tbm_surface_queue_release(eom_output->pp_queue, surface);
+     }
+
+   _e_eom_pp_run(eom_output, EINA_FALSE);
+
+   EOMDB("==============================<  PP");
+}
+
+static void
+_e_eom_cb_dequeuable(tbm_surface_queue_h queue, void *user_data)
+{
+   E_EomOutputPtr eom_output = (E_EomOutputPtr)user_data;
+   EINA_SAFETY_ON_NULL_RETURN(user_data);
+
+   EOMDB("release before in queue");
+
+   tbm_surface_queue_remove_dequeuable_cb(eom_output->pp_queue, _e_eom_cb_dequeuable, eom_output);
+
+   _e_eom_pp_run(eom_output, EINA_FALSE);
+}
+
+static Eina_Bool
+_e_eom_pp_init(E_EomOutputPtr eom_output)
+{
+   tdm_error err = TDM_ERROR_NONE;
+   tdm_pp *pp = NULL;
+
+   if (eom_output->pp == NULL)
+     {
+        /* TODO: Add support for other formats */
+        eom_output->pp_queue = tbm_surface_queue_create(3, eom_output->width,eom_output->height,
+                                                        TBM_FORMAT_ARGB8888, TBM_BO_SCANOUT);
+        EINA_SAFETY_ON_NULL_RETURN_VAL(eom_output->pp_queue, EINA_FALSE);
+
+        pp = tdm_display_create_pp(g_eom->dpy, &err);
+        EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+        eom_output->pp = pp;
+     }
+
+   _e_eom_pp_run(eom_output, EINA_TRUE);
+
+   return EINA_TRUE;
+}
+
+static void
+_e_eom_pp_deinit(E_EomOutputPtr eom_output)
+{
+   if (eom_output->pp_queue)
+     {
+        EOMDB("flush and destroy queue");
+        tbm_surface_queue_flush(eom_output->pp_queue);
+        tbm_surface_queue_destroy(eom_output->pp_queue);
+        eom_output->pp_queue = NULL;
+     }
+
+   if (eom_output->pp)
+     {
+        tdm_pp_destroy(eom_output->pp);
+        eom_output->pp = NULL;
+     }
+}
+
+static E_EomClientPtr
+_e_eom_client_get_current_by_id(int id)
+{
+   Eina_List *l;
+   E_EomClientPtr client;
+
+   EINA_LIST_FOREACH(g_eom->clients, l, client)
+     {
+        if (client &&
+            client->current == EINA_TRUE &&
+            client->output_id == id)
+          return client;
+     }
+
+   return NULL;
+}
+
+static Eina_Bool
+_e_eom_output_start_mirror(E_EomOutputPtr eom_output)
+{
+   tdm_layer *hal_layer;
+   tdm_info_layer layer_info;
+   tdm_error tdm_err = TDM_ERROR_NONE;
+   int ret = 0;
+
+   if (eom_output->state == MIRROR)
+     return EINA_TRUE;
+
+   hal_layer = _e_eom_output_get_layer(eom_output);
+   EINA_SAFETY_ON_NULL_GOTO(hal_layer, err);
+
+   if (!_e_eom_pp_is_needed(g_eom->width, g_eom->height, eom_output->width, eom_output->height))
+     {
+        /* TODO: Internal and external outputs are equal */
+        EOMDB("internal and external outputs are equal");
+     }
+
+   tdm_err = tdm_layer_get_info(hal_layer, &layer_info);
+   EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, err);
+
+   EOMDB("layer info: %dx%d, pos (x:%d, y:%d, w:%d, h:%d,  dpos (x:%d, y:%d, w:%d, h:%d))",
+         layer_info.src_config.size.h,  layer_info.src_config.size.v,
+         layer_info.src_config.pos.x, layer_info.src_config.pos.y,
+         layer_info.src_config.pos.w, layer_info.src_config.pos.h,
+         layer_info.dst_pos.x, layer_info.dst_pos.y,
+         layer_info.dst_pos.w, layer_info.dst_pos.h);
+
+   eom_output->layer = hal_layer;
+
+   tdm_err = tdm_output_set_dpms(eom_output->output, TDM_OUTPUT_DPMS_ON);
+   EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, err);
+
+   _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_MIRROR);
+   eom_output->state = MIRROR;
+
+   ret = _e_eom_pp_init(eom_output);
+   EINA_SAFETY_ON_FALSE_GOTO(ret == EINA_TRUE, err);
+
+   return EINA_TRUE;
+
+err:
+
+   _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_NONE);
+   eom_output->state = NONE;
+
+   return EINA_FALSE;
+}
+
+static void
+_e_eom_output_start_presentation(E_EomOutputPtr eom_output)
+{
+   tdm_layer *hal_layer;
+   tdm_info_layer layer_info;
+   tdm_error tdm_err = TDM_ERROR_NONE;
+
+   hal_layer = _e_eom_output_get_layer(eom_output);
+   EINA_SAFETY_ON_NULL_GOTO(hal_layer, err);
+
+   tdm_err = tdm_layer_get_info(hal_layer, &layer_info);
+   EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, err);
+
+   EOMDB("layer info: %dx%d, pos (x:%d, y:%d, w:%d, h:%d,  dpos (x:%d, y:%d, w:%d, h:%d))",
+         layer_info.src_config.size.h,  layer_info.src_config.size.v,
+         layer_info.src_config.pos.x, layer_info.src_config.pos.y,
+         layer_info.src_config.pos.w, layer_info.src_config.pos.h,
+         layer_info.dst_pos.x, layer_info.dst_pos.y,
+         layer_info.dst_pos.w, layer_info.dst_pos.h);
+
+   eom_output->layer = hal_layer;
+
+   _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_PRESENTATION);
+
+   tdm_err = tdm_output_set_dpms(eom_output->output, TDM_OUTPUT_DPMS_ON);
+   EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, err);
+
+   return;
+
+err:
+
+   _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_NONE);
+   eom_output->state = NONE;
+
+   return;
+}
+
+static void
+_e_eom_output_all_buff_release(E_EomOutputPtr eom_output)
+{
+   Eina_List *l, *ll;
+   E_EomOutputBufferPtr  buff = NULL;
+
+   EINA_LIST_FOREACH_SAFE(eom_output->pending_buff, l, ll, buff)
+     {
+        EOMDB("delete pending tbm_buff:%p", buff->tbm_surface);
+        eom_output->pending_buff = eina_list_remove_list(eom_output->pending_buff, l);
+        _e_eom_output_buff_delete(buff);
+     }
+
+   eom_output->wait_buff = NULL;
+
+   EOMDB("delete show tbm_buff:%p", eom_output->show_buff ? eom_output->show_buff->tbm_surface : NULL);
+   _e_eom_output_buff_delete(eom_output->show_buff);
+   eom_output->show_buff = NULL;
+}
+
+static void
+_e_eom_output_deinit(E_EomOutputPtr eom_output)
+{
+   tdm_error err = TDM_ERROR_NONE;
+
+   if (eom_output->state == NONE)
+     return;
+
+   _e_eom_output_state_set_status(eom_output, TDM_OUTPUT_CONN_STATUS_DISCONNECTED);
+   _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_NONE);
+
+   if (_e_eom_client_get_current_by_id(eom_output->id))
+     eom_output->state = WAIT_PRESENTATION;
+   else
+     eom_output->state = NONE;
+
+   if (eom_output->layer)
+     {
+        err = tdm_layer_unset_buffer(eom_output->layer);
+        if (err != TDM_ERROR_NONE)
+          EOMDB("fail unset buffer:%d", err);
+
+        err = tdm_layer_commit(eom_output->layer, NULL, eom_output);
+        if (err != TDM_ERROR_NONE)
+          EOMDB ("fail commit on deleting output err:%d", err);
+     }
+
+   _e_eom_output_all_buff_release(eom_output);
+
+   _e_eom_pp_deinit(eom_output);
+
+   err = tdm_output_set_dpms(eom_output->output, TDM_OUTPUT_DPMS_OFF);
+   if (err != TDM_ERROR_NONE)
+     EOMER("set DPMS off:%d", err);
+}
+
+static const tdm_output_mode *
+_e_eom_output_get_best_mode(tdm_output *output)
+{
+   tdm_error ret = TDM_ERROR_NONE;
+   const tdm_output_mode *modes;
+   const tdm_output_mode *mode = NULL;
+   const tdm_output_mode *preferred_mode = NULL;
+   const tdm_output_mode *best_mode = NULL;
+   unsigned int best_value = 0;
+   unsigned int best_refresh = 0;
+   unsigned int value;
+   int i, count = 0;
+
+   ret = tdm_output_get_available_modes(output, &modes, &count);
+   if (ret != TDM_ERROR_NONE)
+     {
+        EOMER("tdm_output_get_available_modes fail(%d)", ret);
+        return NULL;
+     }
+
+   for (i = 0; i < count; i++)
+     {
+        if (modes[i].type & TDM_OUTPUT_MODE_TYPE_PREFERRED)
+          preferred_mode = &modes[i];
+
+        value = modes[i].vdisplay + modes[i].hdisplay;
+        if (value > best_value)
+          {
+             best_value = value;
+             best_refresh = modes[i].vrefresh;
+             best_mode = &modes[i];
+          }
+        else if (value == best_value)
+          {
+             if (modes[i].vrefresh > best_refresh)
+               {
+                  best_value = value;
+                  best_refresh = modes[i].vrefresh;
+                  best_mode = &modes[i];
+               }
+          }
+     }
+
+   if (preferred_mode)
+     mode = preferred_mode;
+   else if (best_mode)
+     mode = best_mode;
+
+   if (mode)
+     EOMDB("bestmode : %s, (%dx%d) r(%d), f(%d), t(%d)",
+           mode->name, mode->hdisplay, mode->vdisplay,
+           mode->vrefresh, mode->flags, mode->type);
+
+   return mode;
+}
+
+static Eina_Bool
+_e_eom_timer_delayed_presentation_mode(void *data)
+{
+   E_EomOutputPtr eom_output = NULL;
+
+   EOMDB("timer called %s", __FUNCTION__);
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_CANCEL);
+
+   eom_output = (E_EomOutputPtr )data;
+   eom_output->delay = NULL;
+
+   _e_eom_output_start_mirror(eom_output);
+
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static int
+_e_eom_output_connected(E_EomOutputPtr eom_output)
+{
+   tdm_output *output;
+   tdm_error ret = TDM_ERROR_NONE;
+   E_EomClientPtr iterator = NULL;
+   Eina_List *l;
+   const tdm_output_mode *mode;
+   unsigned int mmWidth, mmHeight;
+
+   output = eom_output->output;
+
+   ret = tdm_output_get_physical_size(output, &mmWidth, &mmHeight);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, -1);
+
+   /* XXX: TMD returns not correct Primary mode for external output,
+    * therefore we have to find it by ourself */
+   mode = _e_eom_output_get_best_mode(output);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(mode, -1);
+
+   ret = tdm_output_set_mode(output, mode);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, -1);
+
+   /* update eom_output connect */
+   eom_output->width = mode->hdisplay;
+   eom_output->height = mode->vdisplay;
+   eom_output->phys_width = mmWidth;
+   eom_output->phys_height = mmHeight;
+
+   EOMDB("Setup new output: %s", eom_output->name);
+
+   /* TODO: check output mode(presentation set) and HDMI type */
+
+   if (eom_output->state == WAIT_PRESENTATION)
+     {
+        EOMDB("Start Presentation");
+
+        if (eom_output->delay)
+          ecore_timer_del(eom_output->delay);
+        eom_output->delay = ecore_timer_add(EOM_DELAY_CHECK_TIMEOUT, _e_eom_timer_delayed_presentation_mode, eom_output);
+
+        _e_eom_output_start_presentation(eom_output);
+     }
+   else
+     {
+        EOMDB("Start Mirroring");
+        _e_eom_output_start_mirror(eom_output);
+     }
+
+   eom_output->connection = WL_EOM_STATUS_CONNECTION;
+
+   /* If there were previously connected clients to the output - notify them */
+   EINA_LIST_FOREACH(g_eom->clients, l, iterator)
+     {
+        if (iterator && iterator->resource)
+          {
+             EOMDB("Send MIRROR ON notification to clients");
+
+             if (iterator->current)
+               wl_eom_send_output_info(iterator->resource, eom_output->id,
+                                       eom_output->type, eom_output->mode,
+                                       eom_output->width, eom_output->height,
+                                       eom_output->phys_width, eom_output->phys_height,
+                                       eom_output->connection,
+                                       0,
+                                       _e_eom_output_state_get_attribute(eom_output),
+                                       EOM_OUTPUT_ATTRIBUTE_STATE_ACTIVE,
+                                       EOM_ERROR_NONE);
+             else
+               wl_eom_send_output_info(iterator->resource, eom_output->id,
+                                       eom_output->type, eom_output->mode,
+                                       eom_output->width, eom_output->height,
+                                       eom_output->phys_width, eom_output->phys_height,
+                                       eom_output->connection,
+                                       1, 0, 0, 0);
+          }
+     }
+
+   return 0;
+}
+
+static void
+_e_eom_output_disconnected(E_EomOutputPtr eom_output)
+{
+   E_EomClientPtr iterator = NULL;
+   Eina_List *l;
+
+   if (eom_output->delay)
+     ecore_timer_del(eom_output->delay);
+
+   if (eom_output->watchdog)
+     ecore_timer_del(eom_output->watchdog);
+
+   if (g_eom->rotate_output == eom_output)
+     {
+        if (g_eom->rotate_timer)
+          ecore_timer_del(g_eom->rotate_timer);
+        g_eom->rotate_timer = NULL;
+        g_eom->rotate_output = NULL;
+     }
+
+   /* update eom_output disconnect */
+   eom_output->width = 0;
+   eom_output->height = 0;
+   eom_output->phys_width = 0;
+   eom_output->phys_height = 0;
+   eom_output->connection = WL_EOM_STATUS_DISCONNECTION;
+
+   _e_eom_output_deinit(eom_output);
+
+   /* If there were previously connected clients to the output - notify them */
+   EINA_LIST_FOREACH(g_eom->clients, l, iterator)
+     {
+        if (iterator && iterator->resource)
+          {
+             EOMDB("Send MIRROR OFF notification to client: %p", iterator);
+             if (iterator->current)
+               wl_eom_send_output_info(iterator->resource, eom_output->id,
+                                       eom_output->type, eom_output->mode,
+                                       eom_output->width, eom_output->height,
+                                       eom_output->phys_width, eom_output->phys_height,
+                                       eom_output->connection,
+                                       0,
+                                       _e_eom_output_state_get_attribute(eom_output),
+                                       EOM_OUTPUT_ATTRIBUTE_STATE_INACTIVE,
+                                       EOM_ERROR_NONE);
+             else
+               wl_eom_send_output_info(iterator->resource, eom_output->id,
+                                       eom_output->type, eom_output->mode,
+                                       eom_output->width, eom_output->height,
+                                       eom_output->phys_width, eom_output->phys_height,
+                                       eom_output->connection,
+                                       1, 0, 0, 0);
+          }
+     }
+
+   EOMDB("Destory output: %s", eom_output->name);
+   eina_stringshare_del(eom_output->name);
+   eom_output->name = NULL;
+}
+
+static void
+_e_eom_cb_tdm_output_status_change(tdm_output *output, tdm_output_change_type type, tdm_value value, void *user_data)
+{
+   tdm_output_type tdm_type;
+   tdm_output_conn_status status, status_check;
+   tdm_error ret = TDM_ERROR_NONE;
+   const char *tmp_name;
+   char new_name[DRM_CONNECTOR_NAME_LEN];
+   E_EomOutputPtr eom_output = NULL, eom_output_tmp = NULL;
+   Eina_List *l;
+
+   g_eom->check_first_boot = 1;
+
+   if (type == TDM_OUTPUT_CHANGE_DPMS || g_eom->main_output_state == 0)
+     return;
+
+   if (g_eom->outputs)
+     {
+        EINA_LIST_FOREACH(g_eom->outputs, l, eom_output_tmp)
+          {
+             if (eom_output_tmp->output == output)
+               eom_output = eom_output_tmp;
+          }
+     }
+
+   EINA_SAFETY_ON_NULL_RETURN(eom_output);
+
+   ret = tdm_output_get_output_type(output, &tdm_type);
+   EINA_SAFETY_ON_FALSE_RETURN(ret == TDM_ERROR_NONE);
+
+   ret = tdm_output_get_conn_status(output, &status_check);
+   EINA_SAFETY_ON_FALSE_RETURN(ret == TDM_ERROR_NONE);
+
+   status = value.u32;
+
+   EOMDB("id (%d), type(%d, %d), status(%d, %d)", eom_output->id, type, tdm_type, status_check, status);
+
+   eom_output->type = (eom_output_type_e)tdm_type;
+   eom_output->status = status;
+
+   if (status == TDM_OUTPUT_CONN_STATUS_CONNECTED)
+     {
+        if (tdm_type < ALEN(eom_conn_types))
+          tmp_name = eom_conn_types[tdm_type];
+        else
+          tmp_name = "unknown";
+
+        /* TODO: What if there will more then one output of same type.
+         * e.g. "HDMI and HDMI" "LVDS and LVDS"*/
+        snprintf(new_name, sizeof(new_name), "%s-%d", tmp_name, 0);
+
+        eom_output->name = eina_stringshare_add(new_name);
+
+        e_comp_override_add();
+
+        _e_eom_output_connected(eom_output);
+     }
+   else if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+     {
+        e_comp_override_del();
+
+        _e_eom_output_disconnected(eom_output);
+     }
+}
+
+static Eina_Bool
+_e_eom_output_init(tdm_display *dpy)
+{
+   E_EomOutputPtr new_output = NULL;
+   tdm_output *output = NULL;
+   tdm_output_type type;
+   tdm_output_conn_status status;
+   const tdm_output_mode *mode = NULL;
+   tdm_error ret = TDM_ERROR_NONE;
+   unsigned int mmWidth, mmHeight;
+   int i, count;
+
+   ret = tdm_display_get_output_count(dpy, &count);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(count > 1, EINA_FALSE);
+
+   g_eom->output_count = count - 1;
+   EOMDB("external output count : %d", g_eom->output_count);
+
+   /* skip main output id:0 */
+   /* start from 1 */
+   for (i = 1; i < count; i++)
+     {
+        output = tdm_display_get_output(dpy, i, &ret);
+        EINA_SAFETY_ON_FALSE_GOTO(ret == TDM_ERROR_NONE, err);
+        EINA_SAFETY_ON_NULL_GOTO(output, err);
+
+        ret = tdm_output_get_output_type(output, &type);
+        EINA_SAFETY_ON_FALSE_GOTO(ret == TDM_ERROR_NONE, err);
+
+        new_output = E_NEW(E_EomOutput, 1);
+        EINA_SAFETY_ON_NULL_GOTO(new_output, err);
+
+        ret = tdm_output_get_conn_status(output, &status);
+        if (ret != TDM_ERROR_NONE)
+          {
+             EOMER("tdm_output_get_conn_status fail(%d)", ret);
+             free(new_output);
+             goto err;
+          }
+
+        new_output->id = i;
+        new_output->type = type;
+        new_output->status = status;
+        new_output->mode = EOM_OUTPUT_MODE_NONE;
+        new_output->connection = WL_EOM_STATUS_NONE;
+        new_output->output = output;
+
+        ret = tdm_output_add_change_handler(output, _e_eom_cb_tdm_output_status_change, NULL);
+        if (ret != TDM_ERROR_NONE)
+          {
+             EOMER("tdm_output_add_change_handler fail(%d)", ret);
+             free(new_output);
+             goto err;
+          }
+
+        if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+          {
+             EOMDB("create(%d)output, type:%d, status:%d",
+                   new_output->id, new_output->type, new_output->status);
+             g_eom->outputs = eina_list_append(g_eom->outputs, new_output);
+             continue;
+          }
+
+        new_output->status = TDM_OUTPUT_CONN_STATUS_CONNECTED;
+
+        ret = tdm_output_get_mode(output, &mode);
+        if (ret != TDM_ERROR_NONE)
+          {
+             EOMER("tdm_output_get_mode fail(%d)", ret);
+             free(new_output);
+             goto err;
+          }
+
+        if (mode == NULL)
+          {
+             new_output->width = 0;
+             new_output->height = 0;
+          }
+        else
+          {
+             new_output->width = mode->hdisplay;
+             new_output->height = mode->vdisplay;
+          }
+
+        ret = tdm_output_get_physical_size(output, &mmWidth, &mmHeight);
+        if (ret != TDM_ERROR_NONE)
+          {
+             EOMER("tdm_output_get_conn_status fail(%d)", ret);
+             free(new_output);
+             goto err;
+          }
+
+        new_output->phys_width = mmWidth;
+        new_output->phys_height = mmHeight;
+
+        EOMDB("create(%d)output, type:%d, status:%d, w:%d, h:%d, mm_w:%d, mm_h:%d",
+              new_output->id, new_output->type, new_output->status,
+              new_output->width, new_output->height, new_output->phys_width, new_output->phys_height);
+
+        g_eom->outputs = eina_list_append(g_eom->outputs, new_output);
+     }
+
+   return EINA_TRUE;
+
+err:
+   if (g_eom->outputs)
+     {
+        Eina_List *l;
+        E_EomOutputPtr output;
+
+        EINA_LIST_FOREACH(g_eom->outputs, l, output)
+           free(output);
+
+        eina_list_free(g_eom->outputs);
+
+        g_eom->outputs = NULL;
+     }
+
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_e_eom_init_internal()
+{
+   g_eom->dpy = e_comp->e_comp_screen->tdisplay;
+   EINA_SAFETY_ON_NULL_GOTO(g_eom->dpy, err);
+
+   g_eom->bufmgr = e_comp->e_comp_screen->bufmgr;
+   EINA_SAFETY_ON_NULL_GOTO(g_eom->bufmgr, err);
+
+   if (_e_eom_output_init(g_eom->dpy) != EINA_TRUE)
+     {
+        EOMER("_e_eom_output_init fail");
+        goto err;
+     }
+
+   return EINA_TRUE;
+
+err:
+
+   if (g_eom->bufmgr)
+     g_eom->bufmgr = NULL;
+
+   if (g_eom->dpy)
+     g_eom->dpy = NULL;
+
+   return EINA_FALSE;
+}
+
+static void
+_e_eom_deinit()
+{
+   Ecore_Event_Handler *h = NULL;
+
+   if (g_eom == NULL) return;
+
+   if (g_eom->handlers)
+     {
+        EINA_LIST_FREE(g_eom->handlers, h)
+           ecore_event_handler_del(h);
+
+        g_eom->handlers = NULL;
+     }
+
+   if (g_eom->outputs)
+     {
+        Eina_List *l;
+        E_EomOutputPtr output;
+
+        EINA_LIST_FOREACH(g_eom->outputs, l, output)
+           free(output);
+
+        eina_list_free(g_eom->outputs);
+
+        g_eom->outputs = NULL;
+     }
+
+   if (g_eom->dpy)
+     g_eom->dpy = NULL;
+
+   if (g_eom->bufmgr)
+     g_eom->bufmgr = NULL;
+
+   if (g_eom->global)
+     wl_global_destroy(g_eom->global);
+   g_eom->global = NULL;
+
+   E_FREE(g_eom);
+}
+
+static E_EomClientPtr
+_e_eom_client_get_by_resource(struct wl_resource *resource)
+{
+   Eina_List *l;
+   E_EomClientPtr client;
+
+   EINA_LIST_FOREACH(g_eom->clients, l, client)
+     {
+        if (client && client->resource == resource)
+          return client;
+     }
+
+   return NULL;
+}
+
+static E_EomOutputPtr
+_e_eom_output_get_by_id(int id)
+{
+   Eina_List *l;
+   E_EomOutputPtr output;
+
+   EINA_LIST_FOREACH(g_eom->outputs, l, output)
+     {
+        if (output && output->id == id)
+          return output;
+     }
+
+   return NULL;
+}
+
+static E_EomOutputPtr
+_e_eom_output_by_ec_child_get(E_Client *ec)
+{
+   E_EomOutputPtr eom_output = NULL;
+   E_EomClientPtr eom_client = NULL;
+   E_Client *parent = NULL;
+   Eina_List *l;
+
+   EINA_LIST_FOREACH(g_eom->outputs, l, eom_output)
+     {
+        eom_client = _e_eom_client_get_current_by_id(eom_output->id);
+        if (!eom_client)
+          continue;
+
+        if (eom_client->ec == ec)
+          return eom_output;
+
+        if (!ec->comp_data || !ec->comp_data->sub.data)
+          continue;
+
+        parent = ec->comp_data->sub.data->parent;
+        while (parent)
+          {
+             if (parent == eom_client->ec)
+               return eom_output;
+
+             if (!parent->comp_data || !parent->comp_data->sub.data)
+               break;
+
+             parent = parent->comp_data->sub.data->parent;
+          }
+     }
+
+   return NULL;
+}
+
+static void
+_e_eom_output_hide_layers(E_EomOutputPtr eom_output)
+{
+   tdm_layer * layer = NULL;
+
+   if (!eom_output || eom_output->state == NONE)
+     return;
+
+   layer = e_comp_wl_video_layer_get(eom_output->output);
+   if (!layer)
+     return;
+
+   /* XXX: sometimes video buffers are keep showing on a layer, therefore
+    * we have to clear those stuck buffers from a layer */
+   tdm_layer_unset_buffer(layer);
+}
+
+static void
+_e_eom_top_ec_angle_get(void)
+{
+   E_Client *ec;
+
+   ec = _e_eom_top_visible_ec_get();
+   if (ec)
+     {
+        g_eom->angle = ec->e.state.rot.ang.curr;
+        EOMDB("top ec rotate angle:%d", g_eom->angle);
+     }
+}
+
+static void
+_e_eom_cb_wl_eom_client_destory(struct wl_resource *resource)
+{
+   E_EomClientPtr client = NULL, iterator = NULL;
+   E_EomOutputPtr output = NULL;
+   Eina_List *l = NULL;
+   Eina_Bool ret;
+
+   EOMDB("=======================>  CLENT UNBIND");
+
+   EINA_SAFETY_ON_NULL_RETURN(resource);
+
+   client = _e_eom_client_get_by_resource(resource);
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   g_eom->clients = eina_list_remove(g_eom->clients, client);
+
+   if (client->current == EINA_FALSE)
+     goto end2;
+
+   output = _e_eom_output_get_by_id(client->output_id);
+   EINA_SAFETY_ON_NULL_GOTO(output, end2);
+
+   ret = _e_eom_output_state_set_attribute(output, EOM_OUTPUT_ATTRIBUTE_NONE);
+   (void)ret;
+
+   if (output->state == NONE)
+     goto end;
+
+   if (output->state == WAIT_PRESENTATION)
+     {
+        output->state = NONE;
+        goto end;
+     }
+
+   /* If a client has been disconnected and mirror mode has not
+    * been restored, start mirror mode
+    */
+   _e_eom_top_ec_angle_get();
+   _e_eom_output_start_mirror(output);
+
+end:
+
+   /* Notify eom clients which are binded to a concrete output that the
+    * state and mode of the output has been changed */
+   EINA_LIST_FOREACH(g_eom->clients, l, iterator)
+     {
+        if (iterator && iterator != client && iterator->output_id == output->id)
+          {
+             wl_eom_send_output_attribute(iterator->resource, output->id,
+                                          _e_eom_output_state_get_attribute(output),
+                                          _e_eom_output_state_get_attribute_state(output),
+                                          EOM_OUTPUT_MODE_NONE);
+
+             wl_eom_send_output_mode(iterator->resource, output->id,
+                                     _e_eom_output_state_get_mode(output));
+          }
+     }
+
+end2:
+
+   free(client);
+}
+
+static void
+_e_eom_cb_wl_request_set_attribute(struct wl_client *client, struct wl_resource *resource, uint32_t output_id, uint32_t attribute)
+{
+   eom_error_e eom_error = EOM_ERROR_NONE;
+   E_EomClientPtr eom_client = NULL, current_eom_client = NULL, iterator = NULL;
+   E_EomOutputPtr eom_output = NULL;
+   Eina_Bool ret = EINA_FALSE;
+   Eina_List *l;
+
+   eom_client = _e_eom_client_get_by_resource(resource);
+   EINA_SAFETY_ON_NULL_RETURN(eom_client);
+
+   /* Bind the client with a concrete output */
+   eom_client->output_id = output_id;
+
+   eom_output = _e_eom_output_get_by_id(output_id);
+   EINA_SAFETY_ON_NULL_GOTO(eom_output, no_output);
+
+   EOMDB("Set attribute:%d", attribute);
+
+   if (eom_client->current == EINA_TRUE && eom_output->id == eom_client->output_id)
+     {
+        /* Current client can set any flag it wants */
+        _e_eom_output_state_set_force_attribute(eom_output, attribute);
+     }
+   else if (eom_output->id == eom_client->output_id)
+     {
+        /* A client is trying to set new attribute */
+        ret = _e_eom_output_state_set_attribute(eom_output, attribute);
+        if (ret == EINA_FALSE)
+          {
+             EOMDB("set attribute FAILED");
+
+             eom_error = EOM_ERROR_INVALID_PARAMETER;
+             goto end;
+          }
+     }
+   else
+     return;
+
+   /* If client has set EOM_OUTPUT_ATTRIBUTE_NONE switching to mirror mode */
+   if (attribute == EOM_OUTPUT_ATTRIBUTE_NONE && eom_output->state != MIRROR)
+     {
+        eom_client->current = EINA_FALSE;
+
+        _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_NONE);
+        _e_eom_output_state_set_attribute(eom_output, EOM_OUTPUT_ATTRIBUTE_NONE);
+
+        if (eom_output->status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+          {
+             EOMDB("output:%d is disconnected", output_id);
+             goto end;
+          }
+
+        ret = _e_eom_output_start_mirror(eom_output);
+        EINA_SAFETY_ON_FALSE_GOTO(ret == EINA_TRUE, end);
+
+        /* If mirror mode has been ran notify all clients about that */
+        EOMDB("client set NONE attribute, send new info to previous current client");
+        EINA_LIST_FOREACH(g_eom->clients, l, iterator)
+          {
+             if (iterator && iterator->output_id == output_id)
+               {
+                  wl_eom_send_output_attribute(iterator->resource, eom_output->id,
+                                               _e_eom_output_state_get_attribute(eom_output),
+                                               _e_eom_output_state_get_attribute_state(eom_output),
+                                               EOM_ERROR_NONE);
+
+                  wl_eom_send_output_mode(iterator->resource, eom_output->id,
+                                          _e_eom_output_state_get_mode(eom_output));
+               }
+          }
+
+        return;
+     }
+
+end:
+
+   /* If client was not able to set attribute send LOST event to it */
+   if (eom_error == EOM_ERROR_INVALID_PARAMETER)
+     {
+        EOMDB("client failed to set attribute");
+
+        wl_eom_send_output_attribute(eom_client->resource, eom_output->id,
+                                     _e_eom_output_state_get_attribute(eom_output),
+                                     EOM_OUTPUT_ATTRIBUTE_STATE_LOST,
+                                     eom_error);
+        return;
+     }
+
+   /* Send changes to the caller-client */
+   wl_eom_send_output_attribute(eom_client->resource, eom_output->id,
+                                _e_eom_output_state_get_attribute(eom_output),
+                                _e_eom_output_state_get_attribute_state(eom_output),
+                                eom_error);
+
+   current_eom_client = _e_eom_client_get_current_by_id(eom_output->id);
+   EOMDB("Substitute current client: new:%p, old:%p",eom_client, current_eom_client );
+
+   /* Send changes to previous current client */
+   if (eom_client->current == EINA_FALSE && current_eom_client)
+     {
+        current_eom_client->current = EINA_FALSE;
+
+        /* Actually deleting of buffers right here is a hack intended to
+         * send release events of buffers to current client, since it could
+         * be locked until it get 'release' event */
+        EOMDB("Send changes to previous current client, and delete buffers");
+        _e_eom_output_all_buff_release(eom_output);
+
+        wl_eom_send_output_attribute(current_eom_client->resource, eom_output->id,
+                                     _e_eom_output_state_get_attribute(eom_output),
+                                     EOM_OUTPUT_ATTRIBUTE_STATE_LOST,
+                                     EOM_ERROR_NONE);
+     }
+
+   /* Set the client as current client of the eom_output */
+   eom_client->current= EINA_TRUE;
+
+   _e_eom_output_hide_layers(eom_output);
+
+   if (eom_output->status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+     eom_output->state = WAIT_PRESENTATION;
+
+   return;
+
+   /* Get here if EOM does not have output referred by output_id */
+no_output:
+
+   wl_eom_send_output_attribute(eom_client->resource, output_id,
+                                EOM_OUTPUT_ATTRIBUTE_NONE,
+                                EOM_OUTPUT_ATTRIBUTE_STATE_NONE,
+                                EOM_ERROR_NO_SUCH_DEVICE);
+
+   wl_eom_send_output_mode(eom_client->resource, output_id,
+                           EOM_OUTPUT_MODE_NONE);
+
+   wl_eom_send_output_type(eom_client->resource, output_id,
+                           EOM_OUTPUT_ATTRIBUTE_STATE_NONE,
+                           TDM_OUTPUT_CONN_STATUS_DISCONNECTED);
+   return;
+}
+
+static Eina_Bool
+_e_eom_cb_comp_object_redirected(void *data, E_Client *ec)
+{
+   E_EomCompObjectInterceptHookData *hook_data;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_TRUE);
+
+   hook_data = (E_EomCompObjectInterceptHookData* )data;
+
+   if (!hook_data->ec || !hook_data->hook)
+     return EINA_TRUE;
+
+   if (hook_data->ec != ec)
+     return EINA_TRUE;
+
+   /* Hide the window from Enlightenment main screen */
+   e_client_redirected_set(ec, EINA_FALSE);
+
+   e_comp_object_intercept_hook_del(hook_data->hook);
+
+   g_eom->comp_object_intercept_hooks = eina_list_remove(g_eom->comp_object_intercept_hooks, hook_data);
+
+   free(hook_data);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_eom_util_add_comp_object_redirected_hook(E_Client *ec)
+{
+   E_EomCompObjectInterceptHookData *hook_data = NULL;
+   E_Comp_Object_Intercept_Hook *hook = NULL;
+
+   hook_data = E_NEW(E_EomCompObjectInterceptHookData, 1);
+   EINA_SAFETY_ON_NULL_GOTO(hook_data, err);
+
+   hook_data->ec = ec;
+
+   hook = e_comp_object_intercept_hook_add(E_COMP_OBJECT_INTERCEPT_HOOK_SHOW_HELPER,
+                                           _e_eom_cb_comp_object_redirected, hook_data);
+   EINA_SAFETY_ON_NULL_GOTO(hook, err);
+
+   hook_data->hook = hook;
+
+   g_eom->comp_object_intercept_hooks = eina_list_append(g_eom->comp_object_intercept_hooks, hook_data);
+
+   EOMDB("_e_eom_redirected_hook have been added");
+   return EINA_TRUE;
+
+err:
+
+   if (hook_data)
+     free(hook_data);
+   return EINA_FALSE;
+}
+
+static void
+_e_eom_window_set_internal(struct wl_resource *resource, int output_id, E_Client *ec)
+{
+   E_EomOutputPtr eom_output = NULL;
+   E_EomClientPtr eom_client = NULL;
+   E_Comp_Client_Data *cdata = NULL;
+   Eina_Bool ret = EINA_FALSE;
+
+   if (resource == NULL || output_id <= 0 || ec == NULL)
+     return;
+
+   cdata = ec->comp_data;
+   EINA_SAFETY_ON_NULL_RETURN(cdata);
+   EINA_SAFETY_ON_NULL_RETURN(cdata->shell.configure_send);
+
+   eom_client = _e_eom_client_get_by_resource(resource);
+   EINA_SAFETY_ON_NULL_RETURN(eom_client);
+
+   eom_output = _e_eom_output_get_by_id(output_id);
+   if (eom_output == NULL)
+     {
+        wl_eom_send_output_set_window(resource, output_id, WL_EOM_ERROR_NO_OUTPUT);
+        return;
+     }
+
+   ret = _e_eom_util_add_comp_object_redirected_hook(ec);
+   EINA_SAFETY_ON_FALSE_RETURN(ret == EINA_TRUE);
+
+   EOMDB("e_comp_object_redirected_set (ec:%p)(ec->frame:%p)\n", ec, ec->frame);
+
+   /* Send reconfigure event to a client which will resize its window to
+    * external output resolution in respond */
+   cdata->shell.configure_send(ec->comp_data->shell.surface, 0, eom_output->width, eom_output->height);
+
+   /* ec is used in buffer_change callback for distinguishing external ec and its buffers */
+   eom_client->ec = ec;
+
+   if (eom_client->current == EINA_TRUE)
+     wl_eom_send_output_set_window(resource, eom_output->id, WL_EOM_ERROR_NONE);
+   else
+     wl_eom_send_output_set_window(resource, eom_output->id, WL_EOM_ERROR_OUTPUT_OCCUPIED);
+}
+
+static void
+_e_eom_cb_wl_request_set_xdg_window(struct wl_client *client, struct wl_resource *resource, uint32_t output_id, struct wl_resource *surface)
+{
+   E_Client *ec = NULL;
+
+   if (resource == NULL || output_id <= 0 || surface == NULL)
+     return;
+
+   EOMDB("set xdg output id:%d resource:%p surface:%p", output_id, resource, surface);
+
+   if (!(ec = wl_resource_get_user_data(surface)))
+     {
+        wl_resource_post_error(surface,WL_DISPLAY_ERROR_INVALID_OBJECT, "No Client For Shell Surface");
+        return;
+     }
+
+   _e_eom_window_set_internal(resource, output_id, ec);
+}
+
+static void
+_e_eom_cb_wl_request_set_shell_window(struct wl_client *client, struct wl_resource *resource, uint32_t output_id, struct wl_resource *surface)
+{
+   E_Client *ec = NULL;
+
+   if (resource == NULL || output_id <= 0 || surface == NULL)
+     return;
+
+   EOMDB("set shell output id:%d resource:%p surface:%p", output_id, resource, surface);
+
+   if (!(ec = wl_resource_get_user_data(surface)))
+     {
+        wl_resource_post_error(surface,WL_DISPLAY_ERROR_INVALID_OBJECT, "No Client For Shell Surface");
+        return;
+     }
+
+   _e_eom_window_set_internal(resource, output_id, ec);
+}
+
+static void
+_e_eom_cb_wl_request_get_output_info(struct wl_client *client, struct wl_resource *resource, uint32_t output_id)
+{
+   EOMDB("output:%d", output_id);
+
+   if (g_eom->outputs)
+     {
+        Eina_List *l;
+        E_EomOutputPtr output = NULL;
+
+        EINA_LIST_FOREACH(g_eom->outputs, l, output)
+          {
+             if (output->id == output_id)
+               {
+                  EOMDB("send - id : %d, type : %d, mode : %d, w : %d, h : %d, w_mm : %d, h_mm : %d, conn : %d",
+                        output->id, output->type, output->mode, output->width, output->height,
+                        output->phys_width, output->phys_height, output->status);
+
+                  wl_eom_send_output_info(resource, output->id, output->type, output->mode, output->width, output->height,
+                                          output->phys_width, output->phys_height, output->connection,
+                                          1, 0, 0, 0);
+               }
+          }
+     }
+}
+
+static const struct wl_eom_interface _e_eom_wl_implementation =
+{
+   _e_eom_cb_wl_request_set_attribute,
+   _e_eom_cb_wl_request_set_xdg_window,
+   _e_eom_cb_wl_request_set_shell_window,
+   _e_eom_cb_wl_request_get_output_info
+};
+
+static void
+_e_eom_cb_wl_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+   struct wl_resource *resource = NULL;
+   E_EomClientPtr new_client = NULL;
+   E_EomPtr eom = NULL;
+   E_EomOutputPtr output = NULL;
+   Eina_List *l;
+
+   EINA_SAFETY_ON_NULL_RETURN(data);
+   eom = data;
+
+   resource = wl_resource_create(client, &wl_eom_interface, MIN(version, 1), id);
+   if (resource == NULL)
+     {
+        EOMER("resource is null. (version :%d, id:%d)", version, id);
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   wl_resource_set_implementation(resource, &_e_eom_wl_implementation, eom, _e_eom_cb_wl_eom_client_destory);
+
+   EOMDB("send - output count : %d", g_eom->output_count);
+
+   wl_eom_send_output_count(resource, g_eom->output_count);
+
+   if (g_eom->outputs)
+     {
+        EINA_LIST_FOREACH(g_eom->outputs, l, output)
+          {
+             EOMDB("send - id : %d, type : %d, mode : %d, w : %d, h : %d, w_mm : %d, h_mm : %d, conn : %d",
+                   output->id, output->type, output->mode, output->width, output->height,
+                   output->phys_width, output->phys_height, output->status);
+             wl_eom_send_output_info(resource, output->id, output->type, output->mode, output->width, output->height,
+                                     output->phys_width, output->phys_height, output->connection,
+                                     1, 0, 0, 0);
+          }
+     }
+
+   new_client = E_NEW(E_EomClient, 1);
+   EINA_SAFETY_ON_NULL_RETURN(new_client);
+
+   new_client->resource = resource;
+   new_client->current = EINA_FALSE;
+   new_client->output_id = -1;
+   new_client->ec = NULL;
+
+   g_eom->clients = eina_list_append(g_eom->clients, new_client);
+
+   EOMDB("=======================>  BIND CLENT");
+}
+
+static Eina_Bool
+_e_eom_cb_ecore_drm_activate(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+
+   Ecore_Drm_Event_Activate *e = NULL;
+
+   if ((!event) || (!data))
+     return ECORE_CALLBACK_PASS_ON;
+
+   e = event;
+   (void) e;
+
+   EOMDB("e->active:%d", e->active);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_boot_connection_check(void *data)
+{
+   E_EomOutputPtr eom_output;
+   tdm_output *output = NULL;
+   tdm_output_type tdm_type = TDM_OUTPUT_TYPE_Unknown;
+   tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
+   tdm_error ret = TDM_ERROR_NONE;
+   const char *tmp_name;
+   char new_name[DRM_CONNECTOR_NAME_LEN];
+   Eina_List *l;
+
+   if (g_eom->check_first_boot != 0)
+     {
+        g_eom->timer = NULL;
+        return ECORE_CALLBACK_CANCEL;
+     }
+
+   g_eom->check_first_boot = 1;
+
+   if (g_eom->outputs)
+     {
+        EINA_LIST_FOREACH(g_eom->outputs, l, eom_output)
+          {
+             if (eom_output->id == 0)
+               continue;
+
+             output = eom_output->output;
+             if (output == NULL)
+               {
+                  EOMER("output is null fail");
+                  continue;
+               }
+
+             ret = tdm_output_get_conn_status(output, &status);
+             if (ret != TDM_ERROR_NONE)
+               {
+                  EOMER("tdm_output_get_conn_status fail(%d)", ret);
+                  continue;
+               }
+
+             if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+               continue;
+
+             ret = tdm_output_get_output_type(output, &tdm_type);
+             if (ret != TDM_ERROR_NONE)
+               {
+                  EOMER("tdm_output_get_output_type fail(%d)", ret);
+                  continue;
+               }
+
+             if (tdm_type < ALEN(eom_conn_types))
+               tmp_name = eom_conn_types[tdm_type];
+             else
+               tmp_name = "unknown";
+             /* TODO: What if there will more then one output of same type.
+              * e.g. "HDMI and HDMI" "LVDS and LVDS"*/
+             snprintf(new_name, sizeof(new_name), "%s-%d", tmp_name, 0);
+
+             eom_output->type = (eom_output_type_e)tdm_type;
+             eom_output->name = eina_stringshare_add(new_name);
+             eom_output->status = status;
+
+             _e_eom_output_connected(eom_output);
+          }
+     }
+   g_eom->timer = NULL;
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static Eina_Bool
+_e_eom_cb_ecore_drm_output(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   Ecore_Drm_Event_Output *e = NULL;
+   char buff[PATH_MAX];
+
+   if (!(e = event)) return ECORE_CALLBACK_PASS_ON;
+
+   EOMDB("id:%d (x,y,w,h):(%d,%d,%d,%d) (w_mm,h_mm):(%d,%d) refresh:%d subpixel_order:%d transform:%d make:%s model:%s name:%s plug:%d",
+         e->id, e->x, e->y, e->w, e->h, e->phys_width, e->phys_height, e->refresh, e->subpixel_order, e->transform, e->make, e->model, e->name, e->plug);
+
+   snprintf(buff, sizeof(buff), "%s", e->name);
+
+   /* main output */
+   if (e->id == 0)
+     {
+        if (e->plug == 1)
+          {
+             g_eom->width = e->w;
+             g_eom->height = e->h;
+             if (g_eom->main_output_name == NULL)
+               g_eom->main_output_name = strdup(buff);
+
+             g_eom->main_output_state = 1;
+
+             if (g_eom->check_first_boot == 0)
+               {
+                  if (g_eom->timer)
+                    ecore_timer_del(g_eom->timer);
+                  g_eom->timer = ecore_timer_add(EOM_CONNECT_CHECK_TIMEOUT, _e_eom_boot_connection_check, NULL);
+               }
+          }
+        else
+          {
+             g_eom->width = -1;
+             g_eom->height = -1;
+             if (g_eom->main_output_name)
+               free(g_eom->main_output_name);
+
+             g_eom->main_output_state = 0;
+          }
+     }
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static E_EomClientPtr
+_e_eom_client_get_current_by_ec(E_Client *ec)
+{
+   Eina_List *l;
+   E_EomClientPtr client;
+
+   EINA_LIST_FOREACH(g_eom->clients, l, client)
+     {
+        if (client && client->current == EINA_TRUE && client->ec == ec)
+          return client;
+     }
+
+   return NULL;
+}
+
+static void
+_e_eom_tbm_buffer_release_ext_mod(E_EomOutputPtr eom_output, tbm_surface_h srfc, void * eom_buff)
+{
+   EOMDB("============>  EXT END     tbm_buff:%p E_EomBuffer:%p", srfc, eom_buff);
+   _e_eom_buffer_destroy(eom_buff);
+}
+
+static Eina_Bool
+_e_eom_cb_client_buffer_change(void *data, int type, void *event)
+{
+   E_Comp_Wl_Buffer *wl_buffer = NULL;
+   E_EomClientPtr eom_client = NULL, eom_client_itr = NULL;
+   E_EomOutputPtr eom_output = NULL;
+   E_Event_Client *ev = event;
+   E_Client *ec = NULL;
+   tbm_surface_h tbm_buffer = NULL;
+   Eina_List *l;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ev, ECORE_CALLBACK_PASS_ON);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ev->ec, ECORE_CALLBACK_PASS_ON);
+
+   ec = ev->ec;
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(e_object_is_del(E_OBJECT(ec)),
+                                  ECORE_CALLBACK_PASS_ON);
+
+   eom_client = _e_eom_client_get_current_by_ec(ec);
+   if (eom_client == NULL)
+     return ECORE_CALLBACK_PASS_ON;
+
+   eom_output = _e_eom_output_get_by_id(eom_client->output_id);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(eom_output, ECORE_CALLBACK_PASS_ON);
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ec->pixmap, ECORE_CALLBACK_PASS_ON);
+
+   wl_buffer = e_pixmap_resource_get(ec->pixmap);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(wl_buffer, ECORE_CALLBACK_PASS_ON);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(wl_buffer->resource, ECORE_CALLBACK_PASS_ON);
+
+   /* Since Enlightenment client has reconfigured its window to fit
+    * external output resolution and Enlightenment no nothing about
+    * external outputs Enlightenment sees that client's resolution
+    * differs form main screen resolution. Therefore, Enlightenment
+    * is trying to resize it back to main screen resolution. It uses
+    * timer for that purpose. To forbid it just delte the timer */
+
+   /* TODO: It works but maybe there is better solution exists ?
+    * Also I do not know how it affects on performance */
+   if (ec->map_timer)
+     {
+        EOMDB("delete map_timer");
+        E_FREE_FUNC(ec->map_timer, ecore_timer_del);
+     }
+
+   /* TODO: Support buffers smaller then output resolution */
+   if (wl_buffer->w != eom_output->width ||
+       wl_buffer->h != eom_output->height )
+     {
+        EOMER("tbm_buffer does not fit output's resolution");
+        return ECORE_CALLBACK_PASS_ON;
+     }
+
+   /* TODO: support different SHMEM buffers etc. */
+   tbm_buffer = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, wl_buffer->resource);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_buffer, ECORE_CALLBACK_PASS_ON);
+
+   E_EomBufferPtr eom_buff = _e_eom_buffer_create(wl_buffer);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(eom_buff, ECORE_CALLBACK_PASS_ON);
+
+   EOMDB("===============>  EXT START   tbm_buff:%p", tbm_buffer);
+
+#ifdef EOM_DUMP_PRESENTATION_BUFFERS
+   char file[256];
+   static int i;
+   snprintf(file, sizeof file, "%s_%d", "eom_external", i++);
+   tbm_surface_internal_dump_buffer(tbm_buffer, file, i++, 0);
+#endif
+
+   if(!_e_eom_output_show(eom_output, tbm_buffer, _e_eom_tbm_buffer_release_ext_mod, eom_buff))
+     {
+        EOMDB("===============>  EXT ENDERR  tbm_buff:%p", tbm_buffer);
+        EOMDB("_e_eom_add_buff_to_show fail");
+        _e_eom_buffer_destroy(eom_buff);
+        return ECORE_CALLBACK_PASS_ON;
+     }
+
+   if (eom_output->state == WAIT_PRESENTATION)
+     {
+        EOMDB("remove delayed presentation timer");
+        if (eom_output->delay)
+          ecore_timer_del(eom_output->delay);
+     }
+
+   if (eom_output->state != PRESENTATION)
+     {
+        _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_PRESENTATION);
+
+        EINA_LIST_FOREACH(g_eom->clients, l, eom_client_itr)
+          {
+             if (eom_client_itr->output_id == eom_output->id)
+               wl_eom_send_output_mode(eom_client_itr->resource, eom_output->id,
+                                       _e_eom_output_state_get_mode(eom_output));
+          }
+
+        eom_output->state = PRESENTATION;
+     }
+
+   EOMDB("===============<  EXT START");
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_cb_rotation_effect_ready(void *data, int type, void *event)
+{
+   E_EomPtr eom = NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_PASS_ON);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(event, ECORE_CALLBACK_PASS_ON);
+
+   eom = data;
+
+   eom->rotate_state = ROTATE_INIT;
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_rotate(void *data)
+{
+   g_eom->rotate_state = ROTATE_DONE;
+
+   _e_eom_top_ec_angle_get();
+
+   if (g_eom->rotate_output)
+     _e_eom_pp_run(g_eom->rotate_output, EINA_FALSE);
+
+   g_eom->rotate_timer = NULL;
+
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static Eina_Bool
+_e_eom_cb_rotation_effect_cancel(void *data, int type, void *event)
+{
+   E_EomPtr eom = NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_PASS_ON);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(event, ECORE_CALLBACK_PASS_ON);
+
+   eom = data;
+
+   eom->rotate_state = ROTATE_CANCEL;
+
+   if (eom->rotate_timer)
+     ecore_timer_del(eom->rotate_timer);
+
+   if (g_eom->rotate_output)
+     eom->rotate_timer = ecore_timer_add(EOM_ROTATE_DELAY_TIMEOUT, _e_eom_rotate, NULL);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_cb_rotation_effect_done(void *data, int type, void *event)
+{
+   E_Event_Zone_Rotation_Effect_Done *ev;
+   E_EomPtr eom = NULL;
+   E_Zone *zone;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_PASS_ON);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(event, ECORE_CALLBACK_PASS_ON);
+
+   ev = event;
+   eom = data;
+
+   zone = ev->zone;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(zone, ECORE_CALLBACK_PASS_ON);
+
+   EOMDB("-----------------------------------------------------");
+
+   EOMDB("effect END: angles: prev:%d  curr:%d  next:%d  sub:%d",
+         zone->rot.prev, zone->rot.curr,
+         zone->rot.next, zone->rot.sub);
+
+   EOMDB("effect END: rotate angle:%d", eom->angle);
+
+   EOMDB("-----------------------------------------------------");
+
+   eom->angle = zone->rot.curr;
+
+   if (eom->rotate_timer)
+     ecore_timer_del(eom->rotate_timer);
+
+   if (g_eom->rotate_output)
+     eom->rotate_timer = ecore_timer_add(EOM_ROTATE_DELAY_TIMEOUT, _e_eom_rotate, NULL);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_cb_rotation_end(void *data, int evtype EINA_UNUSED, void *event)
+{
+   E_Client *ec = NULL;
+   E_Event_Client *ev = NULL;
+   E_EomPtr eom = NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_PASS_ON);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(event, ECORE_CALLBACK_PASS_ON);
+
+   ev = event;
+   eom = data;
+   ec = ev->ec;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ec, ECORE_CALLBACK_PASS_ON);
+
+   /* As I understand E sends rotate events to all visible apps, thus EOM's
+    * "_e_eom_cb_rotation_end" will be called for each visible E_Client.
+    * Therefore we are interested only in the first change of angle, other
+    * events with the same angle value will be ignored. */
+   if (eom->angle == ec->e.state.rot.ang.curr)
+     return ECORE_CALLBACK_PASS_ON;
+
+   if (eom->rotate_state == ROTATE_NONE)
+     {
+        eom->angle = ec->e.state.rot.ang.curr;
+        eom->rotate_state = ROTATE_DONE;
+
+        EOMDB("-----------------------------------------------------");
+        EOMDB("END: ec:%p", ec);
+
+        EOMDB("END: angles: prev:%d  curr:%d  next:%d  res:%d",
+              ec->e.state.rot.ang.prev, ec->e.state.rot.ang.curr,
+              ec->e.state.rot.ang.next, ec->e.state.rot.ang.reserve);
+
+        EOMDB("END: rotate angle:%d", eom->angle);
+        EOMDB("END: ec:%dx%d", ec->w, ec->h);
+
+        EOMDB("-----------------------------------------------------");
+     }
+   else if (eom->rotate_state == ROTATE_INIT)
+     {
+        eom->angle = ec->e.state.rot.ang.curr;
+        eom->rotate_state = ROTATE_PENDING;
+     }
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_init()
+{
+   Eina_Bool ret = EINA_FALSE;
+
+   EINA_SAFETY_ON_NULL_GOTO(e_comp_wl, err);
+
+   g_eom = E_NEW(E_Eom, 1);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(g_eom, EINA_FALSE);
+
+   g_eom->global = wl_global_create(e_comp_wl->wl.disp, &wl_eom_interface, 1, g_eom, _e_eom_cb_wl_bind);
+   EINA_SAFETY_ON_NULL_GOTO(g_eom->global, err);
+
+   g_eom->angle = 0;
+   g_eom->rotate_state = ROTATE_NONE;
+   g_eom->main_output_name = NULL;
+
+   ret = _e_eom_init_internal();
+   EINA_SAFETY_ON_FALSE_GOTO(ret == EINA_TRUE, err);
+
+   E_LIST_HANDLER_APPEND(g_eom->handlers, ECORE_DRM_EVENT_ACTIVATE, _e_eom_cb_ecore_drm_activate, g_eom);
+   E_LIST_HANDLER_APPEND(g_eom->handlers, ECORE_DRM_EVENT_OUTPUT, _e_eom_cb_ecore_drm_output, g_eom);
+   E_LIST_HANDLER_APPEND(g_eom->handlers, E_EVENT_CLIENT_BUFFER_CHANGE, _e_eom_cb_client_buffer_change, NULL);
+   /* TODO: add if def _F_ZONE_WINDOW_ROTATION_ */
+   E_LIST_HANDLER_APPEND(g_eom->handlers, E_EVENT_ZONE_ROTATION_EFFECT_READY, _e_eom_cb_rotation_effect_ready, g_eom);
+   E_LIST_HANDLER_APPEND(g_eom->handlers, E_EVENT_ZONE_ROTATION_EFFECT_CANCEL, _e_eom_cb_rotation_effect_cancel, g_eom);
+   E_LIST_HANDLER_APPEND(g_eom->handlers, E_EVENT_ZONE_ROTATION_EFFECT_DONE, _e_eom_cb_rotation_effect_done, g_eom);
+   E_LIST_HANDLER_APPEND(g_eom->handlers, E_EVENT_CLIENT_ROTATION_CHANGE_END, _e_eom_cb_rotation_end, g_eom);
+
+   return EINA_TRUE;
+
+err:
+
+   _e_eom_deinit();
+   return EINA_FALSE;
+}
+
+EINTERN int
+e_eom_init(void)
+{
+   Eina_Bool ret = EINA_FALSE;
+
+   ret = _e_eom_init();
+
+   if (ret == EINA_FALSE)
+     return 0;
+
+   return 1;
+}
+
+EINTERN int
+e_eom_shutdown(void)
+{
+   if (!g_eom) return 1;
+
+   _e_eom_deinit();
+
+   return 1;
+}
+
+EINTERN Eina_Bool
+e_eom_is_ec_external(E_Client *ec)
+{
+   E_EomOutputPtr eom_output;
+
+   if (!g_eom) return EINA_FALSE;
+
+   eom_output = _e_eom_output_by_ec_child_get(ec);
+   if (!eom_output)
+     return EINA_FALSE;
+   return EINA_TRUE;
+}
+
+EINTERN tdm_output*
+e_eom_tdm_output_by_ec_get(E_Client *ec)
+{
+   E_EomOutputPtr eom_output;
+
+   if (!g_eom) return NULL;
+
+   eom_output = _e_eom_output_by_ec_child_get(ec);
+   if (!eom_output)
+     return NULL;
+   return eom_output->output;
+}
diff --git a/src/bin/e_eom.h b/src/bin/e_eom.h
new file mode 100644 (file)
index 0000000..dd5513b
--- /dev/null
@@ -0,0 +1,15 @@
+#ifdef E_TYPEDEFS
+
+#else
+#ifndef E_COMP_WL_EOM_H
+#define E_COMP_WL_EOM_H
+
+#include <tdm.h>
+
+EINTERN int e_eom_init(void);
+EINTERN int e_eom_shutdown(void);
+EINTERN Eina_Bool e_eom_is_ec_external(E_Client *ec);
+EINTERN tdm_output* e_eom_tdm_output_by_ec_get(E_Client *ec);
+
+#endif
+#endif
index 52ba40e..e942803 100644 (file)
@@ -34,6 +34,7 @@
 #include "e_dialog.h"
 #include "e_screensaver.h"
 #include "e_dpms.h"
+#include "e_eom.h"
 #include "e_obj_dialog.h"
 #include "e_mouse.h"
 #include "e_msgbus.h"
 # include "e_comp_wl_tbm.h"
 #endif
 #include "e_comp_wl_rsm.h"
+#include "e_comp_wl_screenshooter.h"
+#include "e_comp_wl_video.h"
+#include "e_comp_wl_video_buffer.h"
+#include "e_comp_wl_viewport.h"
 #include "e_policy.h"
 #include "e_policy_visibility.h"
 #include "e_process.h"
index dc02cab..ea12c33 100644 (file)
@@ -608,6 +608,15 @@ main(int argc, char **argv)
    TS("E_Pointer Init Done");
    _e_main_shutdown_push(e_pointer_shutdown);
 
+   TS("Dpms Init");
+   if (!e_dpms_init())
+     {
+        e_error_message_show(_("Enlightenment cannot set up dpms.\n"));
+        _e_main_shutdown(-1);
+     }
+   TS("Dpms Init Done");
+   _e_main_shutdown_push(e_dpms_shutdown);
+
    TRACE_DS_BEGIN(MAIN:SCREEN INIT);
    TS("Screens Init");
    if (!_e_main_screens_init())
@@ -620,6 +629,18 @@ main(int argc, char **argv)
    _e_main_shutdown_push(_e_main_screens_shutdown);
    TRACE_DS_END();
 
+   if (e_config->eom_enable)
+     {
+        TS("Eom Init");
+        if (!e_eom_init())
+          {
+             e_error_message_show(_("Enlightenment cannot set up eom.\n"));
+             _e_main_shutdown(-1);
+          }
+        TS("Eom Init Done");
+        _e_main_shutdown_push(e_eom_shutdown);
+     }
+
    TS("E_Screensaver Init");
    if (!e_screensaver_init())
      {