--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}