Support wtz-foreign protocol 62/257262/3 submit/tizen/20210423.022029
authorSeunghun Lee <shiin.lee@samsung.com>
Fri, 9 Apr 2021 05:41:03 +0000 (14:41 +0900)
committerDoyoun Kang <doyoun.kang@samsung.com>
Fri, 23 Apr 2021 02:13:33 +0000 (02:13 +0000)
This is an initial commit to support wtz-foreign protocol.

This is to support out-of-process video playing surface.
wtz-foreign protocol is to enable one client to share its resources to
another client.
The client which exported a reference of foreign shell then can change
attributes - such as size, orientation, and map state - of foreign shell.
So, the client which import a foreign shell can draw its contents onto a
surface without concerning about attributes such as size like as
mentioned.

The e_foreign contains implementation of wtz_exporter and wtz_importer.
And e_foreign_shell contains implementation of wtz_exported_shell and
wtz_foreign_shell.

Change-Id: Idec6194d9b57e3346167eaa4ab6537b4d59c3393

configure.ac
packaging/enlightenment.spec
src/bin/Makefile.mk
src/bin/e_comp_wl.c
src/bin/e_foreign.c [new file with mode: 0644]
src/bin/e_foreign.h [new file with mode: 0644]
src/bin/e_foreign_private.h [new file with mode: 0644]
src/bin/e_foreign_shell.c [new file with mode: 0644]

index 7dfb99c8ed60bd68cb74b7ba7d53259b25aaeff9..ff5b469b4a0d44256306d47be9f5a6f66825efe3 100755 (executable)
@@ -370,7 +370,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-unstable-v5-server xdg-shell-unstable-v6-server tizen-remote-surface-server scaler-server screenshooter-server tizen-extension-server tizen-launch-server tizen-surface-server tizen-dpms-server eom-server presentation-time-server tizen-hwc-server linux-explicit-synchronization-unstable-v1-server],
+  PKG_CHECK_MODULES([WAYLAND], [wayland-server >= 1.8.0 xkbcommon uuid xdg-shell-unstable-v5-server xdg-shell-unstable-v6-server tizen-remote-surface-server scaler-server screenshooter-server tizen-extension-server tizen-launch-server tizen-surface-server tizen-dpms-server eom-server presentation-time-server tizen-hwc-server linux-explicit-synchronization-unstable-v1-server wtz-foreign-server],
     [
       have_wayland=yes
       AC_DEFINE_UNQUOTED([HAVE_WAYLAND],[1],[enable wayland support])
index c480531bdce0596add2e5bea4c3507bedd57ec10..0ebb15bef78633ff6be3559b51409257aff42fbf 100644 (file)
@@ -59,6 +59,7 @@ BuildRequires:  pkgconfig(presentation-time-server)
 BuildRequires:  pkgconfig(egl)
 BuildRequires:  pkgconfig(linux-explicit-synchronization-unstable-v1-server)
 BuildRequires:  pkgconfig(tizen-hwc-server)
+BuildRequires:  pkgconfig(wtz-foreign-server)
 Requires:       libwayland-extension-server
 
 # for gtest/gmock
index b8808235c8c22b9557a48dd47f33c8ecfa17d181..e00be7d3ad88b75e33d0da4cca26936b32650132 100644 (file)
@@ -144,7 +144,9 @@ src/bin/e_dbus_conn.h \
 src/bin/e_xdg_shell_v6.h \
 src/bin/e_devicemgr.h \
 src/bin/e_devicemgr_private.h \
-src/bin/e_msg.h
+src/bin/e_msg.h        \
+src/bin/e_foreign.h \
+src/bin/e_foreign_private.h
 
 enlightenment_src = \
 src/bin/e_actions.c \
@@ -280,7 +282,9 @@ src/bin/e_devicemgr_block.c \
 src/bin/e_devicemgr_input.c \
 src/bin/e_devicemgr_inputgen.c \
 src/bin/e_devicemgr_wl.c \
-src/bin/e_msg.c
+src/bin/e_msg.c \
+src/bin/e_foreign.c \
+src/bin/e_foreign_shell.c
 
 src_bin_enlightenment_CPPFLAGS = $(E_CPPFLAGS) -DEFL_BETA_API_SUPPORT -DEFL_EO_API_SUPPORT -DE_LOGGING=2 @WAYLAND_CFLAGS@ $(TTRACE_CFLAGS) $(DLOG_CFLAGS) $(PIXMAN_CFLAGS) $(POLICY_CFLAGS) $(EGL_CFLAGS)
 if HAVE_LIBGOMP
index 01745c9a730ffb818e6bb0547ad2411900542b89..a1c346f4a7b1bf319c097ecea789edbe0901f153 100644 (file)
@@ -1,4 +1,5 @@
 #include "e.h"
+#include "e_foreign.h"
 #include <tizen-extension-server-protocol.h>
 
 #include <wayland-tbm-server.h>
@@ -4244,6 +4245,9 @@ e_comp_wl_init(void)
    _e_comp_wl_move_resize_init();
    e_presentation_time_init();
 
+   if (!e_foreign_global_init(e_comp_wl->wl.disp))
+     ELOGF("COMP", "Failed to initialize the e_foreign global", NULL);
+
    /* 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);
diff --git a/src/bin/e_foreign.c b/src/bin/e_foreign.c
new file mode 100644 (file)
index 0000000..6639ada
--- /dev/null
@@ -0,0 +1,291 @@
+#include "e_foreign.h"
+#include "e_foreign_private.h"
+
+#include <uuid.h>
+
+#define WTZ_FOREIGN_VERSION         1
+#define WTZ_EXPORTED_SHELL_VERSION  1
+#define WTZ_FOREIGN_SHELL_VERSION   1
+
+typedef struct
+{
+   struct
+     {
+        struct wl_global *exporter;
+        struct wl_global *importer;
+     } global;
+
+   struct wl_listener display_destroy_listener;
+} E_Foreign;
+
+struct _E_Foreign_Handle
+{
+   uuid_t uuid;
+   char str[37];
+};
+
+static E_Foreign _e_foreign_data;
+
+EINTERN E_Foreign_Handle *
+e_foreign_handle_create(void)
+{
+   E_Foreign_Handle *handle;
+
+   handle = E_NEW(E_Foreign_Handle, 1);
+   if (!handle)
+     return NULL;
+
+   /* TODO Make sure uuid is unique. */
+   uuid_generate(handle->uuid);
+   uuid_unparse(handle->uuid, handle->str);
+
+   return handle;
+}
+
+EINTERN void
+e_foreign_handle_destroy(E_Foreign_Handle *handle)
+{
+   free(handle);
+}
+
+EINTERN const char *
+e_foreign_handle_string_get(E_Foreign_Handle *handle)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(handle, NULL);
+   return handle->str;
+}
+
+static void
+_e_exporter_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *exporter)
+{
+   wl_resource_destroy(exporter);
+}
+
+static void
+_e_exporter_cb_export_shell(struct wl_client *client, struct wl_resource *exporter, uint32_t id, struct wl_resource *surface)
+{
+   struct wl_resource *resource;
+   pid_t pid;
+   Eina_Bool res;
+
+   resource = wl_resource_create(client, &wtz_exported_shell_interface,
+                                 WTZ_EXPORTED_SHELL_VERSION, id);
+   if (!resource)
+     {
+        FERR("Cannot create a wl_resource", NULL);
+        wl_resource_post_no_memory(exporter);
+        return;
+     }
+
+   res = e_foreign_shell_export(exporter, resource, surface);
+   if (!res)
+     {
+        FERR("Cannot export foreign shell", NULL);
+        wl_resource_destroy(resource);
+        return;
+     }
+
+   wl_client_get_credentials(client, &pid, NULL, NULL);
+   FINF("Export Foreign Shell: PID@%d wl_surface@%d", NULL,
+        pid, wl_resource_get_id(surface));
+}
+
+static const struct wtz_exporter_interface _e_exporter_impl =
+{
+   _e_exporter_cb_destroy,
+   _e_exporter_cb_export_shell,
+};
+
+static void
+_e_exporter_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+   E_Foreign *foreign;
+   struct wl_resource *resource;
+
+   foreign = data;
+
+   resource = wl_resource_create(client, &wtz_exporter_interface,
+                                 WTZ_FOREIGN_VERSION, id);
+   if (!resource)
+     {
+        FERR("Cannot create wl_resource for wtz_exporter", NULL);
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   wl_resource_set_implementation(resource, &_e_exporter_impl,
+                                  foreign, NULL);
+}
+
+static Eina_Bool
+_e_exporter_global_add(E_Foreign *foreign, struct wl_display *display)
+{
+   struct wl_global *global;
+
+   global = wl_global_create(display, &wtz_exporter_interface,
+                             WTZ_FOREIGN_VERSION, foreign,
+                             _e_exporter_bind);
+   if (!global)
+     {
+        FERR("Cannot create wl_global for wtz_exporter", NULL);
+        return EINA_FALSE;
+     }
+
+   foreign->global.exporter = global;
+
+   return EINA_TRUE;
+}
+
+static void
+_e_importer_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *importer)
+{
+   wl_resource_destroy(importer);
+}
+
+static void
+_e_imported_cb_import_shell(struct wl_client *client, struct wl_resource *importer, uint32_t id, struct wl_resource *surface, const char *handle)
+{
+   struct wl_resource *resource;
+   pid_t pid;
+   Eina_Bool res;
+
+   resource = wl_resource_create(client,
+                                 &wtz_foreign_shell_interface,
+                                 WTZ_FOREIGN_SHELL_VERSION,
+                                 id);
+   if (!resource)
+     {
+        FERR("Cannot create a wl_resource", NULL);
+        wl_resource_post_no_memory(importer);
+        return;
+     }
+
+   res = e_foreign_shell_import(importer, resource, surface, handle);
+   if (!res)
+     {
+        FERR("Cannot import foreign shell", NULL);
+        wl_resource_destroy(resource);
+        return;
+     }
+
+   wl_client_get_credentials(client, &pid, NULL, NULL);
+   FINF("Import Foreign Shell: PID@%d wl_surface@%d", NULL,
+        pid, wl_resource_get_id(surface));
+}
+
+static const struct wtz_importer_interface _e_importer_impl =
+{
+   _e_importer_cb_destroy,
+   _e_imported_cb_import_shell,
+};
+
+static void
+_e_importer_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+   E_Foreign *foreign;
+   struct wl_resource *resource;
+
+   foreign = data;
+
+   resource = wl_resource_create(client, &wtz_importer_interface,
+                                 WTZ_FOREIGN_VERSION, id);
+   if (!resource)
+     {
+        FERR("Cannot create wl_resource for wtz_importer", NULL);
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   wl_resource_set_implementation(resource, &_e_importer_impl,
+                                  foreign, NULL);
+}
+
+static Eina_Bool
+_e_importer_global_add(E_Foreign *foreign, struct wl_display *display)
+{
+   struct wl_global *global;
+
+   global = wl_global_create(display, &wtz_importer_interface,
+                             WTZ_FOREIGN_VERSION, foreign,
+                             _e_importer_bind);
+   if (!global)
+     {
+        FERR("Cannot create wl_global for wtz_importer", NULL);
+        return EINA_FALSE;
+     }
+
+   foreign->global.importer = global;
+
+   return EINA_TRUE;
+}
+
+static void
+_e_foreign_cb_display_destroy(struct wl_listener *listener, void *display)
+{
+   E_Foreign *foreign;
+
+   foreign = wl_container_of(listener, foreign, display_destroy_listener);
+   foreign->display_destroy_listener.notify = NULL;
+
+   E_FREE_FUNC(foreign->global.exporter, wl_global_destroy);
+   E_FREE_FUNC(foreign->global.importer, wl_global_destroy);
+
+   e_foreign_shell_shutdown();
+}
+
+static Eina_Bool
+_e_foreign_init(E_Foreign *foreign, struct wl_display *display)
+{
+   Eina_Bool res;
+
+   if (foreign->display_destroy_listener.notify)
+     {
+        FINF("E_Foreign global already initailized", NULL);
+        return EINA_TRUE;
+     }
+
+   FINF("Initialize E_Foreign", NULL);
+
+   res = e_foreign_shell_init();
+   if (!res)
+     {
+        FERR("Failed to initialize Foreign Shell module", NULL);
+        return EINA_FALSE;
+     }
+
+   res = _e_exporter_global_add(foreign, display);
+   if (!res)
+     {
+        FERR("Failed to add a wl_global for wtz_exporter", NULL);
+        goto err_exporter;
+     }
+
+   res = _e_importer_global_add(foreign, display);
+   if (!res)
+     {
+        FERR("Failed to add a wl_global for wtz_importer", NULL);
+        goto err_importer;
+     }
+
+   wl_list_init(&foreign->display_destroy_listener.link);
+   foreign->display_destroy_listener.notify = _e_foreign_cb_display_destroy;
+   wl_display_add_destroy_listener(display,
+                                   &foreign->display_destroy_listener);
+
+   return EINA_TRUE;
+
+err_importer:
+   E_FREE_FUNC(foreign->global.exporter, wl_global_destroy);
+err_exporter:
+   e_foreign_shell_shutdown();
+
+   return EINA_FALSE;
+}
+
+EINTERN Eina_Bool
+e_foreign_global_init(struct wl_display *display)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(display, EINA_FALSE);
+
+   return _e_foreign_init(&_e_foreign_data, display);
+}
diff --git a/src/bin/e_foreign.h b/src/bin/e_foreign.h
new file mode 100644 (file)
index 0000000..2e683e0
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef E_FOREIGN_H
+# define E_FOREIGN_H
+
+#include "e.h"
+#include <wayland-server.h>
+#include <Eina.h>
+
+EINTERN Eina_Bool e_foreign_global_init(struct wl_display *display);
+
+#endif
diff --git a/src/bin/e_foreign_private.h b/src/bin/e_foreign_private.h
new file mode 100644 (file)
index 0000000..bfcf270
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef E_FOREIGN_PRIVATE_H
+# define E_FOREIGN_PRIVATE_H
+
+#include "e.h"
+#include <wayland-server.h>
+#include <wtz-foreign-server-protocol.h>
+
+#ifdef FERR
+# undef FERR
+#endif
+#define FERR(fmt, ec, arg...)   ELOGF("FOREIGN <ERR>", fmt, ec, ##arg)
+
+#ifdef FINF
+# undef FINF
+#endif
+#define FINF(fmt, ec, arg...)   ELOGF("FOREIGN <INF>", fmt, ec, ##arg)
+
+#ifdef FDBG
+# undef FDBG
+#endif
+#define FDBG(fmt, arg...)   DBG("FOREIGN| "fmt, ##arg)
+
+typedef struct _E_Foreign_Handle    E_Foreign_Handle;
+
+EINTERN E_Foreign_Handle   *e_foreign_handle_create(void);
+EINTERN void                e_foreign_handle_destroy(E_Foreign_Handle *handle);
+EINTERN const char         *e_foreign_handle_string_get(E_Foreign_Handle *handle);
+
+EINTERN Eina_Bool           e_foreign_shell_init(void);
+EINTERN void                e_foreign_shell_shutdown(void);
+EINTERN Eina_Bool           e_foreign_shell_export(struct wl_resource *exporter, struct wl_resource *resource, struct wl_resource *surface);
+EINTERN Eina_Bool           e_foreign_shell_import(struct wl_resource *importer, struct wl_resource *resource, struct wl_resource *suirface, const char *handle);
+
+#endif
diff --git a/src/bin/e_foreign_shell.c b/src/bin/e_foreign_shell.c
new file mode 100644 (file)
index 0000000..a6f3a64
--- /dev/null
@@ -0,0 +1,1090 @@
+#include "e_foreign_private.h"
+
+#define ESINF(obj, fmt, x...) \
+   FINF("EShell:%p| "fmt, wl_resource_get_user_data(obj->surface), obj, ##x)
+
+#define FSINF(obj, fmt, x...) \
+   FINF("FShell:%p| "fmt, wl_resource_get_user_data(obj->surface), obj, ##x)
+
+typedef struct _E_Exported_Shell_State E_Exported_Shell_State;
+typedef struct _E_Exported_Shell       E_Exported_Shell;
+typedef struct _E_Foreign_Shell        E_Foreign_Shell;
+
+typedef enum
+{
+   E_EXPORTED_SHELL_STATE_FLAG_MAP = (1 << 0),
+   E_EXPORTED_SHELL_STATE_FLAG_DESTINATION = (1 << 1),
+   E_EXPORTED_SHELL_STATE_FLAG_TRANSFORM = (1 << 2),
+} E_Exported_Shell_State_Flag;
+
+struct _E_Exported_Shell_State
+{
+   int32_t width, height;
+   int32_t transform;
+   Eina_Bool map;
+   E_Exported_Shell_State_Flag change;
+};
+
+struct _E_Exported_Shell
+{
+   struct wl_resource *resource;
+   struct wl_resource *surface;
+   E_Foreign_Handle *handle;
+   E_Util_Transform *transform;
+   E_Foreign_Shell *foreign;
+
+   E_Exported_Shell_State pending, cache, stage;
+
+   E_Comp_Wl_Hook *commit_to_cache_hook;
+   struct wl_listener surface_destroy_listener;
+   struct wl_listener surface_commit_listener;
+   struct wl_signal destroy_signal;
+
+   Eina_Bool has_cache_data;
+};
+
+struct _E_Foreign_Shell
+{
+   struct wl_resource *resource;
+   struct wl_resource *surface;
+   E_Util_Transform *transform;
+   E_Exported_Shell *exported;
+
+   E_Client_Hook *transform_change_hook;
+   struct wl_listener surface_destroy_listener;
+   struct wl_listener surface_commit_listener;
+   struct wl_listener shell_destroy_listener;
+
+   struct
+     {
+        int x, y, w, h;
+        int32_t transform;
+     } state;
+};
+
+Eina_Hash *_exported_shell_hash = NULL;
+
+static E_Exported_Shell *_e_exported_shell_create(struct wl_resource *resource, struct wl_resource *surface);
+static void              _e_exported_shell_destroy(E_Exported_Shell *es);
+static E_Exported_Shell *_e_exported_shell_find_by_surface(struct wl_resource *surface);
+static E_Exported_Shell *_e_exported_shell_find_by_handle(const char *handle);
+static void              _e_exported_shell_surface_link(E_Exported_Shell *es, struct wl_resource *surface);
+static void              _e_exported_shell_surface_unlink(E_Exported_Shell *es);
+static void              _e_exported_shell_foreign_link(E_Exported_Shell *es, E_Foreign_Shell *fs);
+static void              _e_exported_shell_foreign_unlink(E_Exported_Shell *es);
+static void              _e_exported_shell_visible_set(E_Exported_Shell *es, Eina_Bool visible);
+static void              _e_exported_shell_destination_set(E_Exported_Shell *es, int w, int h);
+static void              _e_exported_shell_transform_set(E_Exported_Shell *es, enum wtz_exported_shell_transform transform);
+
+static E_Foreign_Shell  *_e_foreign_shell_create(struct wl_resource *resource, struct wl_resource *surface, E_Exported_Shell *es);
+static void              _e_foreign_shell_destroy(E_Foreign_Shell *fs);
+static void              _e_foreign_shell_surface_link(E_Foreign_Shell *fs, struct wl_resource *surface);
+static void              _e_foreign_shell_surface_unlink(E_Foreign_Shell *fs);
+static void              _e_foreign_shell_exported_link(E_Foreign_Shell *fs, E_Exported_Shell *es);
+static void              _e_foreign_shell_exported_unlink(E_Foreign_Shell *fs);
+static void              _e_foreign_shell_visible_set(E_Foreign_Shell *fs, Eina_Bool visible);
+static void              _e_foreign_shell_destination_set(E_Foreign_Shell *fs, int x, int y, int w, int h);
+static void              _e_foreign_shell_transform_set(E_Foreign_Shell *fs, enum wtz_exported_shell_transform transform);
+static void              _e_foreign_shell_transform_update(E_Foreign_Shell *fs);
+static void              _e_foreign_shell_viewport_update(E_Foreign_Shell *fs);
+static void              _e_foreign_shell_rotation_update(E_Foreign_Shell *fs);
+
+EINTERN Eina_Bool
+e_foreign_shell_init(void)
+{
+   EINA_SAFETY_ON_FALSE_RETURN_VAL((_exported_shell_hash == NULL), EINA_TRUE);
+
+   _exported_shell_hash = eina_hash_string_superfast_new(NULL);
+   if (!_exported_shell_hash)
+     return EINA_FALSE;
+
+   return EINA_TRUE;
+}
+
+EINTERN void
+e_foreign_shell_shutdown(void)
+{
+   EINA_SAFETY_ON_NULL_RETURN(_exported_shell_hash);
+
+   E_FREE_FUNC(_exported_shell_hash, eina_hash_free);
+}
+
+EINTERN Eina_Bool
+e_foreign_shell_export(struct wl_resource *exporter, struct wl_resource *resource, struct wl_resource *surface)
+{
+   E_Exported_Shell *es;
+   E_Client *esc;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(exporter, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(resource, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(surface, EINA_FALSE);
+
+   es = _e_exported_shell_find_by_surface(surface);
+   if (es)
+     {
+        wl_resource_post_error(exporter,
+                               WTZ_EXPORTER_ERROR_ALREADY_EXPORTED,
+                               "given wl_surface@%d has been already exported.",
+                               wl_resource_get_id(surface));
+        return EINA_FALSE;
+     }
+
+   esc = wl_resource_get_user_data(surface);
+   if ((!esc->comp_data) ||
+       (!esc->comp_data->sub.data))
+     {
+        wl_resource_post_error(exporter,
+                               WTZ_EXPORTER_ERROR_INVALID_ROLE,
+                               "wl_surface@%d must have sub-surface role.",
+                               wl_resource_get_id(surface));
+        return EINA_FALSE;
+     }
+
+   es = _e_exported_shell_create(resource, surface);
+   if (!es)
+     {
+        wl_resource_post_no_memory(exporter);
+        return EINA_FALSE;
+     }
+
+   ESINF(es, "Created. handle(%s)", e_foreign_handle_string_get(es->handle));
+
+   return EINA_TRUE;
+}
+
+EINTERN Eina_Bool
+e_foreign_shell_import(struct wl_resource *importer, struct wl_resource *resource, struct wl_resource *surface, const char *handle)
+{
+   E_Foreign_Shell *fs;
+   E_Exported_Shell *es;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(importer, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(resource, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(handle, EINA_FALSE);
+
+   es = _e_exported_shell_find_by_handle(handle);
+   if ((es) && (es->foreign))
+     {
+        FERR("Cannot import once imported foreign shell. handle(%s)",
+             NULL, handle);
+        wl_resource_post_error(importer,
+                               WTZ_IMPORTER_ERROR_ALREADY_IMPORTED,
+                               "foreign shell associated with given handle"
+                               "(%s) has been already imported.",
+                               handle);
+        return EINA_FALSE;
+     }
+
+   fs = _e_foreign_shell_create(resource, surface, es);
+   if (!fs)
+     {
+        wl_resource_post_no_memory(importer);
+        return EINA_FALSE;
+     }
+
+   FSINF(fs, "Created. Associated Exported_Shell(%p)", es);
+
+   return EINA_TRUE;
+}
+
+static E_Exported_Shell *
+_e_exported_shell_find_by_surface(struct wl_resource *surface)
+{
+   E_Exported_Shell *es;
+   Eina_Iterator *it;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(surface, NULL);
+
+   it = eina_hash_iterator_data_new(_exported_shell_hash);
+   EINA_ITERATOR_FOREACH(it, es)
+     {
+        if (es->surface == surface)
+          return es;
+     }
+   eina_iterator_free(it);
+
+   return NULL;
+}
+
+static E_Exported_Shell *
+_e_exported_shell_find_by_handle(const char *handle)
+{
+   return eina_hash_find(_exported_shell_hash, handle);
+}
+
+static void
+_e_exported_shell_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   wl_resource_destroy(resource);
+}
+
+static void
+_e_exported_shell_cb_destination_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t width, int32_t height)
+{
+   E_Exported_Shell *es;
+
+   es = wl_resource_get_user_data(resource);
+   if (!es)
+     return;
+
+   ESINF(es, "wtz_exported_shell::destination_set(%dx%d)", width, height);
+
+   if ((width < 1) || (height < 1))
+     {
+        FERR("Invalid destination error (%dx%d)", NULL, width, height);
+        wl_resource_post_error(resource,
+                               WTZ_EXPORTED_SHELL_ERROR_BAD_VALUE,
+                               "destination size must be valid. ('%dx%d' specified)",
+                               width, height);
+        return;
+     }
+
+   if ((es->pending.width == width) &&
+       (es->pending.height == height))
+     {
+        FDBG("Ignore setting same destination");
+        return;
+     }
+
+   es->pending.width = width;
+   es->pending.height = height;
+   es->pending.change |= E_EXPORTED_SHELL_STATE_FLAG_DESTINATION;
+}
+
+static void
+_e_exported_shell_cb_transform_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t transform)
+{
+   E_Exported_Shell *es;
+
+   es = wl_resource_get_user_data(resource);
+   if (!es)
+     return;
+
+   ESINF(es, "wtz_exported_shell::transform_set(%d)", transform);
+
+   if ((transform < 0) ||
+       (transform > WTZ_EXPORTED_SHELL_TRANSFORM_FLIPPED_270))
+     {
+        FERR("Invalid transform error (%d)", NULL, transform);
+        wl_resource_post_error(resource,
+                               WTZ_EXPORTED_SHELL_ERROR_INVALID_TRANSFORM,
+                               "transform must be valid. ('%d' specified)",
+                               transform);
+        return;
+     }
+
+   if (es->pending.transform == transform)
+     {
+        FDBG("Ignore setting same transform");
+        return;
+     }
+
+   es->pending.transform = transform;
+   es->pending.change |= E_EXPORTED_SHELL_STATE_FLAG_TRANSFORM;
+}
+
+static void
+_e_exported_shell_cb_map(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   E_Exported_Shell *es;
+
+   es = wl_resource_get_user_data(resource);
+   if (!es)
+     return;
+
+   ESINF(es, "wtz_exported_shell::map");
+
+   if (es->pending.map)
+     {
+        FDBG("Already mapped.");
+        return;
+     }
+
+   es->pending.map = EINA_TRUE;
+   es->pending.change |= E_EXPORTED_SHELL_STATE_FLAG_MAP;
+}
+
+static void
+_e_exported_shell_cb_unmap(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   E_Exported_Shell *es;
+
+   es = wl_resource_get_user_data(resource);
+   if (!es)
+     return;
+
+   ESINF(es, "wtz_exported_shell::unmap");
+
+   if (!es->pending.map)
+     {
+        FDBG("Already mapped.");
+        return;
+     }
+
+   es->pending.map = EINA_FALSE;
+   es->pending.change |= E_EXPORTED_SHELL_STATE_FLAG_MAP;
+}
+
+static const struct wtz_exported_shell_interface _e_exported_shell_impl =
+{
+   _e_exported_shell_cb_destroy,
+   _e_exported_shell_cb_destination_set,
+   _e_exported_shell_cb_transform_set,
+   _e_exported_shell_cb_map,
+   _e_exported_shell_cb_unmap,
+};
+
+static void
+_e_exported_shell_cb_resource_destroy(struct wl_resource *resource)
+{
+   E_Exported_Shell *es;
+
+   es = wl_resource_get_user_data(resource);
+   if (!es)
+     return;
+
+   ESINF(es, "wtz_exported_shell::resource_destroy");
+
+   _e_exported_shell_destroy(es);
+}
+
+static E_Exported_Shell *
+_e_exported_shell_create(struct wl_resource *resource, struct wl_resource *surface)
+{
+   E_Exported_Shell *es;
+
+   es = E_NEW(E_Exported_Shell, 1);
+   if (!es)
+     return NULL;
+
+   es->handle = e_foreign_handle_create();
+   if (!es->handle)
+     {
+        free(es);
+        return NULL;
+     }
+
+   es->transform = e_util_transform_new();
+   if (!es->transform)
+     {
+        e_foreign_handle_destroy(es->handle);
+        free(es);
+        return NULL;
+     }
+
+   wl_signal_init(&es->destroy_signal);
+
+   es->stage.width = 1;
+   es->stage.height = 1;
+   es->stage.map = EINA_FALSE;
+   es->stage.transform = WTZ_EXPORTED_SHELL_TRANSFORM_NORMAL;
+
+   es->resource = resource;
+   wl_resource_set_implementation(resource,
+                                  &_e_exported_shell_impl,
+                                  es,
+                                  _e_exported_shell_cb_resource_destroy);
+   _e_exported_shell_surface_link(es, surface);
+
+   /* Add exported surface with handle string to hash */
+   eina_hash_add(_exported_shell_hash,
+                 e_foreign_handle_string_get(es->handle),
+                 es);
+
+   /* Notify the client a handle of foriegn shell */
+   wtz_exported_shell_send_handle(resource, 
+                                  e_foreign_handle_string_get(es->handle));
+
+   return es;
+}
+
+static void
+_e_exported_shell_destroy(E_Exported_Shell *es)
+{
+   ESINF(es, "Destroy");
+
+   eina_hash_del_by_key(_exported_shell_hash,
+                        e_foreign_handle_string_get(es->handle));
+
+   wl_signal_emit(&es->destroy_signal, es);
+
+   _e_exported_shell_visible_set(es, EINA_FALSE);
+   _e_exported_shell_surface_unlink(es);
+
+   e_util_transform_del(es->transform);
+   e_foreign_handle_destroy(es->handle);
+   free(es);
+}
+
+static void
+_e_exported_shell_cb_eo_move(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj, void *event EINA_UNUSED)
+{
+   E_Exported_Shell *es;
+   E_Client *esc;
+   int x, y;
+
+   es = data;
+   esc = wl_resource_get_user_data(es->surface);
+   evas_object_geometry_get(esc->frame, &x, &y, NULL, NULL);
+
+   if (es->foreign)
+     _e_foreign_shell_destination_set(es->foreign, x, y, es->stage.width, es->stage.height);
+
+   e_util_transform_viewport_set(es->transform,
+                                 x, y,
+                                 es->stage.width,
+                                 es->stage.height);
+   e_client_transform_core_update(esc);
+}
+
+static void
+_e_exported_shell_cb_surface_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
+{
+   E_Exported_Shell *es;
+
+   es = wl_container_of(listener, es, surface_destroy_listener);
+
+   ESINF(es, "wl_surface has been destroyed");
+
+   wl_resource_set_user_data(es->resource, NULL);
+
+   _e_exported_shell_destroy(es);
+}
+
+static void
+_e_exported_shell_cb_surface_commit(struct wl_listener *listener, void *data EINA_UNUSED)
+{
+   E_Exported_Shell *es;
+
+   es = wl_container_of(listener, es, surface_commit_listener);
+
+   if (es->has_cache_data)
+     {
+        es->stage = es->cache;
+        es->cache.change = 0;
+        es->has_cache_data = EINA_FALSE;
+     }
+   else
+     {
+        es->stage = es->pending;
+        es->pending.change = 0;
+     }
+
+   if (es->stage.change == 0)
+     return;
+
+   if (es->stage.change & E_EXPORTED_SHELL_STATE_FLAG_MAP)
+     _e_exported_shell_visible_set(es, es->stage.map);
+
+   if (es->stage.change & E_EXPORTED_SHELL_STATE_FLAG_DESTINATION)
+     _e_exported_shell_destination_set(es, es->stage.width, es->stage.height);
+
+   if (es->stage.change & E_EXPORTED_SHELL_STATE_FLAG_TRANSFORM)
+     _e_exported_shell_transform_set(es, es->stage.transform);
+
+   es->stage.change = 0;
+}
+
+static void
+_e_exported_shell_cb_commit_to_cache(void *data, E_Client *ec)
+{
+   E_Exported_Shell *es;
+   E_Client *esc;
+   E_Exported_Shell_State_Flag change = 0;
+
+   es = data;
+   esc = wl_resource_get_user_data(es->surface);
+   if (ec != esc)
+     return;
+
+   /* It's for flag of the cache changes to keep from being overwritten by
+    * pending changes. Accumulate changes until cache is flushed. */
+   if (es->has_cache_data)
+     change = es->cache.change;
+
+   es->cache = es->pending;
+   es->pending.change = 0;
+
+   /* Merge bit flags with previouse one. */
+   es->cache.change |= change;
+   es->has_cache_data = EINA_TRUE;
+}
+
+static void
+_e_exported_shell_surface_link(E_Exported_Shell *es, struct wl_resource *surface)
+{
+   E_Client *esc;
+
+   esc = wl_resource_get_user_data(surface);
+
+   e_client_transform_core_add(esc, es->transform);
+
+   /* It's to update transform whenever position of comp object changes by
+    * sub-surface. Otherwise, final viewport by transform wouldn't represent
+    * changed position. */
+   evas_object_event_callback_add(esc->frame, EVAS_CALLBACK_MOVE,
+                                  _e_exported_shell_cb_eo_move,
+                                  es);
+
+   es->surface_destroy_listener.notify = _e_exported_shell_cb_surface_destroy;
+   wl_resource_add_destroy_listener(surface, &es->surface_destroy_listener);
+
+   /* FIXME workaround
+    * Use apply_viewport_signal due to the absence of commit signal for now. */
+   es->surface_commit_listener.notify = _e_exported_shell_cb_surface_commit;
+   wl_signal_add(&esc->comp_data->apply_viewport_signal,
+                 &es->surface_commit_listener);
+
+   /* Listening to committing to cache */
+   es->commit_to_cache_hook =
+      e_comp_wl_hook_add(E_COMP_WL_HOOK_SUBSURFACE_COMMIT_TO_CACHE,
+                         _e_exported_shell_cb_commit_to_cache,
+                         es);
+
+   es->surface = surface;
+}
+
+static void
+_e_exported_shell_surface_unlink(E_Exported_Shell *es)
+{
+   E_Client *esc;
+
+   esc = wl_resource_get_user_data(es->surface);
+
+   e_client_transform_core_remove(esc, es->transform);
+
+   evas_object_event_callback_del(esc->frame, EVAS_CALLBACK_MOVE,
+                                  _e_exported_shell_cb_eo_move);
+
+   wl_list_remove(&es->surface_commit_listener.link);
+   wl_list_remove(&es->surface_destroy_listener.link);
+
+   E_FREE_FUNC(es->commit_to_cache_hook, e_comp_wl_hook_del);
+
+   es->surface = NULL;
+}
+
+static void
+_e_exported_shell_visible_set(E_Exported_Shell *es, Eina_Bool visible)
+{
+   E_Client *esc;
+
+   esc = wl_resource_get_user_data(es->surface);
+
+   ESINF(es, "Set visible(%d)", visible);
+
+   if (visible)
+     {
+        if (!evas_object_visible_get(esc->frame))
+          {
+             esc->comp_data->mapped = 1;
+             esc->visible = EINA_TRUE;
+             evas_object_show(esc->frame);
+          }
+     }
+   else
+     {
+        if (evas_object_visible_get(esc->frame))
+          {
+             esc->comp_data->mapped = 0;
+             esc->visible = EINA_FALSE;
+             evas_object_hide(esc->frame);
+          }
+     }
+
+   if (es->foreign)
+     _e_foreign_shell_visible_set(es->foreign, visible);
+}
+
+static void
+_e_exported_shell_destination_set(E_Exported_Shell *es, int w, int h)
+{
+   E_Client *esc;
+   E_Comp_Wl_Buffer_Viewport *vp;
+   int x, y;
+
+   esc = wl_resource_get_user_data(es->surface);
+
+   ESINF(es, "Set destination(%dx%d)", w, h);
+
+   esc->client.w = w;
+   esc->client.h = h;
+   e_client_size_set(esc, w, h);
+   evas_object_resize(esc->frame, w, h);
+
+   vp = &esc->comp_data->scaler.buffer_viewport;
+   vp->surface.width = w;
+   vp->surface.height = h;
+   vp->changed = EINA_TRUE;
+
+   evas_object_geometry_get(esc->frame, &x, &y, NULL, NULL);
+   e_util_transform_viewport_set(es->transform, x, y, w, h);
+   e_client_transform_core_update(esc);
+
+   if (es->foreign)
+     _e_foreign_shell_destination_set(es->foreign, x, y, w, h);
+}
+
+static void
+_e_exported_shell_transform_set(E_Exported_Shell *es, enum wtz_exported_shell_transform transform)
+{
+   ESINF(es, "Set transform(%d)", transform);
+
+   if (es->foreign)
+     _e_foreign_shell_transform_set(es->foreign, transform);
+}
+
+static void
+_e_exported_shell_foreign_link(E_Exported_Shell *es, E_Foreign_Shell *fs)
+{
+   E_Client *esc, *fsc;
+
+   fsc = wl_resource_get_user_data(fs->surface);
+   esc = wl_resource_get_user_data(es->surface);
+   e_client_embedded_client_set(esc, fsc);
+
+   es->foreign = fs;
+}
+
+static void
+_e_exported_shell_foreign_unlink(E_Exported_Shell *es)
+{
+   E_Client *esc;
+
+   /* FIXME
+    * The E_Client associated with exported shell must get hidden before
+    * unsetting embedded client. Otherwise referencing count of E_Client
+    * wouldn't reach to zero permanently by animation triggered by
+    * e_comp_object. We need more general solution for it. */
+   _e_exported_shell_visible_set(es, EINA_FALSE);
+
+   esc = wl_resource_get_user_data(es->surface);
+   e_client_embedded_client_unset(esc);
+
+   es->foreign = NULL;
+}
+
+static void
+_e_foreign_shell_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   wl_resource_destroy(resource);
+}
+
+static const struct wtz_foreign_shell_interface _e_foreign_shell_impl =
+{
+   _e_foreign_shell_cb_destroy,
+};
+
+static void
+_e_foreign_shell_cb_resource_destroy(struct wl_resource *resource)
+{
+   E_Foreign_Shell *fs;
+
+   fs = wl_resource_get_user_data(resource);
+   if (!fs)
+     return;
+
+   FSINF(fs, "wtz_foreign_shell::resource_destroy");
+
+   _e_foreign_shell_destroy(fs);
+}
+
+static E_Foreign_Shell *
+_e_foreign_shell_create(struct wl_resource *resource, struct wl_resource *surface, E_Exported_Shell *es)
+{
+   E_Foreign_Shell *fs;
+
+   fs = E_NEW(E_Foreign_Shell, 1);
+   if (!fs)
+     return NULL;
+
+   fs->transform = e_util_transform_new();
+   if (!fs->transform)
+     {
+        free(fs);
+        return NULL;
+     }
+
+   _e_foreign_shell_surface_link(fs, surface);
+
+   fs->exported = es;
+   if (!es)
+     {
+        FSINF(fs, "The Exported_Shell may have been destroyed "
+             "or given handle may possibly be invalid");
+        wtz_foreign_shell_send_destroyed(resource);
+     }
+   else
+     {
+        wtz_foreign_shell_send_destination_changed(resource,
+                                                   es->stage.width,
+                                                   es->stage.height);
+        wtz_foreign_shell_send_transform_changed(resource,
+                                                 es->stage.transform);
+
+        _e_foreign_shell_exported_link(fs, es);
+     }
+
+   fs->resource = resource;
+   wl_resource_set_implementation(resource,
+                                  &_e_foreign_shell_impl,
+                                  fs,
+                                  _e_foreign_shell_cb_resource_destroy);
+
+   return fs;
+}
+
+static void
+_e_foreign_shell_destroy(E_Foreign_Shell *fs)
+{
+   FSINF(fs, "Destroy");
+
+   if (fs->exported)
+     _e_foreign_shell_exported_unlink(fs);
+
+   _e_foreign_shell_surface_unlink(fs);
+
+   e_util_transform_del(fs->transform);
+   free(fs);
+}
+
+static void
+_e_foreign_shell_cb_surface_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
+{
+   E_Foreign_Shell *fs;
+
+   fs = wl_container_of(listener, fs, surface_destroy_listener);
+
+   FSINF(fs, "wl_surface has been destroyed");
+
+   wl_resource_set_user_data(fs->resource, NULL);
+
+   _e_foreign_shell_destroy(fs);
+}
+
+static void
+_e_foreign_shell_cb_surface_commit(struct wl_listener *listener, void *data EINA_UNUSED)
+{
+   E_Foreign_Shell *fs;
+   E_Client *fsc;
+
+   fs = wl_container_of(listener, fs, surface_commit_listener);
+   fsc = wl_resource_get_user_data(fs->surface);
+
+   /* e_comp_wl has been setting e_client to unignore only for xdg_shell
+    * or internal client. */
+   if (fsc->ignored)
+     {
+        EC_CHANGED(fsc);
+        fsc->new_client = 1;
+        e_comp->new_clients++;
+        e_client_unignore(fsc);
+     }
+
+   /* e_comp_wl has been setting pixmap of e_client to usable only for
+    * xdg_shell */
+   e_pixmap_usable_set(fsc->pixmap, !!e_pixmap_resource_get(fsc->pixmap));
+
+   /* It's supposed to be called in e_comp_wl_surface_attach, but call it
+    * again just in case. */
+   e_pixmap_dirty(fsc->pixmap);
+
+   /* It's to update pixmap size. It's supposed to be called by
+    * e_comp_wl_surface_attach but it failed due to e_pixmap_usable_get. */
+   e_pixmap_refresh(fsc->pixmap);
+
+   e_comp_wl_map_size_cal_from_buffer(fsc);
+
+   if ((fsc->new_client) && (fs->exported))
+     _e_foreign_shell_visible_set(fs, fs->exported->stage.map);
+}
+
+/* TODO */
+#if 0
+static void
+_e_foreign_shell_cb_hook_transform_change(void *data, E_Client *ec)
+{
+   E_Foreign_Shell *fs;
+   E_Client *esc, *fsc;
+
+   fs = data;
+   if ((!fs->exported) ||
+       (!fs->exported->surface))
+     return;
+
+   esc = wl_resource_get_user_data(fs->exported->surface);
+   if (ec != esc)
+     return;
+
+   e_util_transform_copy(fs->transform, &esc->transform_core.result.transform);
+
+   fsc = wl_resource_get_user_data(fs->surface);
+   e_client_transform_core_update(fsc);
+}
+#endif
+
+static void 
+_e_foreign_shell_surface_link(E_Foreign_Shell *fs, struct wl_resource *surface)
+{
+   E_Client *fsc;
+
+   fsc = wl_resource_get_user_data(surface);
+
+   e_client_transform_core_add(fsc, fs->transform);      
+
+   /* TODO */
+#if 0
+   fs->transform_change_hook =
+      e_client_hook_add(E_CLIENT_HOOK_TRANSFORM_CHANGE,
+                        _e_foreign_shell_cb_hook_transform_change,
+                        fs);
+#endif
+
+   fs->surface_destroy_listener.notify = _e_foreign_shell_cb_surface_destroy;
+   wl_resource_add_destroy_listener(surface, &fs->surface_destroy_listener);
+
+   /* FIXME workaround 
+    * Use apply_viewport_signal due to the absence of commit signal for now. */
+   fs->surface_commit_listener.notify = _e_foreign_shell_cb_surface_commit;
+   wl_signal_add(&fsc->comp_data->apply_viewport_signal,
+                 &fs->surface_commit_listener);
+
+   /* To allow changing geometry */
+   fsc->netwm.type = E_WINDOW_TYPE_UTILITY;
+   fsc->lock_focus_in = fsc->lock_focus_out = EINA_TRUE;
+   fsc->netwm.state.skip_taskbar = EINA_TRUE;
+   fsc->netwm.state.skip_pager = EINA_TRUE;
+   fsc->no_shape_cut = EINA_TRUE;
+   fsc->border_size = 0;
+   fsc->lock_user_location = 0;
+   fsc->lock_client_location = 0;
+   fsc->lock_user_size = 0;
+   fsc->lock_client_size = 0;
+   fsc->lock_client_stacking = 0;
+   fsc->lock_user_shade = 0;
+   fsc->lock_client_shade = 0;
+   fsc->lock_user_maximize = 0;
+   fsc->lock_client_maximize = 0;
+   fsc->changes.need_maximize = 0;
+   fsc->maximized = E_MAXIMIZE_NONE;
+
+   fs->surface = surface;
+}
+
+static void
+_e_foreign_shell_surface_unlink(E_Foreign_Shell *fs)
+{
+   E_Client *fsc;
+
+   fsc = wl_resource_get_user_data(fs->surface);
+
+   e_client_transform_core_remove(fsc, fs->transform);
+
+   wl_list_remove(&fs->surface_destroy_listener.link);
+   wl_list_remove(&fs->surface_commit_listener.link);
+
+#if 0
+   E_FREE_FUNC(fs->transform_change_hook, e_client_hook_del);
+#endif
+
+   fs->surface = NULL;
+}
+
+static void
+_e_foreign_shell_cb_exported_shell_destroy(struct wl_listener *listener, void *data)
+{
+   E_Foreign_Shell *fs;
+
+   fs = wl_container_of(listener, fs, shell_destroy_listener);
+
+   wtz_foreign_shell_send_destroyed(fs->resource);
+
+   _e_foreign_shell_exported_unlink(fs);
+}
+
+static void
+_e_foreign_shell_exported_link(E_Foreign_Shell *fs, E_Exported_Shell *es)
+{
+   fs->shell_destroy_listener.notify =
+      _e_foreign_shell_cb_exported_shell_destroy;
+   wl_signal_add(&es->destroy_signal,
+                 &fs->shell_destroy_listener);
+
+   _e_exported_shell_foreign_link(es, fs);
+
+   fs->exported = es;
+}
+
+static void
+_e_foreign_shell_exported_unlink(E_Foreign_Shell *fs)
+{
+   wl_list_remove(&fs->shell_destroy_listener.link);
+
+   _e_exported_shell_foreign_unlink(fs->exported);
+
+   fs->exported = NULL;
+}
+
+static void
+_e_foreign_shell_visible_set(E_Foreign_Shell *fs, Eina_Bool visible)
+{
+   E_Client *fsc;
+
+   fsc = wl_resource_get_user_data(fs->surface);
+
+   FSINF(fs, "Set visible(%d)", visible);
+
+   if ((visible) &&
+       (e_pixmap_usable_get(fsc->pixmap)))
+     {
+        if (!fsc->comp_data->mapped)
+          {
+             fsc->comp_data->mapped = 1;
+             fsc->visible = EINA_TRUE;
+             evas_object_show(fsc->frame);
+          }
+     }
+   else if (fsc->comp_data->mapped)
+     {
+        fsc->comp_data->mapped = 0;
+        fsc->visible = EINA_FALSE;
+        evas_object_hide(fsc->frame);
+     }
+}
+
+static void
+_e_foreign_shell_viewport_update(E_Foreign_Shell *fs)
+{
+   E_Client *fsc;
+   E_Comp_Wl_Buffer_Viewport *vp;
+   int x, y, w, h;
+
+   fsc = wl_resource_get_user_data(fs->surface);
+
+   x = fs->state.x;
+   y = fs->state.y;
+   w = fs->state.w;
+   h = fs->state.h;
+
+   fsc->client.w = w;
+   fsc->client.h = h;
+   e_client_size_set(fsc, w, h);
+   evas_object_resize(fsc->frame, w, h);
+
+   vp = &fsc->comp_data->scaler.buffer_viewport;
+   vp->surface.width = w;
+   vp->surface.height = h;
+   vp->changed = EINA_TRUE;
+   fsc->comp_data->pending.buffer_viewport = *vp;
+
+   e_util_transform_viewport_set(fs->transform, x, y, w, h);
+}
+
+static void
+_e_foreign_shell_rotation_update(E_Foreign_Shell *fs)
+{
+   int x, y, w, h;
+   uint32_t transform;
+   double sx, sy;
+   int cx, cy, dx, dy;
+   int rx, ry, rw, rh;
+
+   x = fs->state.x;
+   y = fs->state.y;
+   w = fs->state.w;
+   h = fs->state.h;
+   transform = fs->state.transform;
+
+   cx = (x + w/2);
+   cy = (y + h/2);
+
+   if ((transform == WTZ_EXPORTED_SHELL_TRANSFORM_90) ||
+       (transform == WTZ_EXPORTED_SHELL_TRANSFORM_270))
+     {
+        sx = (double)h / w;
+        sy = (double)w / h;
+
+        rw = h;
+        rh = w;
+        rx = cx - rw / 2;
+        ry = cy - rh / 2;
+
+        dx = (w - sx * rw) * 0.5f;
+        dy = (h - sy * rh) * 0.5f;
+
+        e_util_transform_rotation_axis_set(fs->transform, cx, cy, -1);
+        e_util_transform_move(fs->transform,
+                              -(rx * sx) + dx + x,
+                              -(ry * sy) + dy + y,
+                              0.0);
+        e_util_transform_scale(fs->transform, sx, sy, 1.0);
+     }
+   else if ((transform == WTZ_EXPORTED_SHELL_TRANSFORM_NORMAL) ||
+            (transform == WTZ_EXPORTED_SHELL_TRANSFORM_180))
+     {
+        e_util_transform_rotation_axis_set(fs->transform, cx, cy, -1);
+        e_util_transform_scale(fs->transform, 1.0, 1.0, 1.0);
+        e_util_transform_move(fs->transform, 0, 0, 0);
+     }
+   else
+     {
+        FSINF(fs, "Not supported. transform(%d)", transform);
+        return;
+     }
+
+   switch (transform)
+     {
+      case WTZ_EXPORTED_SHELL_TRANSFORM_90:
+         e_util_transform_rotation(fs->transform, 0, 0, 90);
+         break;
+      case WTZ_EXPORTED_SHELL_TRANSFORM_270:
+         e_util_transform_rotation(fs->transform, 0, 0, 270);
+         break;
+      case WTZ_EXPORTED_SHELL_TRANSFORM_180:
+         e_util_transform_rotation(fs->transform, 0, 0, 180);
+         break;
+      case WTZ_EXPORTED_SHELL_TRANSFORM_NORMAL:
+         e_util_transform_rotation(fs->transform, 0, 0, 0);
+      default:
+         break;
+     }
+}
+
+static void
+_e_foreign_shell_transform_update(E_Foreign_Shell *fs)
+{
+   E_Client *fsc;
+
+   _e_foreign_shell_viewport_update(fs);
+   _e_foreign_shell_rotation_update(fs);
+
+   fsc = wl_resource_get_user_data(fs->surface);
+   e_client_transform_core_update(fsc);
+}
+
+static void
+_e_foreign_shell_destination_set(E_Foreign_Shell *fs, int x, int y, int w, int h)
+{
+   FSINF(fs, "Set destination(%d,%d %dx%d)", x, y, w, h);
+
+   fs->state.x = x;
+   fs->state.y = y;
+   fs->state.w = w;
+   fs->state.h = h;
+
+   _e_foreign_shell_transform_update(fs);
+
+   wtz_foreign_shell_send_destination_changed(fs->resource, w, h);
+}
+
+static void
+_e_foreign_shell_transform_set(E_Foreign_Shell *fs, enum wtz_exported_shell_transform transform)
+{
+   FSINF(fs, "Set transform(%d)", transform);
+
+   fs->state.transform = transform;
+
+   _e_foreign_shell_transform_update(fs);
+
+   wtz_foreign_shell_send_transform_changed(fs->resource, transform);
+}