summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Blumenkrantz <zmike@osg.samsung.com>2017-06-29 14:54:43 -0400
committerMike Blumenkrantz <zmike@osg.samsung.com>2017-06-29 19:51:44 -0400
commit5986e01ef3c800ff55eb7c124e96a1829a31b18a (patch)
tree257f426ced6f206f296abfc4c5a86514e1128634
parentfbb676584ed5ab12ce0dd101b24a1ebf06bf5043 (diff)
downloadefl-devs/discomfitor/wip2.tar.gz
efl_wl: a multiseat wayland compositor in an evas smart objectdevs/discomfitor/wip2
build when wayland support is enabled and provide two test/demo cases beta api, docs tbd @feature Reviewed-By: Cedric BAIL <cedric@osg.samsung.com>
-rw-r--r--configure.ac38
-rw-r--r--src/Makefile.am1
-rw-r--r--src/Makefile_efl_wl.am50
-rw-r--r--src/bin/efl_wl/efl_wl_test.c65
-rw-r--r--src/bin/efl_wl/efl_wl_test_stack.c87
-rw-r--r--src/lib/efl_wl/Efl_Wl.h57
-rw-r--r--src/lib/efl_wl/copiedfromweston.x358
-rw-r--r--src/lib/efl_wl/dmabuf.c513
-rw-r--r--src/lib/efl_wl/dmabuf.h92
-rw-r--r--src/lib/efl_wl/efl_wl.c5235
-rw-r--r--src/lib/efl_wl/x11.x563
11 files changed, 7059 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 26d5b14144..14b0a58a11 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3672,6 +3672,44 @@ EFL_EVAL_PKGS([ECORE_WL2])
EFL_LIB_END_OPTIONAL([Ecore_Wl2])
#### End of Ecore_Wl2
+#### Efl_Wl
+EFL_LIB_START_OPTIONAL([Efl_Wl], [test -n "${ECORE_WL2_CFLAGS}"])
+
+### Additional options to configure
+
+### Default values
+
+### Checks for programs
+
+### Checks for libraries
+EFL_INTERNAL_DEPEND_PKG([EFL_WL], [ecore-wl2])
+EFL_INTERNAL_DEPEND_PKG([EFL_WL], [ecore-input])
+EFL_INTERNAL_DEPEND_PKG([EFL_WL], [ecore])
+EFL_INTERNAL_DEPEND_PKG([EFL_WL], [ecore-evas])
+EFL_INTERNAL_DEPEND_PKG([EFL_WL], [evas])
+EFL_INTERNAL_DEPEND_PKG([EFL_WL], [emile])
+EFL_INTERNAL_DEPEND_PKG([EFL_WL], [eo])
+EFL_INTERNAL_DEPEND_PKG([EFL_WL], [efl])
+EFL_INTERNAL_DEPEND_PKG([EFL_WL], [eina])
+EFL_OPTIONAL_INTERNAL_DEPEND_PKG([EFL_WL], [${want_x11_any}], [ecore_x])
+
+EFL_EVAL_PKGS([EFL_WL])
+
+### Checks for header files
+
+### Checks for types
+
+### Checks for structures
+
+### Checks for compiler characteristics
+
+### Checks for linker characteristics
+
+### Checks for library functions
+
+EFL_LIB_END_OPTIONAL([Efl_Wl])
+#### End of Efl_Wl
+
#### Eldbus
EFL_LIB_START([Eldbus])
diff --git a/src/Makefile.am b/src/Makefile.am
index 48b00273b7..cb89e3cacd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -73,6 +73,7 @@ include Makefile_Ethumb.am
include Makefile_Ethumb_Client.am
include Makefile_Elocation.am
include Makefile_Elementary.am
+include Makefile_efl_wl.am
include Makefile_Cxx.am
include Makefile_Eolian_Cxx.am
diff --git a/src/Makefile_efl_wl.am b/src/Makefile_efl_wl.am
new file mode 100644
index 0000000000..ea05d2a4d6
--- /dev/null
+++ b/src/Makefile_efl_wl.am
@@ -0,0 +1,50 @@
+if HAVE_ECORE_WL2
+
+lib_LTLIBRARIES += lib/efl_wl/libefl_wl.la
+
+installed_eflwlmainheadersdir = $(includedir)/efl-wl-@VMAJ@
+dist_installed_eflwlmainheaders_DATA = lib/efl_wl/Efl_Wl.h
+
+lib_efl_wl_libefl_wl_la_SOURCES = \
+lib/efl_wl/efl_wl.c \
+lib/efl_wl/dmabuf.c \
+lib/efl_wl/dmabuf.h
+
+
+nodist_lib_efl_wl_libefl_wl_la_SOURCES = \
+lib/efl_wl/linux-dmabuf-unstable-v1-protocol.c \
+lib/efl_wl/linux-dmabuf-unstable-v1-server-protocol.h \
+lib/efl_wl/xdg-shell-unstable-v6-protocol.c \
+lib/efl_wl/xdg-shell-unstable-v6-server-protocol.h
+
+lib_efl_wl_libefl_wl_la_CPPFLAGS = -I$(top_builddir)/lib/efl @EFL_WL_CFLAGS@
+lib_efl_wl_libefl_wl_la_LIBADD = @EFL_WL_LIBS@
+lib_efl_wl_libefl_wl_la_DEPENDENCIES = @EFL_WL_INTERNAL_LIBS@ \
+lib/efl_wl/copiedfromweston.x \
+lib/efl_wl/x11.x
+
+lib_efl_wl_libefl_wl_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@
+
+BUILT_SOURCES += $(nodist_lib_efl_wl_libefl_wl_la_SOURCES)
+
+bin_PROGRAMS += \
+bin/efl_wl/efl_wl_test \
+bin/efl_wl/efl_wl_test_stack
+
+bin_efl_wl_efl_wl_test_SOURCES = bin/efl_wl/efl_wl_test.c
+bin_efl_wl_efl_wl_test_stack_SOURCES = bin/efl_wl/efl_wl_test_stack.c
+
+bin_efl_wl_efl_wl_test_LDADD = @USE_EFL_WL_LIBS@ @USE_ELEMENTARY_LIBS@
+bin_efl_wl_efl_wl_test_DEPENDENCIES = @USE_EFL_WL_INTERNAL_LIBS@ @USE_ELEMENTARY_INTERNAL_LIBS@
+bin_efl_wl_efl_wl_test_CPPFLAGS = \
+@EFL_WL_CFLAGS@ \
+@ELEMENTARY_CFLAGS@
+
+bin_efl_wl_efl_wl_test_stack_LDADD = @USE_EFL_WL_LIBS@ @USE_ELEMENTARY_LIBS@
+bin_efl_wl_efl_wl_test_stack_DEPENDENCIES = @USE_EFL_WL_INTERNAL_LIBS@ @USE_ELEMENTARY_INTERNAL_LIBS@
+bin_efl_wl_efl_wl_test_stack_CPPFLAGS = \
+@EFL_WL_CFLAGS@ \
+@ELEMENTARY_CFLAGS@
+
+
+endif
diff --git a/src/bin/efl_wl/efl_wl_test.c b/src/bin/efl_wl/efl_wl_test.c
new file mode 100644
index 0000000000..29361ee920
--- /dev/null
+++ b/src/bin/efl_wl/efl_wl_test.c
@@ -0,0 +1,65 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "Efl_Wl.h"
+#include "Elementary.h"
+
+static Evas_Object *win;
+static Eina_Strbuf *buf;
+static Ecore_Exe *exe;
+
+static Eina_Bool
+del_handler(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Exe_Event_Del *ev)
+{
+ if (ev->exe == exe) ecore_main_loop_quit();
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void
+focus_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ evas_object_focus_set(data, 1);
+}
+
+static Eina_Bool
+dostuff(void *data)
+{
+ exe = efl_wl_run(data, eina_strbuf_string_get(buf));
+ ecore_event_handler_add(ECORE_EXE_EVENT_DEL, (Ecore_Event_Handler_Cb)del_handler, NULL);
+ evas_object_focus_set(data, 1);
+ return EINA_FALSE;
+}
+
+int
+main(int argc, char *argv[])
+{
+ Evas_Object *o;
+ int i;
+
+ if (argc < 2) return 0;
+ elm_init(argc, (char**)argv);
+ buf = eina_strbuf_new();
+ for (i = 1; i < argc; i++)
+ {
+ eina_strbuf_append(buf, argv[i]);
+ if (i + 1 < argc) eina_strbuf_append_char(buf, ' ');
+ }
+
+ win = elm_win_util_standard_add("comp", "comp");
+ elm_win_autodel_set(win, 1);
+ elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
+
+ o = efl_wl_add(evas_object_evas_get(win));
+ evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_win_resize_object_add(win, o);
+ evas_object_show(o);
+ evas_object_show(win);
+ evas_object_resize(win, 640, 480);
+ evas_object_event_callback_add(win, EVAS_CALLBACK_FOCUS_IN, focus_in, o);
+ ecore_timer_add(1, dostuff, o);
+
+ elm_run();
+ elm_shutdown();
+ return 0;
+}
diff --git a/src/bin/efl_wl/efl_wl_test_stack.c b/src/bin/efl_wl/efl_wl_test_stack.c
new file mode 100644
index 0000000000..51cd598984
--- /dev/null
+++ b/src/bin/efl_wl/efl_wl_test_stack.c
@@ -0,0 +1,87 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "Efl_Wl.h"
+#include "Elementary.h"
+
+static Evas_Object *win;
+
+static const char *cmds[] =
+{
+ "weston-terminal",
+ "terminology",
+};
+
+static unsigned int num_cmds = EINA_C_ARRAY_LENGTH(cmds);
+static unsigned int n;
+
+static Eina_Bool
+dostuff(void *data)
+{
+ efl_wl_run(data, cmds[n++]);
+ evas_object_focus_set(data, 1);
+ return n != num_cmds;
+}
+
+static void
+prev_clicked(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ efl_wl_prev(data);
+}
+
+static void
+next_clicked(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ efl_wl_next(data);
+}
+
+int
+main(int argc, char *argv[])
+{
+ Evas_Object *o, *comp, *prev, *next;
+ elm_init(argc, (char**)argv);
+
+ win = elm_win_util_standard_add("comp", "comp");
+ elm_win_autodel_set(win, 1);
+ elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
+ o = evas_object_rectangle_add(evas_object_evas_get(win));
+ evas_object_color_set(o, 0, 125, 0, 125);
+ evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_win_resize_object_add(win, o);
+ evas_object_show(o);
+
+ o = elm_table_add(win);
+ elm_win_resize_object_add(win, o);
+ evas_object_show(o);
+
+ prev = elm_button_add(win);
+ elm_object_text_set(prev, "prev");
+ evas_object_size_hint_align_set(prev, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_size_hint_weight_set(prev, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_show(prev);
+ elm_table_pack(o, prev, 0, 0, 1, 1);
+
+ next = elm_button_add(win);
+ elm_object_text_set(next, "next");
+ evas_object_size_hint_align_set(next, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_size_hint_weight_set(next, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_show(next);
+ elm_table_pack(o, next, 1, 0, 1, 1);
+
+ comp = efl_wl_add(evas_object_evas_get(win));
+ evas_object_size_hint_min_set(comp, 640, 480);
+ elm_table_pack(o, comp, 0, 1, 2, 1);
+ evas_object_size_hint_align_set(comp, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_size_hint_weight_set(comp, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_show(comp);
+ evas_object_show(win);
+ ecore_timer_add(1, dostuff, comp);
+
+ evas_object_smart_callback_add(prev, "clicked", prev_clicked, comp);
+ evas_object_smart_callback_add(next, "clicked", next_clicked, comp);
+
+ elm_run();
+ elm_shutdown();
+ return 0;
+}
diff --git a/src/lib/efl_wl/Efl_Wl.h b/src/lib/efl_wl/Efl_Wl.h
new file mode 100644
index 0000000000..16332b5cbd
--- /dev/null
+++ b/src/lib/efl_wl/Efl_Wl.h
@@ -0,0 +1,57 @@
+#ifdef EFL_BETA_API_SUPPORT
+
+#ifndef EFL_WL_H
+# define EFL_WL_H
+#include <Evas.h>
+#include <Ecore.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_EINA_BUILD
+# ifdef DLL_EXPORT
+# define EAPI __declspec(dllexport)
+# else
+# define EAPI
+# endif /* ! DLL_EXPORT */
+# else
+# define EAPI __declspec(dllimport)
+# endif /* ! EFL_EINA_BUILD */
+# define EAPI_WEAK
+#else
+# ifdef __GNUC__
+# if __GNUC__ >= 4
+# define EAPI __attribute__ ((visibility("default")))
+# define EAPI_WEAK __attribute__ ((weak))
+# else
+# define EAPI
+# define EAPI_WEAK
+# endif
+# else
+/**
+ * @def EAPI
+ * @brief Used to export functions(by changing visibility).
+ */
+# define EAPI
+# endif
+#endif
+
+typedef enum
+{
+ EFL_WL_ROTATION_0,
+ EFL_WL_ROTATION_90,
+ EFL_WL_ROTATION_180,
+ EFL_WL_ROTATION_270
+} Efl_Wl_Rotation;
+
+EAPI Evas_Object *efl_wl_add(Evas *e);
+EAPI Ecore_Exe *efl_wl_run(Evas_Object *obj, const char *cmd);
+EAPI Eina_Bool efl_wl_next(Evas_Object *obj);
+EAPI Eina_Bool efl_wl_prev(Evas_Object *obj);
+EAPI void efl_wl_rotate(Evas_Object *obj, Efl_Wl_Rotation rot, Eina_Bool rtl);
+EAPI void efl_wl_scale_set(Evas_Object *obj, double scale);
+#endif
+
+#endif
diff --git a/src/lib/efl_wl/copiedfromweston.x b/src/lib/efl_wl/copiedfromweston.x
new file mode 100644
index 0000000000..0b1388b064
--- /dev/null
+++ b/src/lib/efl_wl/copiedfromweston.x
@@ -0,0 +1,358 @@
+/*
+ * Copyright © 2011 Kristian Høgsberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
+
+static uint32_t
+data_offer_choose_action(Comp_Data_Device_Offer *offer)
+{
+ uint32_t available_actions, preferred_action = 0;
+ uint32_t source_actions, offer_actions;
+
+ if (wl_resource_get_version(offer->res) >=
+ WL_DATA_OFFER_ACTION_SINCE_VERSION)
+ {
+ offer_actions = offer->dnd_actions;
+ preferred_action = offer->preferred_dnd_action;
+ }
+ else
+ {
+ offer_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+ }
+
+ if (wl_resource_get_version(offer->source->res) >=
+ WL_DATA_SOURCE_ACTION_SINCE_VERSION)
+ source_actions = offer->source->dnd_actions;
+ else
+ source_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+
+ available_actions = offer_actions & source_actions;
+
+ if (!available_actions)
+ return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+
+ if (offer->source->seat &&
+ offer->source->compositor_action & available_actions)
+ return offer->source->compositor_action;
+
+ /* If the dest side has a preferred DnD action, use it */
+ if ((preferred_action & available_actions) != 0)
+ return preferred_action;
+
+ /* Use the first found action, in bit order */
+ return 1 << (ffs(available_actions) - 1);
+}
+
+static void
+data_offer_update_action(Comp_Data_Device_Offer *offer)
+{
+ uint32_t action;
+
+ if (!offer->source)
+ return;
+
+ action = data_offer_choose_action(offer);
+
+ if (offer->source->current_dnd_action == action)
+ return;
+
+ offer->source->current_dnd_action = action;
+
+ if (offer->in_ask)
+ return;
+
+ if (wl_resource_get_version(offer->source->res) >=
+ WL_DATA_SOURCE_ACTION_SINCE_VERSION)
+ wl_data_source_send_action(offer->source->res, action);
+
+ if (wl_resource_get_version(offer->res) >=
+ WL_DATA_OFFER_ACTION_SINCE_VERSION)
+ wl_data_offer_send_action(offer->res, action);
+}
+
+static void
+data_device_offer_set_actions(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t dnd_actions, uint32_t preferred_action)
+{
+ Comp_Data_Device_Offer *offer = wl_resource_get_user_data(resource);
+
+ if (dnd_actions & ~ALL_ACTIONS)
+ {
+ wl_resource_post_error(offer->res,
+ WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK,
+ "invalid action mask %x", dnd_actions);
+ return;
+ }
+
+ if (preferred_action &&
+ (!(preferred_action & dnd_actions) ||
+ __builtin_popcount(preferred_action) > 1))
+ {
+ wl_resource_post_error(offer->res,
+ WL_DATA_OFFER_ERROR_INVALID_ACTION,
+ "invalid action %x", preferred_action);
+ return;
+ }
+
+ offer->dnd_actions = dnd_actions;
+ offer->preferred_dnd_action = preferred_action;
+ data_offer_update_action(offer);
+}
+
+#ifdef HAVE_ECORE_X
+static Ecore_X_Atom
+action_convert(uint32_t action)
+{
+ if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
+ return ECORE_X_ATOM_XDND_ACTION_MOVE;
+ if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
+ return ECORE_X_ATOM_XDND_ACTION_ASK;
+ return ECORE_X_ATOM_XDND_ACTION_COPY;
+}
+#endif
+static void
+data_device_offer_accept(struct wl_client *client, struct wl_resource *resource,
+ uint32_t serial, const char *mime_type)
+{
+ Comp_Data_Device_Offer *offer = wl_resource_get_user_data(resource);
+ Comp_Surface *cs;
+
+ /* Protect against untimely calls from older data offers */
+ if (!offer->source || offer != offer->source->offer)
+ return;
+
+ switch (offer->type)
+ {
+ case COMP_DATA_DEVICE_OFFER_TYPE_DND:
+ cs = offer->source->seat->drag.enter;
+ if (!offer->source->seat->drag.res) return;
+ if ((!offer->source->seat->drag.source) &&
+ (wl_resource_get_client(cs->res) != wl_resource_get_client(offer->source->seat->drag.res)))
+ return;
+#ifdef HAVE_ECORE_X
+ if (offer->source->x11_owner)
+ {
+ Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(offer->source->seat->c->evas));
+ offer->source->accepted = mime_type != NULL;
+ ecore_x_client_message32_send(offer->source->x11_owner,
+ ECORE_X_ATOM_XDND_STATUS, ECORE_X_EVENT_MASK_NONE,
+ win, 2 | !!mime_type, 0, 0,
+ (!!mime_type) * action_convert(offer->source->current_dnd_action));
+ return;
+ }
+#endif
+ break;
+
+ case COMP_DATA_DEVICE_OFFER_TYPE_CLIPBOARD:
+ break;
+ default: return;
+ }
+ if (offer->source->seat->client_offer)
+ ecore_wl2_offer_accept(offer->source->seat->client_offer, mime_type);
+ else
+ wl_data_source_send_target(offer->source->res, mime_type);
+ offer->source->accepted = mime_type != NULL;
+}
+
+static void
+data_device_offer_receive(struct wl_client *client, struct wl_resource *resource,
+ const char *mime_type, int32_t fd)
+{
+ Comp_Data_Device_Offer *offer = wl_resource_get_user_data(resource);
+
+ if (offer->source && offer == offer->source->offer)
+ {
+ if (offer->proxy)
+ {
+ Ecore_Wl2_Offer *off;
+#ifdef HAVE_ECORE_X
+ if (offer->source->x11_owner)
+ {
+ x11_send_send(offer->source, mime_type, fd, offer->type);
+ return;
+ }
+#endif
+ if (offer->type == COMP_DATA_DEVICE_OFFER_TYPE_CLIPBOARD)
+ off = ecore_wl2_dnd_selection_get(offer->source->seat->seat);
+ else
+ {
+ off = offer->source->seat->client_offer;
+ offer->source->seat->client_offer = NULL;
+ }
+ ecore_wl2_offer_proxy_receive(off, mime_type, fd);
+ offer->proxy_offer = off;
+ }
+ else
+ wl_data_source_send_send(offer->source->res, mime_type, fd);
+ }
+ close(fd);
+}
+
+static void
+data_source_notify_finish(Comp_Data_Device_Source *source)
+{
+ if (!source->actions_set)
+ return;
+
+ if (source->proxy && (!source->x11_owner))
+ ecore_wl2_offer_finish(source->offer->proxy_offer);
+
+ if (source->offer && source->offer->in_ask &&
+ wl_resource_get_version(source->res) >=
+ WL_DATA_SOURCE_ACTION_SINCE_VERSION)
+ {
+ wl_data_source_send_action(source->res,
+ source->current_dnd_action);
+ }
+
+ if (wl_resource_get_version(source->res) >=
+ WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION)
+ {
+ wl_data_source_send_dnd_finished(source->res);
+ }
+
+ source->offer = NULL;
+}
+
+static void
+data_device_offer_finish(struct wl_client *client, struct wl_resource *resource)
+{
+ Comp_Data_Device_Offer *offer = wl_resource_get_user_data(resource);
+
+ if (!offer->source || offer->source->offer != offer)
+ return;
+
+ /* Disallow finish while we have a grab driving drag-and-drop, or
+ * if the negotiation is not at the right stage
+ */
+ if (((!offer->proxy) && offer->source->seat) ||
+ !offer->source->accepted)
+ {
+ wl_resource_post_error(offer->res,
+ WL_DATA_OFFER_ERROR_INVALID_FINISH,
+ "premature finish request");
+ return;
+ }
+
+ switch (offer->source->current_dnd_action)
+ {
+ case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE:
+ case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK:
+ wl_resource_post_error(offer->res,
+ WL_DATA_OFFER_ERROR_INVALID_OFFER,
+ "offer finished with an invalid action");
+ return;
+
+ default:
+ break;
+ }
+
+ data_source_notify_finish(offer->source);
+}
+
+static void
+data_device_offer_impl_destroy(struct wl_resource *resource)
+{
+ Comp_Data_Device_Offer *offer = wl_resource_get_user_data(resource);
+
+ if (!offer->source)
+ goto out;
+
+ if (offer->source->offer != offer)
+ goto out;
+
+ if (offer->type == COMP_DATA_DEVICE_OFFER_TYPE_DND)
+ {
+ /* If the drag destination has version < 3, wl_data_offer.finish
+ * won't be called, so do this here as a safety net, because
+ * we still want the version >=3 drag source to be happy.
+ */
+ if (wl_resource_get_version(offer->res) <
+ WL_DATA_OFFER_ACTION_SINCE_VERSION)
+ {
+ data_source_notify_finish(offer->source);
+ }
+ else if (offer->source->res &&
+ wl_resource_get_version(offer->source->res) >=
+ WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION)
+ {
+ wl_data_source_send_cancelled(offer->source->res);
+ }
+ }
+
+ offer->source->offer = NULL;
+ if (offer->proxy_offer && offer->proxy)
+ ecore_wl2_offer_proxy_receive_end(offer->proxy_offer);
+out:
+ free(offer);
+}
+
+static void
+drag_grab_button(Comp_Seat *s,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+ Comp_Data_Device_Source *data_source = s->drag.source;
+ enum wl_pointer_button_state state = state_w;
+
+ if (data_source &&
+ s->drag.id == button &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED)
+ {
+ if ((s->drag.enter || (s->drag.x11_owner == ecore_evas_window_get(ecore_evas_ecore_evas_get(s->c->evas)))) &&
+ data_source->accepted &&
+ data_source->current_dnd_action)
+ {
+ if (s->drag.enter)
+ wl_data_device_send_drop(data_device_find(s, s->drag.enter->res));
+
+ if (wl_resource_get_version(data_source->res) >=
+ WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION)
+ wl_data_source_send_dnd_drop_performed(data_source->res);
+
+ if (data_source->offer)
+ data_source->offer->in_ask =
+ data_source->current_dnd_action ==
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
+
+ if (!data_source->proxy)
+ data_source->seat = NULL;
+ }
+ else if (wl_resource_get_version(data_source->res) >=
+ WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION)
+ {
+ wl_data_source_send_cancelled(data_source->res);
+ }
+ seat_drag_end(s);
+ if (!data_source->x11_owner)
+ s->drag.source = NULL;
+#ifdef HAVE_ECORE_X
+ if (ecore_x_display_get())
+ ecore_x_pointer_ungrab();
+#endif
+ }
+}
diff --git a/src/lib/efl_wl/dmabuf.c b/src/lib/efl_wl/dmabuf.c
new file mode 100644
index 0000000000..a451179d53
--- /dev/null
+++ b/src/lib/efl_wl/dmabuf.c
@@ -0,0 +1,513 @@
+/* Shamelessly stolen from weston and modified, original license boiler plate
+ * follows.
+ */
+
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wunused-parameter"
+#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4
+# pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+
+/*
+ * Copyright © 2014, 2015 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/mman.h>
+
+#include <assert.h>
+
+#include "linux-dmabuf-unstable-v1-server-protocol.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "dmabuf.h"
+#include <Eina.h>
+
+__attribute__ ((visibility("hidden"))) Eina_Bool comp_dmabuf_test(struct linux_dmabuf_buffer *dmabuf);
+
+static void
+linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer *buffer)
+{
+ int i;
+
+ for (i = 0; i < buffer->attributes.n_planes; i++) {
+ close(buffer->attributes.fd[i]);
+ buffer->attributes.fd[i] = -1;
+ }
+
+ buffer->attributes.n_planes = 0;
+
+ free(buffer);
+}
+
+static void
+destroy_params(struct wl_resource *params_resource)
+{
+ struct linux_dmabuf_buffer *buffer;
+
+ buffer = wl_resource_get_user_data(params_resource);
+
+ if (!buffer)
+ return;
+
+ linux_dmabuf_buffer_destroy(buffer);
+}
+
+static void
+params_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+params_add(struct wl_client *client,
+ struct wl_resource *params_resource,
+ int32_t name_fd,
+ uint32_t plane_idx,
+ uint32_t offset,
+ uint32_t stride,
+ uint32_t modifier_hi,
+ uint32_t modifier_lo)
+{
+ struct linux_dmabuf_buffer *buffer;
+
+ buffer = wl_resource_get_user_data(params_resource);
+ if (!buffer) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
+ "params was already used to create a wl_buffer");
+ close(name_fd);
+ return;
+ }
+
+ assert(buffer->params_resource == params_resource);
+ assert(!buffer->buffer_resource);
+
+ if (plane_idx >= MAX_DMABUF_PLANES) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
+ "plane index %u is too high", plane_idx);
+ close(name_fd);
+ return;
+ }
+
+ if (buffer->attributes.fd[plane_idx] != -1) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
+ "a dmabuf has already been added for plane %u",
+ plane_idx);
+ close(name_fd);
+ return;
+ }
+
+ buffer->attributes.fd[plane_idx] = name_fd;
+ buffer->attributes.offset[plane_idx] = offset;
+ buffer->attributes.stride[plane_idx] = stride;
+ buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) |
+ modifier_lo;
+ buffer->attributes.n_planes++;
+}
+
+static void
+linux_dmabuf_wl_buffer_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_buffer_interface linux_dmabuf_buffer_implementation = {
+ linux_dmabuf_wl_buffer_destroy
+};
+
+static void
+destroy_linux_dmabuf_wl_buffer(struct wl_resource *resource)
+{
+ struct linux_dmabuf_buffer *buffer;
+
+ buffer = wl_resource_get_user_data(resource);
+ assert(buffer->buffer_resource == resource);
+ assert(!buffer->params_resource);
+
+ if (buffer->user_data_destroy_func)
+ buffer->user_data_destroy_func(buffer);
+
+ linux_dmabuf_buffer_destroy(buffer);
+}
+
+static void
+params_create(struct wl_client *client,
+ struct wl_resource *params_resource,
+ int32_t width,
+ int32_t height,
+ uint32_t format,
+ uint32_t flags)
+{
+ struct linux_dmabuf_buffer *buffer;
+ int i;
+
+ buffer = wl_resource_get_user_data(params_resource);
+
+ if (!buffer) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
+ "params was already used to create a wl_buffer");
+ return;
+ }
+
+ assert(buffer->params_resource == params_resource);
+ assert(!buffer->buffer_resource);
+
+ /* Switch the linux_dmabuf_buffer object from params resource to
+ * eventually wl_buffer resource.
+ */
+ wl_resource_set_user_data(buffer->params_resource, NULL);
+ buffer->params_resource = NULL;
+
+ if (!buffer->attributes.n_planes) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "no dmabuf has been added to the params");
+ goto err_out;
+ }
+
+ /* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */
+ for (i = 0; i < buffer->attributes.n_planes; i++) {
+ if (buffer->attributes.fd[i] == -1) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "no dmabuf has been added for plane %i", i);
+ goto err_out;
+ }
+ }
+
+ buffer->attributes.version = 1;
+ buffer->attributes.width = width;
+ buffer->attributes.height = height;
+ buffer->attributes.format = format;
+ buffer->attributes.flags = flags;
+
+ if (width < 1 || height < 1) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
+ "invalid width %d or height %d", width, height);
+ goto err_out;
+ }
+
+ for (i = 0; i < buffer->attributes.n_planes; i++) {
+ off_t size;
+
+ if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "size overflow for plane %i", i);
+ goto err_out;
+ }
+
+ if (i == 0 &&
+ (uint64_t) buffer->attributes.offset[i] +
+ (uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "size overflow for plane %i", i);
+ goto err_out;
+ }
+
+ /* Don't report an error as it might be caused
+ * by the kernel not supporting seeking on dmabuf */
+ size = lseek(buffer->attributes.fd[i], 0, SEEK_END);
+ if (size == -1)
+ continue;
+
+ if (buffer->attributes.offset[i] >= size) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "invalid offset %i for plane %i",
+ buffer->attributes.offset[i], i);
+ goto err_out;
+ }
+
+ if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "invalid stride %i for plane %i",
+ buffer->attributes.stride[i], i);
+ goto err_out;
+ }
+
+ /* Only valid for first plane as other planes might be
+ * sub-sampled according to fourcc format */
+ if (i == 0 &&
+ buffer->attributes.offset[i] + buffer->attributes.stride[i] * height > size) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "invalid buffer stride or height for plane %i", i);
+ goto err_out;
+ }
+ }
+
+ /* XXX: Some additional sanity checks could be done with respect
+ * to the fourcc format. A centralized collection (kernel or
+ * libdrm) would be useful to avoid code duplication for these
+ * checks (e.g. drm_format_num_planes).
+ */
+
+ if (!comp_dmabuf_test(buffer))
+ goto err_failed;
+
+ buffer->buffer_resource = wl_resource_create(client,
+ &wl_buffer_interface,
+ 1, 0);
+ if (!buffer->buffer_resource) {
+ wl_resource_post_no_memory(params_resource);
+ goto err_buffer;
+ }
+
+ wl_resource_set_implementation(buffer->buffer_resource,
+ &linux_dmabuf_buffer_implementation,
+ buffer, destroy_linux_dmabuf_wl_buffer);
+
+ zwp_linux_buffer_params_v1_send_created(params_resource,
+ buffer->buffer_resource);
+
+ return;
+
+err_buffer:
+ if (buffer->user_data_destroy_func)
+ buffer->user_data_destroy_func(buffer);
+
+err_failed:
+ zwp_linux_buffer_params_v1_send_failed(params_resource);
+
+err_out:
+ linux_dmabuf_buffer_destroy(buffer);
+}
+
+static const struct zwp_linux_buffer_params_v1_interface
+zwp_linux_buffer_params_implementation = {
+ params_destroy,
+ params_add,
+ params_create
+};
+
+static void
+linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+linux_dmabuf_create_params(struct wl_client *client,
+ struct wl_resource *linux_dmabuf_resource,
+ uint32_t params_id)
+{
+ void *compositor;
+ struct linux_dmabuf_buffer *buffer;
+ uint32_t version;
+ int i;
+
+ version = wl_resource_get_version(linux_dmabuf_resource);
+ compositor = wl_resource_get_user_data(linux_dmabuf_resource);
+
+ buffer = calloc(1, sizeof *buffer);
+ if (!buffer)
+ goto err_out;
+
+ for (i = 0; i < MAX_DMABUF_PLANES; i++)
+ buffer->attributes.fd[i] = -1;
+
+ buffer->compositor = compositor;
+ buffer->params_resource =
+ wl_resource_create(client,
+ &zwp_linux_buffer_params_v1_interface,
+ version, params_id);
+ if (!buffer->params_resource)
+ goto err_dealloc;
+
+ wl_resource_set_implementation(buffer->params_resource,
+ &zwp_linux_buffer_params_implementation,
+ buffer, destroy_params);
+
+ return;
+
+err_dealloc:
+ free(buffer);
+
+err_out:
+ wl_resource_post_no_memory(linux_dmabuf_resource);
+}
+
+/** Get the linux_dmabuf_buffer from a wl_buffer resource
+ *
+ * If the given wl_buffer resource was created through the linux_dmabuf
+ * protocol interface, returns the linux_dmabuf_buffer object. This can
+ * be used as a type check for a wl_buffer.
+ *
+ * \param resource A wl_buffer resource.
+ * \return The linux_dmabuf_buffer if it exists, or NULL otherwise.
+ */
+struct linux_dmabuf_buffer *
+linux_dmabuf_buffer_get(struct wl_resource *resource)
+{
+ struct linux_dmabuf_buffer *buffer;
+
+ if (!resource)
+ return NULL;
+
+ if (!wl_resource_instance_of(resource, &wl_buffer_interface,
+ &linux_dmabuf_buffer_implementation))
+ return NULL;
+
+ buffer = wl_resource_get_user_data(resource);
+ assert(buffer);
+ assert(!buffer->params_resource);
+ assert(buffer->buffer_resource == resource);
+
+ return buffer;
+}
+
+/** Set renderer-private data
+ *
+ * Set the user data for the linux_dmabuf_buffer. It is invalid to overwrite
+ * a non-NULL user data with a new non-NULL pointer. This is meant to
+ * protect against renderers fighting over linux_dmabuf_buffer user data
+ * ownership.
+ *
+ * The renderer-private data is usually set from the
+ * weston_renderer::import_dmabuf hook.
+ *
+ * \param buffer The linux_dmabuf_buffer object to set for.
+ * \param data The new renderer-private data pointer.
+ * \param func Destructor function to be called for the renderer-private
+ * data when the linux_dmabuf_buffer gets destroyed.
+ *
+ * \sa weston_compositor_import_dmabuf
+ */
+void
+linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
+ void *data,
+ dmabuf_user_data_destroy_func func)
+{
+ assert(data == NULL || buffer->user_data == NULL);
+
+ buffer->user_data = data;
+ buffer->user_data_destroy_func = func;
+}
+
+/** Get renderer-private data
+ *
+ * Get the user data from the linux_dmabuf_buffer.
+ *
+ * \param buffer The linux_dmabuf_buffer to query.
+ * \return Renderer-private data pointer.
+ *
+ * \sa linux_dmabuf_buffer_set_user_data
+ */
+void *
+linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer)
+{
+ return buffer->user_data;
+}
+
+static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = {
+ linux_dmabuf_destroy,
+ linux_dmabuf_create_params
+};
+
+static void
+bind_linux_dmabuf(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ void *compositor = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface,
+ version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &linux_dmabuf_implementation,
+ compositor, NULL);
+
+ /* EGL_EXT_image_dma_buf_import does not provide a way to query the
+ * supported pixel formats. */
+ /* XXX: send formats */
+}
+
+/** Advertise linux_dmabuf support
+ *
+ * Calling this initializes the zwp_linux_dmabuf protocol support, so that
+ * the interface will be advertised to clients. Essentially it creates a
+ * global. Do not call this function multiple times in the compositor's
+ * lifetime. There is no way to deinit explicitly, globals will be reaped
+ * when the wl_display gets destroyed.
+ *
+ * \param compositor The compositor to init for.
+ * \return Zero on success, -1 on failure.
+ */
+int
+linux_dmabuf_setup(struct wl_display *display, void *comp)
+{
+ if (!wl_global_create(display,
+ &zwp_linux_dmabuf_v1_interface, 1,
+ comp, bind_linux_dmabuf))
+ return -1;
+
+ return 0;
+}
+
+/** Resolve an internal compositor error by disconnecting the client.
+ *
+ * This function is used in cases when the dmabuf-based wl_buffer
+ * turns out unusable and there is no fallback path. This is used by
+ * renderers which are the fallback path in the first place.
+ *
+ * It is possible the fault is caused by a compositor bug, the underlying
+ * graphics stack bug or normal behaviour, or perhaps a client mistake.
+ * In any case, the options are to either composite garbage or nothing,
+ * or disconnect the client. This is a helper function for the latter.
+ *
+ * The error is sent as a INVALID_OBJECT error on the client's wl_display.
+ *
+ * \param buffer The linux_dmabuf_buffer that is unusable.
+ * \param msg A custom error message attached to the protocol error.
+ */
+void
+linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
+ const char *msg)
+{
+ struct wl_client *client;
+ struct wl_resource *display_resource;
+ uint32_t id;
+
+ assert(buffer->buffer_resource);
+ id = wl_resource_get_id(buffer->buffer_resource);
+ client = wl_resource_get_client(buffer->buffer_resource);
+ display_resource = wl_client_get_object(client, 1);
+
+ assert(display_resource);
+ wl_resource_post_error(display_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "linux_dmabuf server error with "
+ "wl_buffer@%u: %s", id, msg);
+}
diff --git a/src/lib/efl_wl/dmabuf.h b/src/lib/efl_wl/dmabuf.h
new file mode 100644
index 0000000000..c1da1e3abf
--- /dev/null
+++ b/src/lib/efl_wl/dmabuf.h
@@ -0,0 +1,92 @@
+/* Shamelessly stolen from weston and modified, original license boiler plate
+ * follows.
+ */
+/*
+ * Copyright © 2014, 2015 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef WESTON_LINUX_DMABUF_H
+#define WESTON_LINUX_DMABUF_H
+
+#include <stdint.h>
+
+#define MAX_DMABUF_PLANES 4
+
+struct linux_dmabuf_buffer;
+typedef void (*dmabuf_user_data_destroy_func)(
+ struct linux_dmabuf_buffer *buffer);
+
+struct dmabuf_attributes {
+ int version;
+ int32_t width;
+ int32_t height;
+ uint32_t format;
+ uint32_t flags; /* enum zlinux_buffer_params_flags */
+ int n_planes;
+ int fd[MAX_DMABUF_PLANES];
+ uint32_t offset[MAX_DMABUF_PLANES];
+ uint32_t stride[MAX_DMABUF_PLANES];
+ uint64_t modifier[MAX_DMABUF_PLANES];
+};
+
+struct linux_dmabuf_buffer {
+ struct wl_resource *buffer_resource;
+ struct wl_resource *params_resource;
+ void *compositor;
+ struct dmabuf_attributes attributes;
+
+ void *user_data;
+ dmabuf_user_data_destroy_func user_data_destroy_func;
+
+ /* XXX:
+ *
+ * Add backend private data. This would be for the backend
+ * to do all additional imports it might ever use in advance.
+ * The basic principle, even if not implemented in drivers today,
+ * is that dmabufs are first attached, but the actual allocation
+ * is deferred to first use. This would allow the exporter and all
+ * attachers to agree on how to allocate.
+ *
+ * The DRM backend would use this to create drmFBs for each
+ * dmabuf_buffer, just in case at some point it would become
+ * feasible to scan it out directly. This would improve the
+ * possibilities to successfully scan out, avoiding compositing.
+ */
+};
+
+int
+linux_dmabuf_setup(struct wl_display *display, void *comp);
+
+struct linux_dmabuf_buffer *
+linux_dmabuf_buffer_get(struct wl_resource *resource);
+
+void
+linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
+ void *data,
+ dmabuf_user_data_destroy_func func);
+void *
+linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer);
+
+void
+linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
+ const char *msg);
+
+#endif /* WESTON_LINUX_DMABUF_H */
diff --git a/src/lib/efl_wl/efl_wl.c b/src/lib/efl_wl/efl_wl.c
new file mode 100644
index 0000000000..1d28b0e0f5
--- /dev/null
+++ b/src/lib/efl_wl/efl_wl.c
@@ -0,0 +1,5235 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wunused-parameter"
+#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4
+# pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+
+#include <wayland-server.h>
+#include "xdg-shell-unstable-v6-server-protocol.h"
+#include "dmabuf.h"
+
+#include "Ecore_Evas.h"
+#include "Ecore_Wl2.h"
+#include "Ecore_Input.h"
+#include "Evas_GL.h"
+# ifdef HAVE_ECORE_X
+#include "Ecore_X.h"
+#endif
+
+#include "Efl_Wl.h"
+
+#undef COORDS_INSIDE
+#define COORDS_INSIDE(x, y, xx, yy, ww, hh) \
+ (((x) < ((xx) + (ww))) && ((y) < ((yy) + (hh))) && ((x) >= (xx)) && ((y) >= (yy)))
+
+#ifdef __linux__
+# include <linux/input.h>
+#else
+# define BTN_LEFT 0x110
+# define BTN_RIGHT 0x111
+# define BTN_MIDDLE 0x112
+# define BTN_SIDE 0x113
+# define BTN_EXTRA 0x114
+# define BTN_FORWARD 0x115
+# define BTN_BACK 0x116
+#endif
+
+#undef container_of
+# define container_of(ptr, type, member) \
+ ({ \
+ const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)(void *)( (char *)__mptr - offsetof(type,member) ); \
+ })
+
+#ifndef EGL_HEIGHT
+# define EGL_HEIGHT 0x3056
+#endif
+#ifndef EGL_WIDTH
+# define EGL_WIDTH 0x3057
+#endif
+
+#ifndef EGL_TEXTURE_FORMAT
+# define EGL_TEXTURE_FORMAT 0x3080
+#endif
+#ifndef EGL_TEXTURE_RGBA
+# define EGL_TEXTURE_RGBA 0x305E
+#endif
+#ifndef DRM_FORMAT_ARGB8888
+# define DRM_FORMAT_ARGB8888 0x34325241
+#endif
+#ifndef DRM_FORMAT_XRGB8888
+# define DRM_FORMAT_XRGB8888 0x34325258
+#endif
+
+#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
+
+
+typedef struct Input_Sequence
+{
+ EINA_INLIST;
+ uint32_t id;
+ uint32_t down_serial;
+ uint32_t down_time;
+ uint32_t up_serial;
+ uint32_t up_time;
+ Eina_Bool pass : 1;
+} Input_Sequence;
+
+typedef struct Comp_Subsurface Comp_Subsurface;
+typedef struct Comp_Surface Comp_Surface;
+
+typedef struct Comp_Buffer
+{
+ struct wl_resource *res;
+ Comp_Surface *cs;
+ Eina_List *renders;
+ Eina_List *post_renders;
+ int x, y, w, h;
+ struct wl_listener destroy_listener;
+ struct wl_shm_buffer *shm_buffer;
+ struct linux_dmabuf_buffer *dmabuf_buffer;
+ Eina_Bool dbg : 1;
+} Comp_Buffer;
+
+typedef struct Comp
+{
+ Efl_Wl_Rotation rotation;
+ double scale;
+ char *env;
+ Ecore_Wl2_Display *disp;
+ Ecore_Wl2_Display *parent_disp;
+ Ecore_Wl2_Display *client_disp;
+ struct wl_display *display;
+
+ double wayland_time_base;
+ Evas_Object *obj;
+ Evas_Object *clip;
+ Evas_Object *events;
+
+ Eina_Inlist *surfaces;
+ unsigned int surfaces_count;
+ Eina_Hash *client_surfaces;
+ Comp_Surface *active_surface;
+
+ Eina_Inlist *shells;
+ Eina_List *render_queue;
+ Eina_List *post_render_queue;
+ Evas *evas;
+ Evas_GL *gl;
+ Evas_GL_Config *glcfg;
+ Evas_GL_Context *glctx;
+ Evas_GL_Surface *glsfc;
+ Evas_GL_API *glapi;
+ Eina_List *output_resources;
+ Eina_Inlist *seats;
+ Eina_Bool rendering : 1;
+ Eina_Bool data_device_proxy : 1;
+ Eina_Bool x11_selection : 1;
+ Eina_Bool rtl : 1;
+} Comp;
+
+typedef struct Comp_Data_Device_Source Comp_Data_Device_Source;
+
+typedef struct Comp_Seat
+{
+ EINA_INLIST;
+ Comp *c;
+ Eina_Stringshare *name;
+ struct wl_global *global;
+
+ Ecore_Wl2_Input *seat;
+ Ecore_Wl2_Input *client_seat;
+ Ecore_Wl2_Offer *client_offer;
+ uint32_t client_selection_serial;
+ Eo *dev;
+ Eina_List *resources;
+
+ Eina_Hash *data_devices;
+ struct
+ {
+ struct wl_resource *res;
+ Comp_Data_Device_Source *source;
+ Comp_Surface *surface;
+ Comp_Surface *enter;
+ uint32_t id;
+ Ecore_Evas *proxy_win;
+ Ecore_Window x11_owner;
+ Eina_List *x11_types;
+ Eina_Bool tch : 1;
+ } drag;
+ Comp_Data_Device_Source *selection_source;
+ uint32_t selection_serial;
+ Ecore_Window x11_selection_owner;
+
+ struct wl_client *active_client;
+ Comp_Surface *grab;
+
+ struct
+ {
+ struct wl_array keys;
+ struct
+ {
+ xkb_mod_mask_t depressed;
+ xkb_mod_mask_t latched;
+ xkb_mod_mask_t locked;
+ xkb_layout_index_t group;
+ Eina_Bool changed : 1;
+ } mods;
+ struct xkb_context *context;
+ struct xkb_keymap *keymap;
+ struct xkb_state *state;
+ char *keymap_mem;
+ int keymap_mem_size;
+ int keymap_fd;
+ int repeat_rate;
+ int repeat_delay;
+ Eina_Hash *resources;
+ Comp_Surface *enter;
+ } kbd;
+
+ struct
+ {
+ Eina_Hash *resources;
+ uint32_t button_mask;
+ uint32_t enter_serial;
+ Eina_Inlist *events;
+ Comp_Surface *enter;
+ struct
+ {
+ Comp_Surface *surface;
+ int x, y;
+ } cursor;
+ struct
+ {
+ Evas_Object *obj;
+ int layer;
+ int x, y;
+ } efl;
+ Evas_Point pos;
+ Eina_Bool in : 1;
+ } ptr;
+
+ struct
+ {
+ Eina_Hash *resources;
+ Eina_Inlist *events;
+ Comp_Surface *enter;
+ Evas_Point pos;
+ } tch;
+
+ Eina_Bool pointer : 1;
+ Eina_Bool keyboard : 1;
+ Eina_Bool touch : 1;
+ Eina_Bool focused : 1;
+ Eina_Bool selection_changed : 1;
+ Eina_Bool selection_exists : 1;
+} Comp_Seat;
+
+typedef struct Comp_Buffer_State
+{
+ Comp_Buffer *buffer;
+ Eina_Tiler *opaque;
+ Eina_Tiler *damages;
+ Eina_Tiler *input;
+ Eina_List *frames;
+ Eina_Bool attach : 1;
+ Eina_Bool set_opaque : 1;
+ Eina_Bool set_input : 1;
+} Comp_Buffer_State;
+
+typedef struct Shell_Data
+{
+ EINA_INLIST;
+ Comp *c;
+ struct wl_resource *res;
+ Eina_List *surfaces;
+ Eina_Inlist *positioners;
+ Eina_Bool ping : 1;
+} Shell_Data;
+
+
+typedef struct Shell_Positioner
+{
+ EINA_INLIST;
+ Shell_Data *sd;
+ struct wl_resource *res;
+ Evas_Coord_Size size;
+ Eina_Rectangle anchor_rect;
+ enum zxdg_positioner_v6_anchor anchor;
+ enum zxdg_positioner_v6_gravity gravity;
+ enum zxdg_positioner_v6_constraint_adjustment constrain;
+ Evas_Coord_Point offset;
+} Shell_Positioner;
+
+struct Comp_Surface
+{
+ EINA_INLIST;
+ Comp *c;
+ Evas_Object *obj;
+ Evas_Object *clip;
+ Evas_Object *img;
+ Eina_Array *input_rects;
+ Eina_Array *opaque_rects;
+ Eina_List *proxies;
+ struct wl_resource *res;
+ struct wl_resource *role;
+ Comp_Seat *drag; //drag surface
+ Comp_Buffer *buffer[2]; // new, prev
+ /* subsurface stacking order */
+ Eina_List *subsurfaces;
+ Eina_List *pending_subsurfaces;
+ /* any child surface (xdg or subsurface */
+ Eina_Inlist *children;
+ Comp_Surface *parent;
+
+ Eina_Tiler *opaque;
+ Eina_Tiler *input;
+ Eina_List *frames;
+ Comp_Subsurface *subsurface;
+ Comp_Buffer_State pending;
+ struct
+ {
+ struct wl_resource *surface;
+ Eina_Rectangle geom;
+ Shell_Data *data;
+ Eina_Stringshare *title;
+ Eina_Stringshare *app_id;
+ Shell_Positioner *positioner;
+ Eina_List *grabs;
+ Eina_Bool popup : 1;
+ Eina_Bool new : 1;
+ Eina_Bool activated : 1;
+ } shell;
+ Eina_Bool mapped : 1;
+ Eina_Bool cursor : 1;
+ Eina_Bool render_queue : 1;
+ Eina_Bool post_render_queue : 1;
+ Eina_Bool dead : 1;
+ Eina_Bool commit : 1;
+};
+
+struct Comp_Subsurface
+{
+ Comp_Surface *surface;
+ Comp_Buffer_State cache;
+ Evas_Point offset;
+ Evas_Point pending_offset;
+ Eina_Bool set_offset : 1;
+ Eina_Bool sync : 1;
+ Eina_Bool cached : 1;
+};
+
+typedef enum Comp_Data_Device_Offer_Type
+{
+ COMP_DATA_DEVICE_OFFER_TYPE_DND,
+ COMP_DATA_DEVICE_OFFER_TYPE_CLIPBOARD,
+} Comp_Data_Device_Offer_Type;
+
+typedef struct Comp_Data_Device_Offer
+{
+ Comp_Data_Device_Offer_Type type;
+ struct wl_resource *res;
+ Comp_Data_Device_Source *source;
+ Ecore_Wl2_Offer *proxy_offer;
+ uint32_t dnd_actions;
+ uint32_t preferred_dnd_action;
+ Eina_Bool in_ask : 1;
+ Eina_Bool proxy : 1;
+} Comp_Data_Device_Offer;
+
+typedef struct Comp_Data_Device_Source
+{
+ struct wl_resource *res;
+ Comp_Seat *seat;
+ Comp_Data_Device_Offer *offer;
+ Ecore_Window x11_owner;
+ Eina_Inlist *transfers;
+ Eina_Binbuf *reader_data;
+ Ecore_Fd_Handler *reader;
+ Eina_List *mime_types;
+ uint32_t dnd_actions;
+ uint32_t current_dnd_action;
+ uint32_t compositor_action;
+ Ecore_Event_Handler *proxy_send_handler;
+ uint32_t proxy_serial;
+ Eina_Bool actions_set : 1;
+ Eina_Bool accepted : 1;
+ Eina_Bool proxy : 1;
+} Comp_Data_Device_Source;
+
+typedef struct Comp_Data_Device_Transfer
+{
+ EINA_INLIST;
+ Comp_Data_Device_Offer_Type type;
+ Ecore_Fd_Handler *fdh;
+ size_t offset;
+ Eina_Stringshare *mime_type;
+ Comp_Data_Device_Source *source;
+} Comp_Data_Device_Transfer;
+
+static Eina_List *comps;
+static Eina_List *handlers;
+
+static inline Eina_Tiler *
+tiler_new(void)
+{
+ Eina_Tiler *t;
+
+ t = eina_tiler_new(65535, 65535);
+ eina_tiler_tile_size_set(t, 1, 1);
+ return t;
+}
+
+static inline void
+fdh_del(Ecore_Fd_Handler *fdh)
+{
+ if (!fdh) return;
+ close(ecore_main_fd_handler_fd_get(fdh));
+ ecore_main_fd_handler_del(fdh);
+}
+
+#define PTR_SWAP(A, B) ptr_swap((void**)A, (void**)B)
+
+static inline void
+ptr_swap(void **a, void **b)
+{
+ void *c;
+
+ c = *a;
+ *a = *b;
+ *b = c;
+}
+
+static inline void
+array_clear(Eina_Array **arr)
+{
+ Eina_Array *a = *arr;
+
+ if (!a) return;
+ while (eina_array_count(a))
+ evas_object_del(eina_array_pop(a));
+ eina_array_free(a);
+ *arr = NULL;
+}
+
+static inline void
+comp_data_device_source_reader_clear(Comp_Data_Device_Source *ds)
+{
+ fdh_del(ds->reader);
+ ds->reader = NULL;
+ eina_binbuf_free(ds->reader_data);
+ ds->reader_data = NULL;
+}
+
+static inline void
+comp_surface_reparent(Comp_Surface *cs, Comp_Surface *pcs)
+{
+ if (cs->parent == pcs) return;
+ evas_object_smart_member_del(cs->obj);
+ if (cs->parent)
+ cs->parent->children = eina_inlist_remove(cs->parent->children, EINA_INLIST_GET(cs));
+ if (pcs)
+ {
+ cs->c->surfaces = eina_inlist_remove(cs->c->surfaces, EINA_INLIST_GET(cs));
+ cs->c->surfaces_count--;
+ evas_object_smart_member_add(cs->obj, pcs->obj);
+ pcs->children = eina_inlist_append(pcs->children, EINA_INLIST_GET(cs));
+ }
+ else
+ {
+ evas_object_smart_member_add(cs->obj, cs->c->obj);
+ cs->c->surfaces = eina_inlist_append(cs->c->surfaces, EINA_INLIST_GET(cs));
+ cs->c->surfaces_count++;
+ }
+ cs->parent = pcs;
+}
+
+static inline struct wl_resource *
+data_device_find(Comp_Seat *s, struct wl_resource *resource)
+{
+ struct wl_client *client = wl_resource_get_client(resource);
+ return eina_hash_find(s->data_devices, &client);
+}
+
+static inline void
+seat_drag_end(Comp_Seat *s)
+{
+ s->drag.tch = 0;
+ s->drag.id = 0;
+ s->drag.surface = NULL;
+ s->drag.res = NULL;
+ s->drag.enter = NULL;
+}
+
+static inline Comp_Seat *
+seat_find(Comp_Surface *cs, const Eo *dev)
+{
+ const Eo *seat = dev;
+ Comp_Seat *s;
+
+ if (!cs->c->parent_disp) return EINA_INLIST_CONTAINER_GET(cs->c->seats, Comp_Seat);
+ if (evas_device_class_get(seat) != EVAS_DEVICE_CLASS_SEAT)
+ seat = evas_device_parent_get(seat);
+ EINA_INLIST_FOREACH(cs->c->seats, s)
+ if (s->dev == seat) return s;
+ return NULL;
+}
+
+static inline Comp_Seat *
+comp_seat_find(Comp *c, const Eo *dev)
+{
+ const Eo *seat = dev;
+ Comp_Seat *s;
+
+ if (!c->parent_disp) return EINA_INLIST_CONTAINER_GET(c->seats, Comp_Seat);
+ if (evas_device_class_get(seat) != EVAS_DEVICE_CLASS_SEAT)
+ seat = evas_device_parent_get(seat);
+ EINA_INLIST_FOREACH(c->seats, s)
+ if (s->dev == seat) return s;
+ return NULL;
+}
+
+static inline Eina_List *
+seat_kbd_active_resources_get(Comp_Seat *s)
+{
+ Eina_List *l, *lcs, *llcs;
+ Comp_Surface *cs;
+
+ if (!s->active_client) return NULL;
+ if (!s->kbd.resources) return NULL;
+ l = eina_hash_find(s->kbd.resources, &s->active_client);
+ if (!l) return NULL;
+ lcs = eina_hash_find(s->c->client_surfaces, &s->active_client);
+ if (!lcs) return NULL;
+ EINA_LIST_REVERSE_FOREACH(lcs, llcs, cs)
+ if (cs->role && (!cs->shell.popup)) return l;
+ return NULL;
+}
+
+static inline Eina_List *
+seat_ptr_resources_get(Comp_Seat *s, struct wl_client *client)
+{
+ return s->ptr.resources ? eina_hash_find(s->ptr.resources, &client) : NULL;
+}
+
+static inline Eina_List *
+seat_tch_resources_get(Comp_Seat *s, struct wl_client *client)
+{
+ return s->tch.resources ? eina_hash_find(s->tch.resources, &client) : NULL;
+}
+
+static void comp_render_pre_proxied(Evas_Object *o, Evas *e, void *event_info);
+static void comp_render_post_proxied(Comp_Surface *cs, Evas *e, void *event_info);
+static void comp_surface_commit_image_state(Comp_Surface *cs, Comp_Buffer *buffer, Evas_Object *o);
+
+static void
+comp_surface_proxy_del(void *data, Evas *e, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ Comp_Surface *cs = data;
+ int i;
+
+ cs->proxies = eina_list_remove(cs->proxies, obj);
+ for (i = 0; i < 2; i++)
+ {
+ Comp_Buffer *buffer = cs->buffer[i];
+ if (!buffer) continue;
+ if (buffer->renders) buffer->renders = eina_list_remove(buffer->renders, e);
+ if (buffer->post_renders) buffer->post_renders = eina_list_remove(buffer->post_renders, e);
+ }
+ evas_event_callback_del_full(e, EVAS_CALLBACK_RENDER_PRE, (Evas_Event_Cb)comp_render_pre_proxied, obj);
+ evas_event_callback_del_full(e, EVAS_CALLBACK_RENDER_POST, (Evas_Event_Cb)comp_render_post_proxied, cs);
+#ifdef HAVE_ECORE_X
+ if (ecore_x_display_get())
+ ecore_x_dnd_callback_pos_update_set(NULL, NULL);
+#endif
+}
+
+static void
+comp_surface_proxy_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ int w, h;
+
+ evas_object_geometry_get(obj, NULL, NULL, &w, &h);
+ ecore_evas_resize(data, w, h);
+}
+
+static Eina_Bool
+comp_surface_is_alpha(Comp_Surface *cs, Comp_Buffer *buffer)
+{
+ int format;
+ if (buffer->shm_buffer)
+ format = wl_shm_buffer_get_format(buffer->shm_buffer);
+ else if (buffer->dmabuf_buffer)
+ format = buffer->dmabuf_buffer->attributes.format;
+ else if (cs->c->gl)
+ cs->c->glapi->evasglQueryWaylandBuffer(cs->c->gl, buffer->res, EGL_TEXTURE_FORMAT, &format);
+ else return EINA_FALSE; //not a real case
+
+ switch (format)
+ {
+ case DRM_FORMAT_ARGB8888:
+ case WL_SHM_FORMAT_ARGB8888:
+ case EGL_TEXTURE_RGBA:
+ return EINA_TRUE;
+ default:
+ break;
+ }
+ return EINA_FALSE;
+}
+
+static void
+comp_surface_proxy_win_del(Ecore_Evas *ee)
+{
+ Comp_Seat *s = ecore_evas_data_get(ee, "comp_seat");
+
+ s->drag.proxy_win = NULL;
+ //fprintf(stderr, "PROXY WIN DEL\n");
+}
+
+static void
+seat_drag_proxy_win_add(Comp_Seat *s)
+{
+ Evas_Object *o;
+
+ if (s->drag.proxy_win) abort();
+ evas_object_hide(s->drag.surface->obj);
+ s->drag.proxy_win = ecore_evas_new(NULL, 0, 0, 1, 1, NULL);
+ ecore_evas_data_set(s->drag.proxy_win, "comp_seat", s);
+ ecore_evas_callback_destroy_set(s->drag.proxy_win, comp_surface_proxy_win_del);
+ ecore_evas_alpha_set(s->drag.proxy_win, 1);
+ ecore_evas_borderless_set(s->drag.proxy_win, 1);
+ ecore_evas_override_set(s->drag.proxy_win, 1);
+ ecore_evas_show(s->drag.proxy_win);
+ o = evas_object_image_filled_add(ecore_evas_get(s->drag.proxy_win));
+ evas_object_data_set(o, "comp_surface", s->drag.surface);
+ evas_event_callback_add(ecore_evas_get(s->drag.proxy_win),
+ EVAS_CALLBACK_RENDER_PRE, (Evas_Event_Cb)comp_render_pre_proxied, o);
+ evas_event_callback_add(ecore_evas_get(s->drag.proxy_win),
+ EVAS_CALLBACK_RENDER_POST, (Evas_Event_Cb)comp_render_post_proxied, s->drag.surface);
+ evas_object_image_border_center_fill_set(o, EVAS_BORDER_FILL_SOLID);
+ evas_object_image_colorspace_set(o, EVAS_COLORSPACE_ARGB8888);
+ if (!s->drag.surface->render_queue)
+ {
+ comp_surface_commit_image_state(s->drag.surface, s->drag.surface->buffer[1], o);
+ evas_object_image_alpha_set(o,
+ comp_surface_is_alpha(s->drag.surface, s->drag.surface->buffer[1]));
+ evas_object_image_data_update_add(o, 0, 0, 9999, 9999);
+ }
+ evas_object_show(o);
+ evas_object_event_callback_add(o, EVAS_CALLBACK_DEL,
+ comp_surface_proxy_del, s->drag.surface);
+ evas_object_event_callback_add(o, EVAS_CALLBACK_RESIZE,
+ comp_surface_proxy_resize, s->drag.proxy_win);
+ evas_object_resize(o,
+ s->drag.surface->buffer[!s->drag.surface->render_queue]->w,
+ s->drag.surface->buffer[!s->drag.surface->render_queue]->h);
+ s->drag.surface->proxies = eina_list_append(s->drag.surface->proxies, o);
+}
+
+static void comp_surface_send_data_device_leave(Comp_Surface *cs, Comp_Seat *s);
+static void comp_surface_send_data_device_enter(Comp_Surface *cs, Comp_Seat *s);
+
+static void
+dnd_motion(Comp_Seat *s, int ex, int ey)
+{
+ struct wl_resource *res;
+ Eina_List *l, *lcs;
+ Comp_Surface *cs;
+
+ s->ptr.pos.x = ex;
+ s->ptr.pos.y = ey;
+ if (!s->active_client) return;
+ l = eina_hash_find(s->c->client_surfaces, &s->active_client);
+ EINA_LIST_REVERSE_FOREACH(l, lcs, cs)
+ {
+ int x, y, w, h;
+ if ((!cs->mapped) || (!cs->role)) continue;
+ evas_object_geometry_get(cs->obj, &x, &y, &w, &h);
+ if (!COORDS_INSIDE(ex, ey, x, y, w, h)) continue;
+ if (s->drag.enter != cs)
+ {
+ if (s->drag.enter)
+ comp_surface_send_data_device_leave(s->drag.enter, s);
+ s->drag.enter = cs;
+ if (s->drag.source)
+ comp_surface_send_data_device_enter(cs, s);
+ }
+ if (!s->drag.source) break;
+ res = eina_hash_find(s->data_devices, &s->active_client);
+ wl_data_device_send_motion(res,
+ (unsigned int)((unsigned long long)(ecore_time_get() * 1000.0) & 0xffffffff),
+ wl_fixed_from_int(ex - x), wl_fixed_from_int(ey - y));
+ break;
+ }
+}
+#ifdef HAVE_ECORE_X
+static void x11_send_send(Comp_Data_Device_Source *source, const char* mime_type, int32_t fd, Comp_Data_Device_Offer_Type type);
+#endif
+#include "copiedfromweston.x"
+#ifdef HAVE_ECORE_X
+# include "x11.x"
+#endif
+
+static void
+resource_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+#define CONTAINS(x, y, w, h, xx, yy, ww, hh) \
+ (((xx) >= (x)) && (((x) + (w)) >= ((xx) + (ww))) && ((yy) >= (y)) && (((y) + (h)) >= ((yy) + (hh))))
+#define CONSTRAINED(W, H, X, Y) \
+ !CONTAINS(zx, zy, zw, zh, (X), (Y), (W), (H))
+
+static int
+_apply_positioner_x(int x, Shell_Positioner *sp, Eina_Bool invert)
+{
+ enum zxdg_positioner_v6_anchor an = ZXDG_POSITIONER_V6_ANCHOR_NONE;
+ enum zxdg_positioner_v6_gravity grav = ZXDG_POSITIONER_V6_GRAVITY_NONE;
+
+ if (invert)
+ {
+ if (sp->anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT)
+ an |= ZXDG_POSITIONER_V6_ANCHOR_RIGHT;
+ else if (sp->anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)
+ an |= ZXDG_POSITIONER_V6_ANCHOR_LEFT;
+ if (sp->gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT)
+ grav |= ZXDG_POSITIONER_V6_GRAVITY_RIGHT;
+ else if (sp->gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
+ grav |= ZXDG_POSITIONER_V6_GRAVITY_LEFT;
+ }
+ else
+ {
+ an = sp->anchor;
+ grav = sp->gravity;
+ }
+
+ /* left edge */
+ if (an & ZXDG_POSITIONER_V6_ANCHOR_LEFT)
+ x += sp->anchor_rect.x;
+ /* right edge */
+ else if (an & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)
+ x += sp->anchor_rect.x + sp->anchor_rect.w;
+ /* center */
+ else
+ x += sp->anchor_rect.x + (sp->anchor_rect.w / 2);
+
+ /* flip left over anchor */
+ if (grav & ZXDG_POSITIONER_V6_GRAVITY_LEFT)
+ x -= sp->size.w;
+ /* center on anchor */
+ else if (!(grav & ZXDG_POSITIONER_V6_GRAVITY_RIGHT))
+ x -= sp->size.w / 2;
+ return x;
+}
+
+static int
+_apply_positioner_y(int y, Shell_Positioner *sp, Eina_Bool invert)
+{
+ enum zxdg_positioner_v6_anchor an = ZXDG_POSITIONER_V6_ANCHOR_NONE;
+ enum zxdg_positioner_v6_gravity grav = ZXDG_POSITIONER_V6_GRAVITY_NONE;
+
+ if (invert)
+ {
+ if (sp->anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP)
+ an |= ZXDG_POSITIONER_V6_ANCHOR_BOTTOM;
+ else if (sp->anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)
+ an |= ZXDG_POSITIONER_V6_ANCHOR_TOP;
+ if (sp->gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP)
+ grav |= ZXDG_POSITIONER_V6_GRAVITY_BOTTOM;
+ else if (sp->gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)
+ grav |= ZXDG_POSITIONER_V6_GRAVITY_TOP;
+ }
+ else
+ {
+ an = sp->anchor;
+ grav = sp->gravity;
+ }
+
+ /* up edge */
+ if (an & ZXDG_POSITIONER_V6_ANCHOR_TOP)
+ y += sp->anchor_rect.y;
+ /* bottom edge */
+ else if (an & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)
+ y += sp->anchor_rect.y + sp->anchor_rect.h;
+ /* center */
+ else
+ y += sp->anchor_rect.y + (sp->anchor_rect.h / 2);
+
+ /* flip up over anchor */
+ if (grav & ZXDG_POSITIONER_V6_GRAVITY_TOP)
+ y -= sp->size.h;
+ /* center on anchor */
+ else if (!(grav & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM))
+ y -= sp->size.h / 2;
+ return y;
+}
+
+static Eina_Bool
+_apply_positioner_slide(Comp_Surface *cs, Shell_Positioner *sp, int *x, int *y, int *w, int *h, int zx, int zy, int zw, int zh)
+{
+ int px, py, cx, cy;
+
+ evas_object_geometry_get(cs->parent->obj, &px, &py, NULL, NULL);
+ evas_object_geometry_get(cs->c->obj, &cx, &cy, NULL, NULL);
+ px -= cx, py -= cy;
+ if ((sp->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X) &&
+ (!CONTAINS(zx, zy, zw, zh, *x, zy, *w, 1)))
+ {
+ int sx = *x;
+
+ if (sp->gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT)
+ {
+ if (*x + *w > zx + zw)
+ sx = MAX(zx + zw - *w, px + sp->anchor_rect.x - *w);
+ else if (*x < zx)
+ sx = MIN(zx, px + sp->anchor_rect.x + sp->anchor_rect.w);
+ }
+ else if (sp->gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
+ {
+ if (*x < zx)
+ sx = MIN(zx, *x + sp->anchor_rect.x + sp->anchor_rect.w);
+ else if (*x + *w > zx + zw)
+ sx = MAX(zx + zw - *w, px + sp->anchor_rect.x - *w);
+ }
+ if (CONTAINS(zx, zy, zw, zh, sx, zy, *w, 1))
+ *x = sx;
+ }
+ if (!CONSTRAINED(*w, *h, *x, *y)) return EINA_TRUE;
+ if ((sp->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y) &&
+ (!CONTAINS(zx, zy, zw, zh, zx, *y, 1, *h)))
+ {
+ int sy = *y;
+
+ if (sp->gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP)
+ {
+ if (*y + *h > zy + zh)
+ sy = MAX(zy + zh - *h, py + sp->anchor_rect.y - *h);
+ else if (*y < zy)
+ sy = MIN(zy, py + sp->anchor_rect.y + sp->anchor_rect.h);
+ }
+ else if (sp->gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)
+ {
+ if (*y < zy)
+ sy = MIN(zy, py + sp->anchor_rect.y + sp->anchor_rect.h);
+ else if (*y + *h > zy + zh)
+ sy = MAX(zy + zh - *h, py + sp->anchor_rect.y - *h);
+ }
+ if (CONTAINS(zx, zy, zw, zh, zx, sy, 1, *h))
+ *y = sy;
+ }
+ return !CONSTRAINED(*w, *h, *x, *y);
+}
+
+static void
+_apply_positioner(Comp_Surface *cs, Shell_Positioner *sp)
+{
+ int ix, iy, x, y, w = -1, h = -1, px, py, cx, cy;
+ int zx, zy, zw, zh;
+ /* apply base geometry:
+ * coords are relative to parent
+ */
+ evas_object_geometry_get(cs->parent->obj, &px, &py, NULL, NULL);
+ evas_object_geometry_get(cs->c->obj, &cx, &cy, NULL, NULL);
+ px -= cx, py -= cy;
+ ix = x = px + sp->offset.x;
+ iy = y = py + sp->offset.y;
+
+ if (sp->size.w && sp->size.h)
+ {
+ w = sp->size.w;
+ h = sp->size.h;
+ }
+
+ /* apply policies in order:
+ - anchor (add anchor_rect using anchor point)
+ - gravity (perform flips if gravity is not right|bottom)
+ - constrain (adjust if popup does not fit)
+ */
+ x = _apply_positioner_x(x, sp, 0);
+ y = _apply_positioner_y(y, sp, 0);
+
+ evas_object_geometry_get(cs->c->obj, &zx, &zy, &zw, &zh);
+
+ if (!CONSTRAINED(w, h, x, y))
+ {
+ evas_object_move(cs->obj, zx + x, zy + y);
+ if (w > 0) evas_object_resize(cs->obj, w, h);
+ return;
+ }
+
+ /* assume smart placement:
+ - flip
+ - slide
+ - resize
+ */
+ if ((sp->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X) &&
+ (!CONTAINS(zx, zy, zw, zh, x, zy, w, 1)))
+ {
+ int fx;
+
+ fx = _apply_positioner_x(ix, sp, 1);
+ if (CONTAINS(zx, zy, zw, zh, fx, zy, w, 1))
+ x = fx;
+ }
+ if (!CONSTRAINED(w, h, x, y))
+ {
+ evas_object_move(cs->obj, zx + x, zy + y);
+ if (w > 0) evas_object_resize(cs->obj, w, h);
+ return;
+ }
+ if ((sp->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y) &&
+ (!CONTAINS(zx, zy, zw, zh, zx, y, 1, h)))
+ {
+ int fy;
+
+ fy = _apply_positioner_y(iy, sp, 1);
+ if (CONTAINS(zx, zy, zw, zh, zx, fy, 1, h))
+ y = fy;
+ }
+ if (!CONSTRAINED(w, h, x, y))
+ {
+ evas_object_move(cs->obj, zx + x, zy + y);
+ if (w > 0) evas_object_resize(cs->obj, w, h);
+ return;
+ }
+ if (_apply_positioner_slide(cs, sp, &x, &y, &w, &h, zx, zy, zw, zh))
+ {
+ evas_object_move(cs->obj, zx + x, zy + y);
+ if (w > 0) evas_object_resize(cs->obj, w, h);
+ return;
+ }
+
+ if (!CONSTRAINED(w, h, x, y))
+ {
+ evas_object_move(cs->obj, zx + x, zy + y);
+ if (w > 0) evas_object_resize(cs->obj, w, h);
+ return;
+ }
+
+ if ((sp->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X) &&
+ (!CONTAINS(zx, zy, zw, zh, x, zy, w, 1)))
+ {
+ w = zx + zw - x;
+ if (!CONSTRAINED(w, h, x, y))
+ {
+ evas_object_move(cs->obj, zx + x, zy + y);
+ if (w > 0) evas_object_resize(cs->obj, w, h);
+ return;
+ }
+ }
+ if ((sp->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y) &&
+ (!CONTAINS(zx, zy, zw, zh, zx, y, 1, h)))
+ {
+ h = zy + zh - y;
+ }
+ evas_object_move(cs->obj, zx + x, zy + y);
+ if (w > 0) evas_object_resize(cs->obj, w, h);
+}
+
+static const struct wl_data_offer_interface data_device_offer_interface =
+{
+ data_device_offer_accept,
+ data_device_offer_receive,
+ resource_destroy,
+ data_device_offer_finish,
+ data_device_offer_set_actions,
+};
+
+static void
+data_device_offer_create(Comp_Data_Device_Source *source, struct wl_resource *resource)
+{
+ Comp_Data_Device_Offer *off;
+ Eina_List *l;
+ Eina_Stringshare *type;
+
+ off = calloc(1, sizeof(Comp_Data_Device_Offer));
+ off->res = wl_resource_create(wl_resource_get_client(resource), &wl_data_offer_interface,
+ wl_resource_get_version(resource), 0);
+ wl_resource_set_implementation(off->res, &data_device_offer_interface, off, data_device_offer_impl_destroy);
+ off->source = source;
+ source->offer = off;
+ off->proxy = source->proxy;
+ wl_data_device_send_data_offer(resource, off->res);
+ EINA_LIST_FOREACH(source->mime_types, l, type)
+ wl_data_offer_send_offer(off->res, type);
+}
+
+static void
+comp_buffer_state_alloc(Comp_Buffer_State *cbs)
+{
+ cbs->damages = tiler_new();
+ cbs->opaque = tiler_new();
+ cbs->input = tiler_new();
+}
+
+static void
+comp_buffer_state_clear(Comp_Buffer_State *cbs)
+{
+ eina_tiler_free(cbs->damages);
+ eina_tiler_free(cbs->opaque);
+ eina_tiler_free(cbs->input);
+ while (cbs->frames)
+ wl_resource_destroy(eina_list_data_get(cbs->frames));
+}
+
+static void
+comp_seat_send_modifiers(Comp_Seat *s, struct wl_resource *res, uint32_t serial)
+{
+ wl_keyboard_send_modifiers(res, serial, s->kbd.mods.depressed,
+ s->kbd.mods.latched,
+ s->kbd.mods.locked,
+ s->kbd.mods.group);
+ s->kbd.mods.changed = 1;
+}
+
+static Eina_Bool
+data_device_selection_read(void *d, Ecore_Fd_Handler *fdh)
+{
+ Comp_Data_Device_Source *ds = d;
+ int len;
+
+ do
+ {
+ unsigned char buf[2048];
+
+ len = read(ecore_main_fd_handler_fd_get(fdh), buf, sizeof(buf));
+ if (len > 0)
+ {
+ if (!ds->reader_data)
+ ds->reader_data = eina_binbuf_new();
+ eina_binbuf_append_length(ds->reader_data, buf, len);
+ return ECORE_CALLBACK_RENEW;
+ }
+ } while (0);
+ fdh_del(fdh);
+ ds->reader = NULL;
+ if (len < 0)
+ {
+ eina_binbuf_free(ds->reader_data);
+ ds->reader_data = NULL;
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void
+comp_seat_kbd_data_device_enter(Comp_Seat *s)
+{
+ struct wl_resource *res;
+ Comp_Data_Device_Source *ds = s->selection_source;
+ int fd[2];
+
+ if (!s->kbd.enter) return;
+ res = data_device_find(s, s->kbd.enter->res);
+ if (!res) return;
+ if (ds)
+ {
+ data_device_offer_create(ds, res);
+ ds->offer->type = COMP_DATA_DEVICE_OFFER_TYPE_CLIPBOARD;
+ }
+ if (ds)
+ wl_data_device_send_selection(res, ds->offer ? ds->offer->res : NULL);
+ if ((!ds) || (!ds->mime_types)) return;
+ EINA_SAFETY_ON_TRUE_RETURN(pipe2(fd, O_CLOEXEC) < 0);
+ wl_data_source_send_send(ds->res, eina_list_data_get(ds->mime_types), fd[1]);
+ close(fd[1]);
+ ds->reader = ecore_main_fd_handler_file_add(fd[0], ECORE_FD_READ | ECORE_FD_ERROR,
+ data_device_selection_read, ds, NULL, NULL);
+}
+
+static void
+comp_seats_redo_enter(Comp *c, Comp_Surface *cs)
+{
+ Comp_Seat *s;
+ uint32_t serial;
+ struct wl_client *client = NULL;
+
+ serial = wl_display_next_serial(c->display);
+ if (cs)
+ client = wl_resource_get_client(cs->res);
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ Eina_List *l, *ll;
+ struct wl_resource *res;
+ if (c->active_surface && (cs != c->active_surface))
+ {
+ l = seat_kbd_active_resources_get(s);
+ EINA_LIST_FOREACH(l, ll, res)
+ wl_keyboard_send_leave(res, serial, c->active_surface->res);
+ }
+ s->active_client = client;
+ if (cs)
+ {
+ l = seat_kbd_active_resources_get(s);
+ EINA_LIST_FOREACH(l, ll, res)
+ {
+ wl_keyboard_send_enter(res, serial, cs->res, &s->kbd.keys);
+ comp_seat_send_modifiers(s, res, serial);
+ }
+ }
+ s->kbd.enter = cs;
+ if (s->kbd.enter && s->selection_source)
+ comp_seat_kbd_data_device_enter(s);
+ }
+ c->active_surface = cs;
+}
+
+static void shell_surface_send_configure(Comp_Surface *cs);
+
+static void
+shell_surface_deactivate_recurse(Comp_Surface *cs)
+{
+ Comp_Surface *ccs;
+
+ EINA_INLIST_FOREACH(cs->children, ccs)
+ {
+ if (!ccs->shell.popup) continue;
+ shell_surface_deactivate_recurse(ccs);
+ if (ccs->shell.grabs) evas_object_hide(ccs->obj);
+ }
+}
+
+static void
+shell_surface_activate_recurse(Comp_Surface *cs)
+{
+ Comp_Surface *lcs, *parent = cs->parent;
+ Eina_List *l, *parents = NULL;
+ Eina_Inlist *i;
+
+ if (parent)
+ {
+ /* apply focus to toplevel in case where focus is reverted */
+ while (parent)
+ {
+ parents = eina_list_append(parents, parent);
+ if (!parent->shell.popup) break;
+ parent = parent->parent;
+ }
+ }
+ EINA_INLIST_FOREACH_SAFE(cs->c->surfaces, i, lcs)
+ if (lcs->shell.activated && (lcs != cs))
+ {
+ if ((!parents) || (!eina_list_data_find(parents, lcs)))
+ {
+ lcs->shell.activated = 0;
+ shell_surface_send_configure(lcs);
+ cs->c->surfaces = eina_inlist_promote(cs->c->surfaces, EINA_INLIST_GET(lcs));
+ }
+ }
+ /* last item is the toplevel */
+ EINA_LIST_REVERSE_FOREACH(parents, l, lcs)
+ {
+ if (lcs->shell.activated) continue;
+ lcs->shell.activated = 1;
+ if (!lcs->shell.popup)
+ shell_surface_send_configure(lcs);
+ }
+ eina_list_free(parents);
+}
+
+static void
+shell_surface_send_configure(Comp_Surface *cs)
+{
+ uint32_t serial, *s;
+ struct wl_array states;
+ int w, h;
+
+ cs->shell.new = 0;
+ if (cs->shell.popup)
+ {
+ int x, y, px, py;
+ evas_object_geometry_get(cs->obj, &x, &y, &w, &h);
+ evas_object_geometry_get(cs->parent->obj, &px, &py, NULL, NULL);
+ serial = wl_display_next_serial(cs->c->display);
+ zxdg_popup_v6_send_configure(cs->role, x - px, y - py, w, h);
+ zxdg_surface_v6_send_configure(cs->shell.surface, serial);
+ return;
+ }
+ wl_array_init(&states);
+ s = wl_array_add(&states, sizeof(uint32_t));
+ *s = ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN;
+ if (cs->shell.activated)
+ {
+ s = wl_array_add(&states, sizeof(uint32_t));
+ *s = ZXDG_TOPLEVEL_V6_STATE_ACTIVATED;
+ evas_object_raise(cs->obj);
+ if (cs->parent)
+ cs->parent->children = eina_inlist_demote(cs->parent->children, EINA_INLIST_GET(cs));
+ else
+ cs->c->surfaces = eina_inlist_demote(cs->c->surfaces, EINA_INLIST_GET(cs));
+ shell_surface_activate_recurse(cs);
+ }
+ serial = wl_display_next_serial(cs->c->display);
+ evas_object_geometry_get(cs->c->clip, NULL, NULL, &w, &h);
+ zxdg_toplevel_v6_send_configure(cs->role, w, h, &states);
+ zxdg_surface_v6_send_configure(cs->shell.surface, serial);
+ wl_array_release(&states);
+ if (cs->shell.activated)
+ {
+ Comp_Surface *ccs;
+
+ comp_seats_redo_enter(cs->c, cs);
+ EINA_INLIST_FOREACH(cs->children, ccs)
+ if (ccs->shell.surface && ccs->role && ccs->shell.popup)
+ ccs->shell.activated = cs->shell.activated;
+ }
+ else
+ shell_surface_deactivate_recurse(cs);
+}
+
+static void
+shell_surface_init(Comp_Surface *cs)
+{
+ /* update activate status: newest window is always activated */
+ Comp_Surface *parent = cs->parent;
+
+ if (cs->c->active_surface && parent && (!parent->shell.activated))
+ {
+ /* child windows/popups cannot steal focus from toplevel */
+ shell_surface_send_configure(cs);
+ return;
+ }
+
+ cs->shell.activated = 1;
+ shell_surface_send_configure(cs);
+}
+
+static void
+comp_surface_output_enter(Comp_Surface *cs)
+{
+ Eina_List *l;
+ struct wl_resource *res;
+
+ EINA_LIST_FOREACH(cs->c->output_resources, l, res)
+ if (wl_resource_get_client(res) == wl_resource_get_client(cs->res))
+ wl_surface_send_enter(cs->res, res);
+}
+
+static void
+comp_surface_buffer_detach(Comp_Buffer **pbuffer)
+{
+ Comp_Buffer *buffer;
+
+ buffer = *pbuffer;
+ if (!buffer) return;
+ if (buffer->post_renders)
+ fprintf(stderr, "CRASH %u\n", wl_resource_get_id(buffer->res));
+ eina_list_free(buffer->renders);
+ wl_list_remove(&buffer->destroy_listener.link);
+ //if (buffer->dbg) fprintf(stderr, "BUFFER(%d) RELEASE\n", wl_resource_get_id(buffer->res));
+ wl_resource_queue_event(buffer->res, WL_BUFFER_RELEASE);
+ free(buffer);
+ *pbuffer = NULL;
+}
+
+static void
+comp_surface_buffer_post_render(Comp_Surface *cs)
+{
+ double t = ecore_loop_time_get() - cs->c->wayland_time_base;
+
+ //if (cs->subsurface)
+ //fprintf(stderr, "FRAME(%d)\n", wl_resource_get_id(cs->res));
+ while (cs->frames)
+ {
+ struct wl_resource *frame = eina_list_data_get(cs->frames);
+
+ wl_callback_send_done(frame, t * 1000);
+ wl_resource_destroy(frame);
+ }
+}
+
+static void
+comp_surface_pixels_get(void *data, Evas_Object *obj)
+{
+ Comp_Surface *cs = data;
+ Comp_Buffer *buffer;
+
+ evas_object_image_pixels_dirty_set(obj, 0);
+ evas_object_image_pixels_get_callback_set(obj, NULL, NULL);
+ buffer = cs->buffer[!cs->render_queue];
+ //if (cs->cursor || (buffer->w == 32)) fprintf(stderr, "RENDER(%d) %dx%d\n", wl_resource_get_id(buffer->res), buffer->w, buffer->h);
+ evas_object_image_size_set(obj, buffer->w, buffer->h);
+ evas_object_image_data_set(obj, wl_shm_buffer_get_data(buffer->shm_buffer));
+}
+
+static void
+comp_surface_commit_image_state(Comp_Surface *cs, Comp_Buffer *buffer, Evas_Object *o)
+{
+ if ((!buffer->renders) || (!eina_list_data_find(buffer->renders, evas_object_evas_get(o))))
+ buffer->renders = eina_list_append(buffer->renders, evas_object_evas_get(o));
+ evas_object_image_pixels_dirty_set(o, 1);
+
+ if (buffer->shm_buffer)
+ {
+ //if (cs->subsurface)
+ //fprintf(stderr, "SET CB\n");
+ evas_object_image_pixels_get_callback_set(o, comp_surface_pixels_get, cs);
+ }
+ else
+ {
+ Evas_Native_Surface ns;
+
+ evas_object_image_pixels_get_callback_set(o, NULL, NULL);
+ if (buffer->dmabuf_buffer)
+ {
+ ns.type = EVAS_NATIVE_SURFACE_WL_DMABUF;
+ ns.version = EVAS_NATIVE_SURFACE_VERSION;
+
+ ns.data.wl_dmabuf.attr = &buffer->dmabuf_buffer->attributes;
+ ns.data.wl_dmabuf.resource = buffer->res;
+ }
+ else
+ {
+ ns.type = EVAS_NATIVE_SURFACE_WL;
+ ns.version = EVAS_NATIVE_SURFACE_VERSION;
+ ns.data.wl.legacy_buffer = buffer->res;
+ }
+ evas_object_image_native_surface_set(o, &ns);
+ }
+}
+
+static void
+comp_surface_commit_state(Comp_Surface *cs, Comp_Buffer_State *state)
+{
+ int x, y;
+ Eina_List *l;
+ Evas_Object *o;
+ Comp_Buffer *buffer = NULL;
+
+ if (state->attach)
+ {
+ comp_surface_buffer_detach(&cs->buffer[0]);
+ buffer = cs->buffer[0] = state->buffer;
+
+ if (buffer)
+ {
+ //if (cs->subsurface)
+ //fprintf(stderr, "BUFFER(%d) COMMIT %d\n", wl_resource_get_id(buffer->res), wl_resource_get_id(cs->res));
+ if ((!cs->post_render_queue) && ((!cs->buffer[1]) || (!cs->buffer[1]->post_renders)))
+ comp_surface_buffer_detach(&cs->buffer[1]);
+ }
+ else
+ {
+ evas_object_hide(cs->obj);
+ EINA_LIST_FOREACH(cs->proxies, l, o)
+ evas_object_hide(o);
+ }
+ }
+ evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
+
+ if (buffer && (!cs->mapped))
+ {
+ if (cs->role)
+ evas_object_show(cs->obj);
+ }
+
+ if (state->attach && state->buffer)
+ {
+ evas_object_move(cs->img, x + buffer->x, y + buffer->y);
+ evas_object_resize(cs->obj, buffer->w, buffer->h);
+ }
+ else if (cs->shell.new)
+ shell_surface_init(cs);
+
+ state->attach = 0;
+ state->buffer = NULL;
+
+ cs->frames = eina_list_merge(cs->frames, state->frames);
+ state->frames = NULL;
+
+ if (eina_tiler_empty(state->damages))
+ {
+ comp_surface_buffer_detach(&buffer);
+ comp_surface_buffer_post_render(cs);
+ if (!cs->post_render_queue)
+ {
+ evas_object_image_pixels_dirty_set(cs->img, 0);
+ EINA_LIST_FOREACH(cs->proxies, l, o)
+ evas_object_image_pixels_dirty_set(o, 0);
+ }
+ }
+ else if (buffer)
+ {
+ Eina_Iterator *it;
+ Eina_Rectangle *rect;
+
+ comp_surface_commit_image_state(cs, buffer, cs->img);
+ EINA_LIST_FOREACH(cs->proxies, l, o)
+ comp_surface_commit_image_state(cs, buffer, o);
+
+ it = eina_tiler_iterator_new(state->damages);
+ EINA_ITERATOR_FOREACH(it, rect)
+ {
+ //if (cs->subsurface) fprintf(stderr, "BUFFER(%d) DAMAGE %d\n", wl_resource_get_id(buffer->res), wl_resource_get_id(cs->res));
+ evas_object_image_data_update_add(cs->img, rect->x, rect->y, rect->w, rect->h);
+ EINA_LIST_FOREACH(cs->proxies, l, o)
+ evas_object_image_data_update_add(o, rect->x, rect->y, rect->w, rect->h);
+ }
+ eina_iterator_free(it);
+ if (!cs->render_queue)
+ cs->c->render_queue = eina_list_append(cs->c->render_queue, cs);
+ cs->render_queue = 1;
+ }
+ eina_tiler_clear(state->damages);
+
+ if (state->set_opaque && (!eina_tiler_equal(cs->opaque, state->opaque)))
+ {
+ array_clear(&cs->opaque_rects);
+ if (eina_tiler_empty(state->opaque))
+ {
+ evas_object_image_border_set(cs->img, 0, 0, 0, 0);
+ EINA_LIST_FOREACH(cs->proxies, l, o)
+ evas_object_image_border_set(o, 0, 0, 0, 0);
+ }
+ else /* FIXME: proxied opaque regions */
+ {
+ Eina_Iterator *it;
+ Eina_Rectangle *rect;
+ Evas_Object *r;
+
+ it = eina_tiler_iterator_new(state->opaque);
+ cs->opaque_rects = eina_array_new(1);
+ EINA_ITERATOR_FOREACH(it, rect)
+ {
+ r = evas_object_rectangle_add(cs->c->evas);
+ evas_object_name_set(r, "opaque_rect");
+ evas_object_pass_events_set(r, 1);
+ evas_object_color_set(r, 0, 0, 0, 255);
+ evas_object_smart_member_add(r, cs->obj);
+ evas_object_geometry_set(r, x + rect->x, y + rect->y, rect->w, rect->h);
+ evas_object_stack_below(r, cs->img);
+ evas_object_show(r);
+ eina_array_push(cs->opaque_rects, r);
+ }
+ /* FIXME: maybe use image border here */
+
+ eina_iterator_free(it);
+ }
+ PTR_SWAP(&cs->opaque, &state->opaque);
+ }
+ eina_tiler_clear(state->opaque);
+ state->set_opaque = 0;
+
+ if (state->set_input)
+ {
+ if (eina_tiler_empty(state->input))
+ {
+ array_clear(&cs->input_rects);
+ evas_object_pass_events_set(cs->img, 0);
+ evas_object_pointer_mode_set(cs->img, EVAS_OBJECT_POINTER_MODE_NOGRAB);
+ }
+ else if (!eina_tiler_equal(cs->input, state->input))
+ {
+ Eina_Iterator *it;
+ Eina_Rectangle *rect;
+ Evas_Object *r;
+
+ array_clear(&cs->input_rects);
+ it = eina_tiler_iterator_new(state->input);
+ cs->input_rects = eina_array_new(1);
+ EINA_ITERATOR_FOREACH(it, rect)
+ {
+ r = evas_object_rectangle_add(cs->c->evas);
+ evas_object_name_set(r, "input_rect");
+ evas_object_color_set(r, 0, 0, 0, 0);
+ evas_object_smart_member_add(r, cs->obj);
+ evas_object_geometry_set(r, x + rect->x, y + rect->y, rect->w, rect->h);
+ evas_object_stack_above(r, cs->img);
+ evas_object_pointer_mode_set(r, EVAS_OBJECT_POINTER_MODE_NOGRAB);
+ evas_object_show(r);
+ evas_object_show(r);
+ eina_array_push(cs->input_rects, r);
+ }
+ evas_object_pass_events_set(cs->img, 1);
+ eina_iterator_free(it);
+ }
+ PTR_SWAP(&cs->input, &state->input);
+ }
+ eina_tiler_clear(state->input);
+ state->set_input = 0;
+
+ if (cs->pending_subsurfaces)
+ {
+ cs->subsurfaces = eina_list_free(cs->subsurfaces);
+ PTR_SWAP(&cs->subsurfaces, &cs->pending_subsurfaces);
+ }
+ if (cs->subsurface)
+ {
+ if (cs->subsurface->set_offset)
+ {
+ cs->subsurface->offset.x = cs->subsurface->pending_offset.x;
+ cs->subsurface->offset.y = cs->subsurface->pending_offset.y;
+ cs->subsurface->pending_offset.x = cs->subsurface->pending_offset.y = 0;
+ cs->subsurface->set_offset = 0;
+ evas_object_geometry_get(cs->parent->obj, &x, &y, NULL, NULL);
+ evas_object_move(cs->obj, x + cs->subsurface->offset.x, y + cs->subsurface->offset.y);
+ }
+ }
+}
+
+static void
+comp_surface_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+ if (cs)
+ {
+ if (cs->post_render_queue)
+ {
+ cs->dead = 1;
+ evas_object_pass_events_set(cs->obj, 1);
+ return;
+ }
+ cs->res = NULL;
+ }
+ wl_resource_destroy(resource);
+}
+
+static void
+comp_surface_buffer_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
+{
+ Comp_Buffer *buffer;
+ Comp_Surface *cs;
+
+ buffer = container_of(listener, Comp_Buffer, destroy_listener);
+ cs = buffer->cs;
+ if (cs)
+ {
+ if (cs->buffer[0] == buffer) cs->buffer[0] = NULL;
+ else if (cs->buffer[1] == buffer) cs->buffer[1] = NULL;
+ else if (cs->pending.buffer == buffer) cs->pending.buffer = NULL;
+ else if (cs->subsurface)
+ {
+ if (cs->subsurface->cache.buffer == buffer) cs->subsurface->cache.buffer = NULL;
+ }
+ }
+ free(buffer);
+}
+
+static void
+comp_surface_attach(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *buffer_resource, int32_t x, int32_t y)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+ Comp_Buffer *buffer;
+ struct wl_shm_buffer *shmbuff;
+ struct linux_dmabuf_buffer *dmabuf;
+
+ comp_surface_buffer_detach(&cs->pending.buffer);
+ cs->pending.attach = 1;
+ if (!buffer_resource) return;
+
+ buffer = calloc(1, sizeof(Comp_Buffer));
+ if (cs->subsurface)
+ {
+ //fprintf(stderr, "BUFFER(%d) HELD BY %d\n", wl_resource_get_id(buffer_resource), wl_resource_get_id(resource));
+ buffer->dbg = 1;
+ }
+ buffer->cs = cs;
+ buffer->x = x;
+ buffer->y = y;
+ shmbuff = wl_shm_buffer_get(buffer_resource);
+ dmabuf = linux_dmabuf_buffer_get(buffer_resource);
+ if (shmbuff)
+ {
+ buffer->w = wl_shm_buffer_get_width(shmbuff);
+ buffer->h = wl_shm_buffer_get_height(shmbuff);
+ }
+ else if (dmabuf)
+ {
+ buffer->w = dmabuf->attributes.width;
+ buffer->h = dmabuf->attributes.height;
+ }
+ else if (cs->c->gl)
+ {
+ cs->c->glapi->evasglQueryWaylandBuffer(cs->c->gl, buffer_resource, EGL_WIDTH, &buffer->w);
+ cs->c->glapi->evasglQueryWaylandBuffer(cs->c->gl, buffer_resource, EGL_HEIGHT, &buffer->h);
+ }
+ buffer->shm_buffer = shmbuff;
+ buffer->dmabuf_buffer = dmabuf;
+
+ buffer->res = buffer_resource;
+ buffer->destroy_listener.notify = comp_surface_buffer_destroy;
+ wl_resource_add_destroy_listener(buffer_resource, &buffer->destroy_listener);
+ cs->pending.buffer = buffer;
+}
+
+static void
+comp_surface_damage_buffer(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+ eina_tiler_rect_add(cs->pending.damages, &(Eina_Rectangle){x, y, w, h});
+}
+
+/*
+ * Currently damage and damage_buffer are the same because we don't support
+ * buffer_scale, transform, or viewport. Once we support those we'll have
+ * to make surface_cb_damage handle damage in surface co-ordinates.
+ */
+static void
+comp_surface_damage(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
+{
+ comp_surface_damage_buffer(client, resource, x, y, w, h);
+}
+
+static void
+comp_surface_frame_impl_destroy(struct wl_resource *resource)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+
+ if (!cs) return;
+ if (cs->frames)
+ cs->frames = eina_list_remove(cs->frames, resource);
+ if (cs->pending.frames)
+ cs->pending.frames = eina_list_remove(cs->pending.frames, resource);
+
+ if (cs->subsurface && cs->subsurface->cached)
+ cs->subsurface->cache.frames = eina_list_remove(cs->subsurface->cache.frames, resource);
+}
+
+static void
+comp_surface_frame(struct wl_client *client, struct wl_resource *resource, uint32_t callback)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+ struct wl_resource *res;
+
+ res = wl_resource_create(client, &wl_callback_interface, 1, callback);
+ wl_resource_set_implementation(res, NULL, cs, comp_surface_frame_impl_destroy);
+ cs->pending.frames = eina_list_append(cs->pending.frames, res);
+}
+
+static void
+comp_surface_set_opaque_region(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+
+ cs->pending.set_opaque = 1;
+ eina_tiler_clear(cs->pending.opaque);
+ if (region_resource)
+ eina_tiler_union(cs->pending.opaque, wl_resource_get_user_data(region_resource));
+}
+
+static void
+comp_surface_set_input_region(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+
+ cs->pending.set_input = 1;
+ eina_tiler_clear(cs->pending.input);
+ if (region_resource)
+ eina_tiler_union(cs->pending.input, wl_resource_get_user_data(region_resource));
+}
+
+static void
+subcomp_subsurface_cache_merge(Comp_Subsurface *css)
+{
+ //fprintf(stderr, "CACHE MERGE\n");
+ css->cached = 1;
+ if (css->cache.frames || css->surface->pending.frames)
+ css->cache.frames = eina_list_merge(css->cache.frames, css->surface->pending.frames);
+ css->surface->pending.frames = NULL;
+ eina_tiler_union(css->cache.damages, css->surface->pending.damages);
+ eina_tiler_clear(css->surface->pending.damages);
+ css->cache.set_input = css->surface->pending.set_input;
+ if (css->surface->pending.set_input)
+ {
+ eina_tiler_clear(css->cache.input);
+ PTR_SWAP(&css->cache.input, &css->surface->pending.input);
+ }
+ css->cache.set_opaque = css->surface->pending.set_opaque;
+ if (css->surface->pending.set_opaque)
+ {
+ eina_tiler_clear(css->cache.opaque);
+ PTR_SWAP(&css->cache.opaque, &css->surface->pending.opaque);
+ }
+ css->surface->pending.set_input = 0;
+ css->surface->pending.set_opaque = 0;
+ if (!css->surface->pending.attach) return;
+ css->cache.attach = 1;
+ comp_surface_buffer_detach(&css->cache.buffer);
+ PTR_SWAP(&css->cache.buffer, &css->surface->pending.buffer);
+ css->surface->pending.attach = 0;
+}
+
+static void
+comp_surface_commit(struct wl_client *client, struct wl_resource *resource)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+ Comp_Subsurface *css;
+ Eina_List *l;
+ Comp_Buffer_State *cbs = &cs->pending;
+
+ cs->commit = 1;
+ if (cs->subsurface)
+ {
+ Comp_Surface *parent;
+
+ css = cs->subsurface;
+ parent = cs->parent;
+ if ((!parent->commit) && css->sync)
+ {
+ subcomp_subsurface_cache_merge(css);
+ return;
+ }
+ while (parent && (!parent->commit) && parent->subsurface)
+ {
+ Comp_Subsurface *pss = parent->subsurface;
+
+ if (pss->sync)
+ {
+ subcomp_subsurface_cache_merge(css);
+ return;
+ }
+ parent = parent->parent;
+ }
+
+
+ subcomp_subsurface_cache_merge(css);
+ cbs = &css->cache;
+ }
+
+ comp_surface_commit_state(cs, cbs);
+ EINA_LIST_FOREACH(cs->subsurfaces, l, css)
+ if (css->sync) comp_surface_commit(client, css->surface->res);
+ cs->commit = 0;
+}
+
+static void
+comp_surface_set_buffer_transform(struct wl_client *client, struct wl_resource *resource, int32_t transform)
+{
+ //Comp_Surface *cs = wl_resource_get_user_data(resource);
+}
+
+static void
+comp_surface_set_buffer_scale(struct wl_client *client, struct wl_resource *resource, int32_t scale)
+{
+ //Comp_Surface *cs = wl_resource_get_user_data(resource);
+}
+
+static const struct wl_surface_interface comp_surface_interface =
+{
+ comp_surface_destroy,
+ comp_surface_attach,
+ comp_surface_damage,
+ comp_surface_frame,
+ comp_surface_set_opaque_region,
+ comp_surface_set_input_region,
+ comp_surface_commit,
+ comp_surface_set_buffer_transform,
+ comp_surface_set_buffer_scale,
+ comp_surface_damage_buffer
+};
+
+static void
+comp_surface_impl_destroy(struct wl_resource *resource)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+ Eina_List *subsurfaces;
+ Comp_Subsurface *css;
+ Comp_Seat *s;
+ struct wl_client *client = wl_resource_get_client(resource);
+
+ EINA_INLIST_FOREACH(cs->c->seats, s)
+ {
+ if (s->kbd.enter == cs) s->kbd.enter = NULL;
+ if (s->ptr.enter == cs) s->ptr.enter = NULL;
+ if (s->ptr.cursor.surface == cs) s->ptr.cursor.surface = NULL;
+ if (s->drag.surface == cs) s->drag.surface = NULL;
+ }
+ eina_hash_list_remove(cs->c->client_surfaces, &client, cs);
+ if (cs->render_queue)
+ cs->c->render_queue = eina_list_remove(cs->c->render_queue, cs);
+ subsurfaces = cs->pending_subsurfaces ?: cs->subsurfaces;
+ EINA_LIST_FREE(subsurfaces, css)
+ {
+ evas_object_hide(css->surface->obj);
+ comp_surface_reparent(css->surface, NULL);
+ }
+ if (cs->pending_subsurfaces) eina_list_free(cs->subsurfaces);
+ cs->pending_subsurfaces = cs->subsurfaces = NULL;
+ comp_surface_buffer_detach(&cs->buffer[0]);
+ cs->res = NULL;
+ if (cs->post_render_queue && (!cs->dead))
+ {
+ Eina_List *l;
+ Evas_Object *o;
+
+ cs->dead = 1;
+ evas_object_hide(cs->obj);
+ EINA_LIST_FOREACH(cs->proxies, l, o)
+ evas_object_hide(o);
+ }
+ else
+ {
+ comp_surface_buffer_detach(&cs->buffer[1]);
+ if (!cs->dead) evas_object_del(cs->obj);
+ }
+}
+
+
+static Evas_Smart *comp_surface_smart = NULL;
+
+static inline Eina_Bool
+comp_surface_check_grab(Comp_Surface *cs, Comp_Seat *s)
+{
+ Comp_Surface *parent;
+ if (!s->grab) return EINA_TRUE;
+ if (cs == s->grab) return EINA_TRUE;
+ parent = s->grab->parent;
+ while (parent)
+ {
+ if (cs == parent) return EINA_TRUE;
+ parent = parent->parent;
+ }
+ return EINA_FALSE;
+}
+
+static void
+comp_surface_input_event(Eina_Inlist **list, uint32_t id, uint32_t serial, uint32_t time, Eina_Bool up)
+{
+ Input_Sequence *ev;
+
+ if (up)
+ {
+ EINA_INLIST_FOREACH(*list, ev)
+ if (ev->id == id)
+ {
+ ev->up_serial = serial;
+ ev->up_time = time;
+ return;
+ }
+ return;
+ }
+ ev = calloc(1, sizeof(Input_Sequence));
+ ev->id = id;
+ ev->down_serial = serial;
+ ev->down_time = time;
+ *list = eina_inlist_append(*list, EINA_INLIST_GET(ev));
+}
+
+static void
+comp_surface_send_data_device_enter(Comp_Surface *cs, Comp_Seat *s)
+{
+ struct wl_resource *offer = NULL;
+ int x, y, cx, cy;
+ uint32_t serial;
+ struct wl_resource *res = data_device_find(s, cs->res);
+
+
+ if (!res) return;
+ evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
+ if (s->drag.tch)
+ cx = s->tch.pos.x, cy = s->tch.pos.y;
+ else
+ cx = s->ptr.pos.x, cy = s->ptr.pos.y;
+ if (s->drag.source)
+ {
+ data_device_offer_create(s->drag.source, res);
+ s->drag.source->offer->type = COMP_DATA_DEVICE_OFFER_TYPE_DND;
+ offer = s->drag.source->offer->res;
+ }
+ s->drag.enter = cs;
+ s->ptr.enter_serial = serial = wl_display_next_serial(cs->c->display);
+ wl_data_device_send_enter(res, serial, cs->res,
+ wl_fixed_from_int(cx - x), wl_fixed_from_int(cy - y), offer);
+}
+
+static void
+comp_surface_send_pointer_enter(Comp_Surface *cs, Comp_Seat *s, int cx, int cy)
+{
+ Eina_List *l, *ll;
+ struct wl_resource *res;
+ uint32_t serial;
+ int x, y;
+
+ if (s->ptr.enter && (cs != s->grab)) return;
+ if (!comp_surface_check_grab(cs, s)) return;
+ s->ptr.enter = cs;
+ if (cs->dead) return;
+ if (s->drag.res && (!s->drag.tch))
+ {
+ comp_surface_send_data_device_enter(cs, s);
+ return;
+ }
+ l = seat_ptr_resources_get(s, wl_resource_get_client(cs->res));
+ if (!l) return;
+ s->ptr.enter_serial = serial = wl_display_next_serial(cs->c->display);
+ //fprintf(stderr, "ENTER %s\n", cs->shell.popup ? "POPUP" : "TOPLEVEL");
+ evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
+ EINA_LIST_FOREACH(l, ll, res)
+ wl_pointer_send_enter(res, serial, cs->res,
+ wl_fixed_from_int(cx - x), wl_fixed_from_int(cy - y));
+}
+
+static void
+comp_surface_mouse_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Evas_Event_Mouse_In *ev = event_info;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ comp_surface_send_pointer_enter(data, seat_find(data, ev->dev), ev->canvas.x, ev->canvas.y);
+}
+
+static void
+comp_surface_send_data_device_leave(Comp_Surface *cs, Comp_Seat *s)
+{
+ struct wl_resource *res = data_device_find(s, cs->res);
+ if (!res) return;
+ if (s->drag.source)
+ {
+ s->drag.source->offer->source = NULL;
+ s->drag.source->offer = NULL;
+ }
+ s->drag.enter = NULL;
+ wl_data_device_send_leave(res);
+}
+
+static void
+comp_surface_send_pointer_leave(Comp_Surface *cs, Comp_Seat *s)
+{
+ Eina_List *l, *ll;
+ struct wl_resource *res;
+ uint32_t serial;
+
+ if (s->ptr.enter != cs) return;
+ if (!comp_surface_check_grab(cs, s)) return;
+ s->ptr.enter = NULL;
+ if (cs->dead) return;
+ if (s->drag.res)
+ {
+ comp_surface_send_data_device_leave(cs, s);
+ return;
+ }
+ l = seat_ptr_resources_get(s, wl_resource_get_client(cs->res));
+ if (!l) return;
+ serial = wl_display_next_serial(cs->c->display);
+ //fprintf(stderr, "LEAVE %s\n", cs->shell.popup ? "POPUP" : "TOPLEVEL");
+ EINA_LIST_FOREACH(l, ll, res)
+ wl_pointer_send_leave(res, serial, cs->res);
+}
+
+static void
+comp_surface_mouse_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Evas_Event_Mouse_Out *ev = event_info;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ comp_surface_send_pointer_leave(data, seat_find(data, ev->dev));
+}
+
+static void
+comp_surface_mouse_button(Comp_Surface *cs, Comp_Seat *s, uint32_t timestamp, uint32_t button_id, uint32_t state)
+{
+ uint32_t serial, btn;
+ Eina_List *l, *ll;
+ struct wl_resource *res;
+
+ switch (button_id)
+ {
+ case 1:
+ btn = BTN_LEFT;
+ break;
+ case 2:
+ btn = BTN_MIDDLE;
+ break;
+ case 3:
+ btn = BTN_RIGHT;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ /* these are supposedly axis events */
+ return;
+ default:
+ btn = button_id + BTN_SIDE - 8;
+ break;
+ }
+ if (s->ptr.enter != cs) return;
+ if (!comp_surface_check_grab(cs, s)) return;
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ s->ptr.button_mask |= 1 << button_id;
+ else
+ {
+ if (!(s->ptr.button_mask & (1 << button_id))) return;
+ s->ptr.button_mask &= ~(1 << button_id);
+ if (s->drag.res && (!s->drag.tch))
+ {
+ drag_grab_button(s, timestamp, button_id, WL_POINTER_BUTTON_STATE_RELEASED);
+ comp_surface_input_event(&s->ptr.events, button_id, 0, timestamp, state == WL_POINTER_BUTTON_STATE_RELEASED);
+ s->ptr.enter = NULL;
+ comp_surface_send_pointer_enter(cs, s, s->ptr.pos.x, s->ptr.pos.y);
+ return;
+ }
+ }
+
+ if (cs->dead)
+ {
+ comp_surface_input_event(&s->ptr.events, button_id, 0, timestamp, state == WL_POINTER_BUTTON_STATE_RELEASED);
+ return;
+ }
+
+ l = seat_ptr_resources_get(s, wl_resource_get_client(cs->res));
+ if (!l) return;
+ serial = wl_display_next_serial(s->c->display);
+ comp_surface_input_event(&s->ptr.events, button_id, serial, timestamp, state == WL_POINTER_BUTTON_STATE_RELEASED);
+
+ EINA_LIST_FOREACH(l, ll, res)
+ wl_pointer_send_button(res, serial, timestamp, btn, state);
+}
+
+static void
+comp_surface_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Evas_Event_Mouse_Down *ev = event_info;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ comp_surface_mouse_button(data, seat_find(data, ev->dev), ev->timestamp, ev->button, WL_POINTER_BUTTON_STATE_PRESSED);
+}
+
+static void
+comp_surface_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Evas_Event_Mouse_Down *ev = event_info;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ comp_surface_mouse_button(data, seat_find(data, ev->dev), ev->timestamp, ev->button, WL_POINTER_BUTTON_STATE_RELEASED);
+}
+
+static void
+comp_surface_mouse_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Evas_Event_Mouse_Move *ev = event_info;
+ Comp_Surface *cs = data;
+ Eina_List *l, *ll;
+ struct wl_resource *res;
+ int x, y;
+ Comp_Seat *s;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ if (cs->dead) return;
+ s = seat_find(data, ev->dev);
+ if (s->ptr.enter != cs) return;
+ if (!comp_surface_check_grab(cs, s)) return;
+ if (s->drag.res)
+ {
+ if (s->drag.enter != cs) return;
+ res = data_device_find(s, cs->res);
+ if (!res) return;
+ }
+ else
+ {
+ l = seat_ptr_resources_get(s, wl_resource_get_client(cs->res));
+ if (!l) return;
+ }
+ evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
+ //fprintf(stderr, "MOTION %s\n", cs->shell.popup ? "POPUP" : "TOPLEVEL");
+ if (s->drag.res)
+ wl_data_device_send_motion(res, ev->timestamp,
+ wl_fixed_from_int(ev->cur.canvas.x - x), wl_fixed_from_int(ev->cur.canvas.y - y));
+ else
+ {
+ //int n = 0;
+ EINA_LIST_FOREACH(l, ll, res)
+ {
+ //fprintf(stderr, "motion %d\n", n++);
+ wl_pointer_send_motion(res, ev->timestamp,
+ wl_fixed_from_int(ev->cur.canvas.x - x), wl_fixed_from_int(ev->cur.canvas.y - y));
+ }
+ }
+}
+
+static void
+comp_surface_mouse_wheel(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
+{
+ Evas_Event_Mouse_Wheel *ev = event;
+ Comp_Surface *cs = data;
+ Eina_List *l, *ll;
+ struct wl_resource *res;
+ uint32_t axis, dir;
+ Comp_Seat *s;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ if (ev->direction == 0)
+ axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
+ else
+ axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
+
+ if (ev->z < 0)
+ dir = -wl_fixed_from_int(abs(ev->z));
+ else
+ dir = wl_fixed_from_int(ev->z);
+
+ if (cs->dead) return;
+ s = seat_find(data, ev->dev);
+ if (s->ptr.enter != cs) return;
+ if (!comp_surface_check_grab(cs, s)) return;
+ l = seat_ptr_resources_get(s, wl_resource_get_client(cs->res));
+ if (!l) return;
+ EINA_LIST_FOREACH(l, ll, res)
+ wl_pointer_send_axis(res, ev->timestamp, axis, dir);
+}
+
+static void
+comp_surface_multi_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Evas_Event_Multi_Down *ev = event_info;
+ Comp_Surface *cs = data;
+ Eina_List *l, *ll;
+ struct wl_resource *res;
+ uint32_t serial;
+ int x, y;
+ Comp_Seat *s;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ if (cs->dead) return;
+ s = seat_find(data, ev->dev);
+ if (!comp_surface_check_grab(cs, s)) return;
+ s->tch.enter = cs;
+ l = seat_tch_resources_get(s, wl_resource_get_client(cs->res));
+ if (!l)
+ {
+ comp_surface_input_event(&s->tch.events, ev->device, 0, ev->timestamp, 0);
+ return;
+ }
+ evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
+ serial = wl_display_next_serial(cs->c->display);
+ comp_surface_input_event(&s->tch.events, ev->device, serial, ev->timestamp, 0);
+ EINA_LIST_FOREACH(l, ll, res)
+ wl_touch_send_down(res, serial, ev->timestamp, cs->res, ev->device,
+ wl_fixed_from_int(ev->canvas.x - x), wl_fixed_from_int(ev->canvas.y - y));
+}
+
+static void
+comp_surface_multi_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Evas_Event_Multi_Up *ev = event_info;
+ Comp_Surface *cs = data;
+ Eina_List *l, *ll;
+ struct wl_resource *res;
+ uint32_t serial;
+ Comp_Seat *s;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ if (cs->dead) return;
+ s = seat_find(data, ev->dev);
+ if (!comp_surface_check_grab(cs, s)) return;
+ if (!ev->device)
+ s->tch.enter = NULL;
+ l = seat_tch_resources_get(s, wl_resource_get_client(cs->res));
+ if ((!l) || (s->drag.tch && ((uint32_t)ev->device == s->drag.id)))
+ {
+ if (s->drag.tch)
+ wl_data_device_send_drop(data_device_find(s, cs->res));
+ comp_surface_input_event(&s->tch.events, ev->device, 0, ev->timestamp, 1);
+ return;
+ }
+ serial = wl_display_next_serial(cs->c->display);
+ comp_surface_input_event(&s->tch.events, ev->device, serial, ev->timestamp, 1);
+ EINA_LIST_FOREACH(l, ll, res)
+ wl_touch_send_up(res, serial, ev->timestamp, ev->device);
+}
+
+static void
+comp_surface_multi_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Evas_Event_Multi_Move *ev = event_info;
+ Comp_Surface *cs = data;
+ struct wl_resource *res;
+ int x, y;
+ Comp_Seat *s;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ if (cs->dead) return;
+ s = seat_find(data, ev->dev);
+ if (!comp_surface_check_grab(cs, s)) return;
+ evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
+ if (s->drag.tch)
+ {
+ if (s->drag.enter != cs) return;
+ res = data_device_find(s, cs->res);
+ if (res)
+ wl_data_device_send_motion(res, ev->timestamp,
+ wl_fixed_from_int(ev->cur.canvas.x - x), wl_fixed_from_int(ev->cur.canvas.y - y));
+ return;
+ }
+ else
+ {
+ Eina_List *l, *ll;
+
+ l = seat_tch_resources_get(s, wl_resource_get_client(cs->res));
+ if (!l) return;
+ EINA_LIST_FOREACH(l, ll, res)
+ wl_touch_send_motion(res, ev->timestamp, ev->device,
+ wl_fixed_from_int(ev->cur.canvas.x - x), wl_fixed_from_int(ev->cur.canvas.y - y));
+ }
+}
+
+static void
+comp_surface_smart_add(Evas_Object *obj)
+{
+ Comp_Surface *cs;
+ Evas *e;
+
+ cs = calloc(1, sizeof(Comp_Surface));
+ evas_object_smart_data_set(obj, cs);
+ cs->obj = obj;
+ evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_DOWN, comp_surface_mouse_down, cs);
+ evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_UP, comp_surface_mouse_up, cs);
+ evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_IN, comp_surface_mouse_in, cs);
+ evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_OUT, comp_surface_mouse_out, cs);
+ evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_MOVE, comp_surface_mouse_move, cs);
+ evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_WHEEL, comp_surface_mouse_wheel, cs);
+ evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MULTI_DOWN, comp_surface_multi_down, cs);
+ evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MULTI_UP, comp_surface_multi_up, cs);
+ evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MULTI_MOVE, comp_surface_multi_move, cs);
+ e = evas_object_evas_get(obj);
+ cs->img = evas_object_image_filled_add(e);
+ evas_object_show(cs->img);
+ cs->clip = evas_object_rectangle_add(e);
+
+ evas_object_smart_member_add(cs->img, cs->obj);
+ evas_object_smart_member_add(cs->clip, cs->obj);
+ evas_object_image_border_center_fill_set(cs->img, EVAS_BORDER_FILL_SOLID);
+ evas_object_image_colorspace_set(cs->img, EVAS_COLORSPACE_ARGB8888);
+}
+
+static void
+comp_surface_smart_del(Evas_Object *obj)
+{
+ Comp_Surface *cs = evas_object_smart_data_get(obj);
+
+ array_clear(&cs->input_rects);
+ array_clear(&cs->opaque_rects);
+ eina_tiler_free(cs->opaque);
+ eina_tiler_free(cs->input);
+ comp_buffer_state_clear(&cs->pending);
+ while (cs->proxies)
+ evas_object_del(eina_list_data_get(cs->proxies));
+ if (cs->res)
+ {
+ cs->dead = 1;
+ wl_resource_destroy(cs->res);
+ }
+ evas_object_del(cs->img);
+ evas_object_del(cs->clip);
+ cs->c->surfaces = eina_inlist_remove(cs->c->surfaces, EINA_INLIST_GET(cs));
+ cs->c->surfaces_count--;
+ free(cs);
+}
+
+static void
+comp_surface_smart_move(Evas_Object *obj, int x, int y)
+{
+ Eina_List *l;
+ Evas_Object *o;
+ int px, py, cx, cy;
+
+ evas_object_geometry_get(obj, &px, &py, NULL, NULL);
+ //{
+ //Comp_Surface *cs = evas_object_smart_data_get(obj);
+ //if (cs->cursor)
+ //fprintf(stderr, "COMP %sSURFACE(%p) %d,%d\n", cs->subsurface ? "SUB" : "", cs, x, y);
+ //}
+ l = evas_object_smart_members_get(obj);
+ EINA_LIST_FREE(l, o)
+ {
+ evas_object_geometry_get(o, &cx, &cy, NULL, NULL);
+ evas_object_move(o, x + (cx - px), y + (cy - py));
+ //fprintf(stderr, "SUBOBJ %d,%d\n", x + (cx - px), y + (cy - py));
+ }
+}
+
+static void
+comp_surface_smart_resize(Evas_Object *obj, int w, int h)
+{
+ Comp_Surface *cs = evas_object_smart_data_get(obj);
+ evas_object_resize(cs->clip, w, h);
+ //if (cs->cursor) fprintf(stderr, "COMP %sSURFACE(%p) %dx%d\n", cs->subsurface ? "SUB" : "", cs, w, h);
+ if (cs->drag)
+ evas_object_move(cs->obj, cs->drag->ptr.pos.x, cs->drag->ptr.pos.y);
+}
+
+static void
+comp_surface_smart_show(Evas_Object *obj)
+{
+ Comp_Surface *cs = evas_object_smart_data_get(obj);
+ evas_object_show(cs->clip);
+ cs->mapped = 1;
+}
+
+static void
+comp_surface_smart_hide(Evas_Object *obj)
+{
+ Comp_Surface *cs = evas_object_smart_data_get(obj);
+ Eina_List *l;
+ Evas_Object *o;
+
+ evas_object_hide(cs->clip);
+ cs->mapped = 0;
+
+ if (!cs->shell.activated) return;
+ cs->shell.activated = 0;
+ if (cs->shell.grabs)
+ zxdg_popup_v6_send_popup_done(cs->role);
+ if (cs->parent && cs->shell.popup) return;
+ /* attempt to revert focus based on stacking order */
+ l = evas_object_smart_members_get(evas_object_smart_parent_get(obj));
+ EINA_LIST_FREE(l, o)
+ {
+ Comp_Surface *lcs;
+ if (o == obj) continue;
+ if (!evas_object_visible_get(o)) continue;
+ if (o == cs->c->clip) continue;
+ if (o == cs->c->events) continue;
+ if (!eina_streq(evas_object_type_get(o), "comp_surface")) continue;
+ lcs = evas_object_smart_data_get(o);
+ if ((!lcs->shell.surface) || (!lcs->role)) continue;
+ lcs->shell.activated = 1;
+ if (lcs->shell.popup)
+ evas_object_raise(lcs->obj);
+ else
+ shell_surface_send_configure(lcs);
+ return;
+ }
+ if (cs->c->seats)
+ comp_seats_redo_enter(cs->c, NULL);
+}
+
+static void
+comp_surface_smart_clip_set(Evas_Object *obj, Evas_Object *clip)
+{
+ Comp_Surface *cs = evas_object_smart_data_get(obj);
+ evas_object_clip_set(cs->clip, clip);
+}
+
+static void
+comp_surface_smart_clip_unset(Evas_Object *obj)
+{
+ Comp_Surface *cs = evas_object_smart_data_get(obj);
+ evas_object_clip_unset(cs->clip);
+}
+
+static void
+comp_surface_smart_member_add(Evas_Object *obj, Evas_Object *child)
+{
+ Comp_Surface *cs = evas_object_smart_data_get(obj);
+ if (child != cs->clip) evas_object_clip_set(child, cs->clip);
+}
+
+static void
+comp_surface_smart_member_del(Evas_Object *obj, Evas_Object *child)
+{
+ Comp_Surface *cs = evas_object_smart_data_get(obj);
+ if (child != cs->clip) evas_object_clip_unset(child);
+}
+
+static void
+comp_surface_smart_init(void)
+{
+ if (comp_surface_smart) return;
+ {
+ static const Evas_Smart_Class sc =
+ {
+ "comp_surface",
+ EVAS_SMART_CLASS_VERSION,
+ comp_surface_smart_add,
+ comp_surface_smart_del,
+ comp_surface_smart_move,
+ comp_surface_smart_resize,
+ comp_surface_smart_show,
+ comp_surface_smart_hide,
+ NULL, //color_set
+ comp_surface_smart_clip_set,
+ comp_surface_smart_clip_unset,
+ NULL,
+ comp_surface_smart_member_add,
+ comp_surface_smart_member_del,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ comp_surface_smart = evas_smart_class_new(&sc);
+ }
+}
+
+static void
+comp_surface_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
+{
+ struct wl_resource *res;
+ Comp_Surface *cs;
+ Comp *c = wl_resource_get_user_data(resource);
+ Evas_Object *obj;
+ int x, y;
+
+ res = wl_resource_create(client, &wl_surface_interface, wl_resource_get_version(resource), id);
+ comp_surface_smart_init();
+ obj = evas_object_smart_add(c->evas, comp_surface_smart);
+ cs = evas_object_smart_data_get(obj);
+ cs->res = res;
+ evas_object_smart_member_add(cs->obj, c->obj);
+ cs->c = c;
+ evas_object_geometry_get(c->obj, &x, &y, NULL, NULL);
+ evas_object_move(cs->obj, x, y);
+
+ c->surfaces = eina_inlist_prepend(c->surfaces, EINA_INLIST_GET(cs));
+ c->surfaces_count++;
+ eina_hash_list_append(c->client_surfaces, &client, cs);
+ comp_surface_output_enter(cs);
+
+ cs->opaque = tiler_new();
+ cs->input = tiler_new();
+ comp_buffer_state_alloc(&cs->pending);
+
+ wl_resource_set_implementation(res, &comp_surface_interface, cs, comp_surface_impl_destroy);
+}
+
+/////////////////////////////////////////////////////////////////
+
+static void
+comp_region_add(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
+{
+ Eina_Tiler *tiler = wl_resource_get_user_data(resource);
+ eina_tiler_rect_add(tiler, &(Eina_Rectangle){x, y, w, h});
+}
+
+static void
+comp_region_subtract(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
+{
+ Eina_Tiler *tiler = wl_resource_get_user_data(resource);
+ eina_tiler_rect_del(tiler, &(Eina_Rectangle){x, y, w, h});
+}
+
+static const struct wl_region_interface comp_region_interface =
+{
+ resource_destroy,
+ comp_region_add,
+ comp_region_subtract
+};
+
+static void
+comp_region_impl_destroy(struct wl_resource *resource)
+{
+ eina_tiler_free(wl_resource_get_user_data(resource));
+}
+
+static void
+comp_region_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
+{
+ Eina_Tiler *tiler;
+ struct wl_resource *res;
+
+ tiler = tiler_new();
+ res = wl_resource_create(client, &wl_region_interface, 1, id);
+ wl_resource_set_implementation(res, &comp_region_interface, tiler, comp_region_impl_destroy);
+}
+
+/////////////////////////////////////////////////////////////////
+
+static const struct wl_compositor_interface comp_interface =
+{
+ comp_surface_create,
+ comp_region_create,
+};
+
+static void
+comp_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *res;
+
+ res = wl_resource_create(client, &wl_compositor_interface, version, id);
+ wl_resource_set_implementation(res, &comp_interface, data, NULL);
+}
+
+/////////////////////////////////////////////////////////////////
+
+
+static void
+subcomp_subsurface_impl_destroy(struct wl_resource *resource)
+{
+ Comp_Subsurface *css = wl_resource_get_user_data(resource);
+
+ evas_object_hide(css->surface->obj);
+ if (css->surface->parent)
+ {
+ css->surface->parent->subsurfaces = eina_list_remove(css->surface->parent->subsurfaces, css);
+ if (css->surface->parent->pending_subsurfaces)
+ css->surface->parent->pending_subsurfaces = eina_list_remove(css->surface->parent->pending_subsurfaces, css);
+ }
+ comp_surface_reparent(css->surface, NULL);
+ comp_buffer_state_clear(&css->cache);
+ comp_surface_buffer_detach(&css->cache.buffer);
+ css->surface->subsurface = NULL;
+ css->surface->role = NULL;
+ free(css);
+}
+
+static void
+subcomp_subsurface_set_position(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y)
+{
+ Comp_Subsurface *css = wl_resource_get_user_data(resource);
+
+ css->pending_offset.x = x;
+ css->pending_offset.y = y;
+ css->set_offset = 1;
+}
+
+static void
+subcomp_subsurface_place_above(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *sibling_resource)
+{
+ Comp_Subsurface *css = wl_resource_get_user_data(resource);
+ Comp_Subsurface *css2 = wl_resource_get_user_data(sibling_resource);
+
+ if ((!css) || (!css2))
+ {
+ wl_resource_post_error( (!css) ? resource : sibling_resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "surface is not a valid subsurface");
+ return;
+ }
+
+ if (!css->surface->parent->pending_subsurfaces)
+ css->surface->parent->pending_subsurfaces = eina_list_clone(css->surface->parent->subsurfaces);
+ css->surface->parent->pending_subsurfaces = eina_list_remove(css->surface->parent->pending_subsurfaces, css);
+ css->surface->parent->pending_subsurfaces = eina_list_append_relative(css->surface->parent->pending_subsurfaces, css, css2);
+}
+
+static void
+subcomp_subsurface_place_below(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *sibling_resource)
+{
+ Comp_Subsurface *css = wl_resource_get_user_data(resource);
+ Comp_Subsurface *css2 = wl_resource_get_user_data(sibling_resource);
+
+ if ((!css) || (!css2))
+ {
+ wl_resource_post_error( (!css) ? resource : sibling_resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "surface is not a valid subsurface");
+ return;
+ }
+
+ if (!css->surface->parent->pending_subsurfaces)
+ css->surface->parent->pending_subsurfaces = eina_list_clone(css->surface->parent->subsurfaces);
+ css->surface->parent->pending_subsurfaces = eina_list_remove(css->surface->parent->pending_subsurfaces, css);
+ css->surface->parent->pending_subsurfaces = eina_list_prepend_relative(css->surface->parent->pending_subsurfaces, css, css2);
+}
+
+static void
+subcomp_subsurface_set_sync(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+ Comp_Subsurface *css = wl_resource_get_user_data(resource);
+ css->sync = 1;
+}
+
+static void
+subcomp_subsurface_set_desync(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+ Comp_Subsurface *css = wl_resource_get_user_data(resource);
+ css->sync = 0;
+}
+
+static const struct wl_subsurface_interface subcomp_subsurface_interface =
+{
+ resource_destroy,
+ subcomp_subsurface_set_position,
+ subcomp_subsurface_place_above,
+ subcomp_subsurface_place_below,
+ subcomp_subsurface_set_sync,
+ subcomp_subsurface_set_desync
+};
+
+static void
+subcomp_subsurface_create(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *parent_resource)
+{
+ Comp_Subsurface *css;
+ Comp_Surface *cs = wl_resource_get_user_data(surface_resource);
+ Comp_Surface *pcs = wl_resource_get_user_data(parent_resource);
+
+ if (surface_resource == parent_resource)
+ {
+ wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "cannot create subsurface as its own child");
+ return;
+ }
+
+ if (cs->role)
+ {
+ wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "surface already has a role");
+ return;
+ }
+
+ css = cs->subsurface = calloc(1, sizeof(Comp_Subsurface));
+ comp_buffer_state_alloc(&css->cache);
+ css->sync = 1;
+ evas_object_name_set(cs->img, "subsurface");
+ css->surface = cs;
+ if (!pcs->pending_subsurfaces)
+ pcs->pending_subsurfaces = eina_list_clone(pcs->subsurfaces);
+ pcs->pending_subsurfaces = eina_list_append(pcs->pending_subsurfaces, css);
+ comp_surface_reparent(cs, pcs);
+
+ cs->role = wl_resource_create(client, &wl_subsurface_interface, 1, id);
+ wl_resource_set_implementation(cs->role, &subcomp_subsurface_interface, css, subcomp_subsurface_impl_destroy);
+}
+
+static const struct wl_subcompositor_interface subcomp_interface =
+{
+ resource_destroy,
+ subcomp_subsurface_create,
+};
+
+static void
+subcomp_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *res;
+
+ res = wl_resource_create(client, &wl_subcompositor_interface, version, id);
+ wl_resource_set_implementation(res, &subcomp_interface, data, NULL);
+}
+
+/////////////////////////////////////////////////////////////////
+
+static void
+data_device_source_offer(struct wl_client *client, struct wl_resource *resource, const char *type)
+{
+ Comp_Data_Device_Source *ds = wl_resource_get_user_data(resource);
+
+ if ((!ds->proxy) || (!ds->x11_owner))
+ ds->mime_types = eina_list_append(ds->mime_types, eina_stringshare_add(type));
+}
+
+static void
+data_device_source_set_actions(struct wl_client *client, struct wl_resource *resource, uint32_t dnd_actions)
+{
+ Comp_Data_Device_Source *ds = wl_resource_get_user_data(resource);
+
+ if (ds->actions_set)
+ {
+ wl_resource_post_error(ds->res, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
+ "dnd_actions already set");
+ return;
+ }
+
+ if (dnd_actions & ~ALL_ACTIONS)
+ {
+ wl_resource_post_error(ds->res, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
+ "invalid dnd_actions");
+ return;
+ }
+
+ if (ds->seat && (!ds->proxy))
+ {
+ wl_resource_post_error(ds->res, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
+ "drag already begun");
+ return;
+ }
+
+ ds->dnd_actions = dnd_actions;
+ ds->actions_set = 1;
+}
+
+static void
+data_device_source_impl_destroy(struct wl_resource *resource)
+{
+ Comp_Data_Device_Source *ds = wl_resource_get_user_data(resource);
+ Eina_Stringshare *type;
+
+ EINA_LIST_FREE(ds->mime_types, type) eina_stringshare_del(type);
+ while (ds->transfers)
+ {
+ Comp_Data_Device_Transfer *dt = EINA_INLIST_CONTAINER_GET(ds->transfers, Comp_Data_Device_Transfer);
+ fdh_del(dt->fdh);
+ ds->transfers = eina_inlist_remove(ds->transfers, EINA_INLIST_GET(dt));
+ free(dt);
+ }
+ if (ds->seat && (ds->seat->selection_source == ds)) ds->seat->selection_source = NULL;
+ if (ds->seat && (ds->seat->drag.source == ds)) ds->seat->drag.source = NULL;
+ comp_data_device_source_reader_clear(ds);
+ ecore_event_handler_del(ds->proxy_send_handler);
+ if (ds->offer) ds->offer->source = NULL;
+ free(ds);
+}
+
+static const struct wl_data_source_interface data_device_source_interface =
+{
+ data_device_source_offer,
+ resource_destroy,
+ data_device_source_set_actions,
+};
+
+static void
+data_device_manager_source_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
+{
+ Comp_Data_Device_Source *ds;
+ pid_t pid;
+ Comp_Seat *s;
+ Comp *c;
+
+ ds = calloc(1, sizeof(Comp_Data_Device_Source));
+ c = wl_resource_get_user_data(resource);
+ wl_client_get_credentials(client, &pid, NULL, NULL);
+ ds->proxy = c->client_disp && (pid == getpid());
+ ds->res = wl_resource_create(client, &wl_data_source_interface, MIN(wl_resource_get_version(resource), 3), id);
+ wl_resource_set_implementation(ds->res, &data_device_source_interface, ds, data_device_source_impl_destroy);
+ if (!ds->proxy) return;
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ int x, y;
+ struct wl_resource *res;
+ Eina_Array *arr;
+ Eina_Array_Iterator it;
+ unsigned int i;
+ char *type;
+ if (((!s->client_offer) && (!s->drag.x11_owner)) || s->drag.res) continue;
+
+ //proxied drag
+ s->drag.res = resource;
+ s->drag.source = ds;
+ ds->seat = s;
+ if (!s->drag.enter) return;
+ evas_object_geometry_get(s->drag.enter->obj, &x, &y, NULL, NULL);
+ if (s->client_offer)
+ {
+ arr = ecore_wl2_offer_mimes_get(s->client_offer);
+ EINA_ARRAY_ITER_NEXT(arr, i, type, it)
+ ds->mime_types = eina_list_append(ds->mime_types, eina_stringshare_add(type));
+ }
+ else if (s->drag.x11_owner)
+ {
+ PTR_SWAP(&s->drag.x11_types, &ds->mime_types);
+ ds->x11_owner = s->drag.x11_owner;
+ s->drag.x11_owner = 0;
+ }
+ comp_surface_send_data_device_enter(s->drag.enter, s);
+ res = data_device_find(s, s->drag.enter->res);
+ if (!res) return;
+ wl_data_device_send_motion(res,
+ (unsigned int)((unsigned long long)(ecore_time_get() * 1000.0) & 0xffffffff),
+ wl_fixed_from_int(s->ptr.pos.x - x), wl_fixed_from_int(s->ptr.pos.y - y));
+ }
+}
+
+static void
+data_device_manager_device_impl_destroy(struct wl_resource *resource)
+{
+ Comp_Seat *s = wl_resource_get_user_data(resource);
+ struct wl_client *client = wl_resource_get_client(resource);
+ eina_hash_del_by_key(s->data_devices, &client);
+}
+
+static void
+data_device_start_drag(struct wl_client *client, struct wl_resource *resource, struct wl_resource *source_resource, struct wl_resource *origin_resource, struct wl_resource *icon_resource, uint32_t serial)
+{
+ Comp_Seat *s = wl_resource_get_user_data(resource);
+ Comp_Data_Device_Source *ds = NULL;
+ Comp_Surface *cs = wl_resource_get_user_data(origin_resource);
+ Comp_Surface *ics = wl_resource_get_user_data(icon_resource);
+ Comp_Surface *enter;
+ Input_Sequence *ev;
+ Eina_Bool found = EINA_FALSE, up = EINA_FALSE;
+ int cx, cy;
+
+ if (source_resource) ds = wl_resource_get_user_data(source_resource);
+ if ((cs != s->ptr.enter) && (cs != s->tch.enter)) return;
+ enter = s->ptr.enter;
+ if (enter) comp_surface_send_pointer_leave(enter, s);
+ EINA_INLIST_FOREACH(s->tch.events, ev)
+ {
+ if (ev->down_serial == serial)
+ {
+ s->drag.tch = 1;
+ up = !!ev->up_time;
+ found = 1;
+ s->drag.id = ev->id;
+ break;
+ }
+ }
+ if (!found)
+ {
+ EINA_INLIST_FOREACH(s->ptr.events, ev)
+ {
+ if (ev->down_serial == serial)
+ {
+ up = !!ev->up_time;
+ found = 1;
+ s->drag.id = ev->id;
+ break;
+ }
+ }
+ }
+ if (!found) return;
+ if (s->ptr.enter && (!s->drag.tch))
+ comp_surface_send_pointer_leave(s->ptr.enter, s);
+ s->drag.res = resource;
+ s->drag.source = ds;
+ if (ds) ds->seat = s;
+ s->drag.surface = ics;
+ if (ds && ds->proxy && s->drag.x11_owner)
+ ds->x11_owner = s->drag.x11_owner;
+ s->drag.x11_owner = 0;
+
+ if (s->drag.tch)
+ {
+ cx = s->tch.pos.x;
+ cy = s->tch.pos.y;
+ }
+ else
+ {
+ cx = s->ptr.pos.x;
+ cy = s->ptr.pos.y;
+ }
+ if (ics)
+ {
+ int w, h;
+
+ ics->cursor = 1;
+ ics->drag = s;
+ evas_object_smart_member_del(ics->obj);
+ evas_object_pass_events_set(ics->obj, 1);
+ evas_object_layer_set(ics->obj, EVAS_LAYER_MAX - 1);
+ evas_object_geometry_get(ics->obj, NULL, NULL, &w, &h);
+ evas_object_move(ics->obj, cx, cy);
+ evas_object_show(ics->obj);
+ }
+ if (s->drag.tch)
+ {
+ if (s->tch.enter)
+ comp_surface_send_data_device_enter(s->tch.enter, s);
+ }
+ else if (enter)
+ comp_surface_send_pointer_enter(enter, s, cx, cy);
+#ifdef HAVE_ECORE_X
+ if (ecore_x_display_get())
+ ecore_x_pointer_grab(ecore_evas_window_get(ecore_evas_ecore_evas_get(s->c->evas)));
+#endif
+ if (up) drag_grab_button(s, ev->up_time, ev->id, WL_POINTER_BUTTON_STATE_RELEASED);
+}
+
+static Eina_Bool
+data_device_proxy_send_send(void *d, int t EINA_UNUSED, void *event)
+{
+ Comp_Data_Device_Source *ds = d;
+ Ecore_Wl2_Event_Data_Source_Send *ev = event;
+
+ if (ds->proxy_serial != ev->serial) return ECORE_CALLBACK_RENEW;
+ if (ev->display == ds->seat->c->parent_disp)
+ {
+ if (ecore_wl2_input_seat_id_get(ds->seat->seat) != ev->seat) return ECORE_CALLBACK_RENEW;
+ wl_data_source_send_send(ds->res, ev->type, ev->fd);
+ close(ev->fd);
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void
+data_device_set_selection(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *source_resource, uint32_t serial)
+{
+ Comp_Seat *s = wl_resource_get_user_data(resource);
+ Comp_Data_Device_Source *ds = NULL;
+
+ if (source_resource)
+ ds = wl_resource_get_user_data(source_resource);
+
+ if (ds && ds->actions_set)
+ {
+ wl_resource_post_error(source_resource, WL_DATA_SOURCE_ERROR_INVALID_SOURCE, "invalid source");
+ return;
+ }
+ if (s->selection_source && (s->selection_serial - serial < UINT32_MAX / 2))
+ return;
+ if (s->selection_source)
+ {
+ if (!s->selection_source->transfers)
+ comp_data_device_source_reader_clear(s->selection_source);
+ ecore_event_handler_del(s->selection_source->proxy_send_handler);
+ s->selection_source->proxy_send_handler = NULL;
+ }
+ if (ds) ds->seat = s;
+ s->selection_source = ds;
+ s->selection_serial = serial;
+ comp_seat_kbd_data_device_enter(s);
+ if (ds && ds->proxy && s->x11_selection_owner)
+ ds->x11_owner = s->x11_selection_owner;
+ s->x11_selection_owner = 0;
+ if (1)
+ //if (s->c->data_device_proxy)
+ {
+ if (s->c->parent_disp) //wayland
+ {
+ if (ds && ds->mime_types)
+ {
+ char *t, *types[eina_list_count(ds->mime_types) + 1];
+ Eina_List *l;
+ int i = 0;
+
+ EINA_LIST_FOREACH(ds->mime_types, l, t)
+ types[i++] = t;
+ types[i] = NULL;
+ ds->proxy_serial = ecore_wl2_dnd_selection_set(s->seat, (const char**)types);
+ ds->proxy_send_handler =
+ ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_SEND,
+ data_device_proxy_send_send, ds);
+ }
+ else if (ds)
+ ds->proxy_serial = ecore_wl2_dnd_selection_clear(s->seat);
+ }
+#ifdef HAVE_ECORE_X
+ else if (ds && (!ds->proxy) && ecore_x_display_get())
+ {
+ Eina_List *l;
+ Comp *c;
+ Ecore_X_Time t = ecore_x_current_time_get();
+
+ EINA_LIST_FOREACH(comps, l, c)
+ c->x11_selection = 0;
+ s->c->x11_selection = 1;
+ ecore_x_selection_owner_set(ecore_evas_window_get(ecore_evas_ecore_evas_get(s->c->evas)),
+ ECORE_X_ATOM_SELECTION_CLIPBOARD, t);
+ }
+#endif
+ }
+ s->client_selection_serial = 0;
+}
+
+static const struct wl_data_device_interface data_device_interface =
+{
+ data_device_start_drag,
+ data_device_set_selection,
+ resource_destroy,
+};
+
+static void
+data_device_manager_device_create(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *seat_resource)
+{
+ Comp_Seat *s = wl_resource_get_user_data(seat_resource);
+ struct wl_resource *res;
+
+ res = wl_resource_create(client, &wl_data_device_interface, wl_resource_get_version(manager_resource), id);
+ wl_resource_set_implementation(res, &data_device_interface, s, data_device_manager_device_impl_destroy);
+ eina_hash_add(s->data_devices, &client, res);
+}
+
+static const struct wl_data_device_manager_interface data_device_manager_interface =
+{
+ data_device_manager_source_create,
+ data_device_manager_device_create
+};
+
+static void
+data_device_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *res;
+
+ res = wl_resource_create(client, &wl_data_device_manager_interface, MIN(3, version), id);
+ wl_resource_set_implementation(res, &data_device_manager_interface, data, NULL);
+}
+
+/////////////////////////////////////////////////////////////////
+
+static void
+output_resize(Comp *c, struct wl_resource *res)
+{
+ int w, h;
+ int rot[][4] =
+ {
+ {
+ [EFL_WL_ROTATION_0] = WL_OUTPUT_TRANSFORM_NORMAL,
+ [EFL_WL_ROTATION_90] = WL_OUTPUT_TRANSFORM_90,
+ [EFL_WL_ROTATION_180] = WL_OUTPUT_TRANSFORM_180,
+ [EFL_WL_ROTATION_270] = WL_OUTPUT_TRANSFORM_270,
+ },
+ {
+ [EFL_WL_ROTATION_0] = WL_OUTPUT_TRANSFORM_FLIPPED,
+ [EFL_WL_ROTATION_90] = WL_OUTPUT_TRANSFORM_FLIPPED_90,
+ [EFL_WL_ROTATION_180] = WL_OUTPUT_TRANSFORM_FLIPPED_180,
+ [EFL_WL_ROTATION_270] = WL_OUTPUT_TRANSFORM_FLIPPED_270,
+ },
+ };
+
+ evas_object_geometry_get(c->clip, NULL, NULL, &w, &h);
+ /* FIXME: transform */
+ wl_output_send_geometry(res, 0, 0, w, h, 0, "", "", rot[c->rtl][c->rotation]);
+ wl_output_send_mode(res, WL_OUTPUT_MODE_CURRENT, w, h, 60 * 1000);
+ if (wl_resource_get_version(res) >= WL_OUTPUT_DONE_SINCE_VERSION)
+ wl_output_send_done(res);
+}
+
+static void
+output_unbind(struct wl_resource *resource)
+{
+ Comp *c = wl_resource_get_user_data(resource);
+ c->output_resources = eina_list_remove(c->output_resources, resource);
+}
+
+static void
+output_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ Comp *c = data;
+ Comp_Surface *cs;
+ struct wl_resource *res;
+
+ res = wl_resource_create(client, &wl_output_interface, version, id);
+ c->output_resources = eina_list_append(c->output_resources, res);
+ wl_resource_set_implementation(res, NULL, data, output_unbind);
+ if (wl_resource_get_version(res) >= WL_OUTPUT_SCALE_SINCE_VERSION)
+ wl_output_send_scale(res, lround(c->scale));
+ output_resize(c, res);
+ EINA_INLIST_FOREACH(c->surfaces, cs)
+ if (wl_resource_get_client(cs->res) == client)
+ comp_surface_output_enter(cs);
+}
+
+/////////////////////////////////////////////////////////////////
+
+static void
+shell_surface_toplevel_impl_destroy(struct wl_resource *resource)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+
+ cs->role = NULL;
+ evas_object_hide(cs->obj);
+ if (!cs->parent) return;
+ comp_surface_reparent(cs, NULL);
+}
+
+static void
+shell_surface_toplevel_set_parent(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *parent_resource)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+ Comp_Surface *pcs = NULL;
+
+ if (parent_resource) pcs = wl_resource_get_user_data(parent_resource);
+
+ comp_surface_reparent(cs, pcs);
+}
+
+static void
+shell_surface_toplevel_set_title(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *title)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+
+ eina_stringshare_replace(&cs->shell.title, title);
+}
+
+static void
+shell_surface_toplevel_set_app_id(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *id)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+
+ eina_stringshare_replace(&cs->shell.app_id, id);
+}
+
+static void
+shell_surface_toplevel_show_window_menu(){}
+static void
+shell_surface_toplevel_move(){}
+static void
+shell_surface_toplevel_resize(){}
+static void
+shell_surface_toplevel_set_max_size(){}
+static void
+shell_surface_toplevel_set_min_size(){}
+static void
+shell_surface_toplevel_set_maximized(){}
+static void
+shell_surface_toplevel_unset_maximized(){}
+static void
+shell_surface_toplevel_set_fullscreen(){}
+static void
+shell_surface_toplevel_unset_fullscreen(){}
+static void
+shell_surface_toplevel_set_minimized(){}
+
+static const struct zxdg_toplevel_v6_interface shell_surface_toplevel_interface =
+{
+ resource_destroy,
+ shell_surface_toplevel_set_parent,
+ shell_surface_toplevel_set_title,
+ shell_surface_toplevel_set_app_id,
+ shell_surface_toplevel_show_window_menu,
+ shell_surface_toplevel_move,
+ shell_surface_toplevel_resize,
+ shell_surface_toplevel_set_max_size,
+ shell_surface_toplevel_set_min_size,
+ shell_surface_toplevel_set_maximized,
+ shell_surface_toplevel_unset_maximized,
+ shell_surface_toplevel_set_fullscreen,
+ shell_surface_toplevel_unset_fullscreen,
+ shell_surface_toplevel_set_minimized,
+};
+
+static void
+shell_surface_toplevel_create(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t id)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+
+ if (cs->buffer[0] || cs->pending.buffer)
+ {
+ wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER,
+ "buffer attached/committed before configure");
+ return;
+ }
+ if (cs->role)
+ {
+ wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_ROLE,
+ "surface already has assigned role");
+ return;
+ }
+
+ cs->role = wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id);
+ wl_resource_set_implementation(cs->role, &shell_surface_toplevel_interface, cs, shell_surface_toplevel_impl_destroy);
+ cs->shell.new = 1;
+}
+
+static void
+shell_surface_popup_grab(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial EINA_UNUSED)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+ Comp_Seat *s = wl_resource_get_user_data(seat);
+
+ if (cs->dead || (!cs->role) || (!cs->shell.surface))
+ {
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "can't grab for this resource");
+ return;
+ }
+ if (cs->mapped)
+ {
+ wl_resource_post_error(resource, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
+ "grab requested on mapped popup");
+ return;
+ }
+
+ if (cs->parent->shell.popup && (s->grab != cs->parent))
+ {
+ wl_resource_post_error(resource, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
+ "grab requested on ungrabbed nested popup");
+ return;
+ }
+ s->grab = cs;
+ cs->shell.grabs = eina_list_append(cs->shell.grabs, s);
+}
+
+static const struct zxdg_popup_v6_interface shell_surface_popup_interface =
+{
+ resource_destroy,
+ shell_surface_popup_grab,
+};
+
+static void
+shell_surface_popup_impl_destroy(struct wl_resource *resource)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+ Comp_Seat *s;
+
+ cs->role = NULL;
+ cs->shell.popup = 0;
+ EINA_LIST_FREE(cs->shell.grabs, s)
+ if (s->grab == cs)
+ {
+ if (cs->parent->shell.grabs &&
+ eina_list_data_find(cs->parent->shell.grabs, s))
+ s->grab = cs->parent;
+ else
+ s->grab = NULL;
+ }
+ if (cs->children)
+ wl_resource_post_error(cs->shell.surface, ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES,
+ "popups dismissed out of order");
+ evas_object_hide(cs->obj);
+ if (cs->parent)
+ comp_surface_reparent(cs, NULL);
+}
+
+static void
+shell_surface_popup_create(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *parent_resource, struct wl_resource *positioner_resource)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+
+ if (cs->buffer[0] || cs->pending.buffer)
+ {
+ wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER,
+ "buffer attached/committed before configure");
+ return;
+ }
+ if (cs->role)
+ {
+ wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_ROLE,
+ "surface already has assigned role");
+ return;
+ }
+ if (!parent_resource)
+ {
+ wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_INVALID_POPUP_PARENT,
+ "popup surface has no parent");
+ return;
+ }
+
+ cs->role = wl_resource_create(client, &zxdg_popup_v6_interface, 1, id);
+ wl_resource_set_implementation(cs->role, &shell_surface_popup_interface, cs, shell_surface_popup_impl_destroy);
+ cs->shell.new = 1;
+ cs->shell.popup = 1;
+ comp_surface_reparent(cs, wl_resource_get_user_data(parent_resource));
+ cs->shell.positioner = wl_resource_get_user_data(positioner_resource);
+ _apply_positioner(cs, cs->shell.positioner);
+}
+
+static void
+_validate_size(struct wl_resource *resource, int32_t value)
+{
+ if (value <= 0)
+ wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid size passed");
+}
+
+static void
+shell_positioner_set_size(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, int32_t w, int32_t h)
+{
+ Shell_Positioner *p = wl_resource_get_user_data(resource);
+
+ _validate_size(resource, w);
+ _validate_size(resource, h);
+
+ p->size.w = w;
+ p->size.h = h;
+}
+
+static void
+shell_positioner_set_anchor_rect(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
+{
+ Shell_Positioner *p = wl_resource_get_user_data(resource);
+
+ _validate_size(resource, w);
+ _validate_size(resource, h);
+
+ EINA_RECTANGLE_SET(&p->anchor_rect, x, y, w, h);
+}
+
+static void
+shell_positioner_set_anchor(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, enum zxdg_positioner_v6_anchor anchor)
+{
+ Shell_Positioner *p = wl_resource_get_user_data(resource);
+
+ if ((anchor & (ZXDG_POSITIONER_V6_ANCHOR_TOP | ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)) ==
+ (ZXDG_POSITIONER_V6_ANCHOR_TOP | ZXDG_POSITIONER_V6_ANCHOR_BOTTOM))
+ wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid anchor values passed");
+ else if ((anchor & (ZXDG_POSITIONER_V6_ANCHOR_LEFT | ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) ==
+ (ZXDG_POSITIONER_V6_ANCHOR_LEFT | ZXDG_POSITIONER_V6_ANCHOR_RIGHT))
+ wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid anchor values passed");
+ else
+ p->anchor = anchor;
+}
+
+static void
+shell_positioner_set_gravity(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, enum zxdg_positioner_v6_gravity gravity)
+{
+ Shell_Positioner *p = wl_resource_get_user_data(resource);
+
+ if ((gravity & (ZXDG_POSITIONER_V6_GRAVITY_TOP | ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)) ==
+ (ZXDG_POSITIONER_V6_GRAVITY_TOP | ZXDG_POSITIONER_V6_GRAVITY_BOTTOM))
+ wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid gravity values passed");
+ else if ((gravity & (ZXDG_POSITIONER_V6_GRAVITY_LEFT | ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) ==
+ (ZXDG_POSITIONER_V6_GRAVITY_LEFT | ZXDG_POSITIONER_V6_GRAVITY_RIGHT))
+ wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid gravity values passed");
+ else
+ p->gravity = gravity;
+}
+
+static void
+shell_positioner_set_constraint_adjustment(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, enum zxdg_positioner_v6_constraint_adjustment constraint_adjustment)
+{
+ Shell_Positioner *p = wl_resource_get_user_data(resource);
+
+ p->constrain = constraint_adjustment;
+}
+
+static void
+shell_positioner_set_offset(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y)
+{
+ Shell_Positioner *p = wl_resource_get_user_data(resource);
+
+ p->offset.x = x;
+ p->offset.y = y;
+}
+
+static const struct zxdg_positioner_v6_interface shell_positioner_interface =
+{
+ resource_destroy,
+ shell_positioner_set_size,
+ shell_positioner_set_anchor_rect,
+ shell_positioner_set_anchor,
+ shell_positioner_set_gravity,
+ shell_positioner_set_constraint_adjustment,
+ shell_positioner_set_offset,
+};
+
+static void
+shell_positioner_impl_destroy(struct wl_resource *resource)
+{
+ Shell_Positioner *sp;
+
+ sp = wl_resource_get_user_data(resource);
+ if (!sp) return;
+ if (sp->sd) sp->sd->positioners = eina_inlist_remove(sp->sd->positioners, EINA_INLIST_GET(sp));
+ free(sp);
+}
+
+static void
+shell_positioner_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
+{
+ struct wl_resource *res;
+ Shell_Data *sd;
+ Shell_Positioner *sp;
+
+ sd = wl_resource_get_user_data(resource);
+ res = wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id);
+ sp = calloc(1, sizeof(Shell_Positioner));
+ sp->sd = sd;
+ sp->res = res;
+ sd->positioners = eina_inlist_append(sd->positioners, EINA_INLIST_GET(sp));
+ wl_resource_set_implementation(res, &shell_positioner_interface, sp, shell_positioner_impl_destroy);
+}
+
+static void
+shell_surface_set_window_geometry(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
+{
+ Comp_Surface *cs = wl_resource_get_user_data(resource);
+
+ EINA_RECTANGLE_SET(&cs->shell.geom, x, y, w, h);
+}
+
+static void
+shell_surface_ack_configure(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t serial)
+{
+}
+
+static const struct zxdg_surface_v6_interface shell_surface_interface =
+{
+ resource_destroy,
+ shell_surface_toplevel_create,
+ shell_surface_popup_create,
+ shell_surface_set_window_geometry,
+ shell_surface_ack_configure,
+};
+
+static void
+shell_surface_impl_destroy(struct wl_resource *resource)
+{
+ Comp_Surface *ccs, *cs = wl_resource_get_user_data(resource);
+
+ if (cs->role)
+ {
+ wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES, "shell surface destroyed before role surfaces");
+ wl_resource_destroy(cs->role);
+ }
+ cs->shell.surface = NULL;
+ cs->shell.data->surfaces = eina_list_remove(cs->shell.data->surfaces, cs);
+ cs->shell.data = NULL;
+ while (cs->children)
+ {
+ ccs = EINA_INLIST_CONTAINER_GET(cs->children, Comp_Surface);
+ evas_object_hide(ccs->obj);
+ comp_surface_reparent(ccs, cs->parent);
+ }
+}
+
+static void
+shell_surface_create(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource)
+{
+ Shell_Data *sd = wl_resource_get_user_data(resource);
+ Comp_Surface *cs = wl_resource_get_user_data(surface_resource);
+
+ if (cs->role || cs->shell.surface)
+ {
+ wl_resource_post_error(surface_resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "surface already has role");
+ return;
+ }
+
+ cs->shell.surface = wl_resource_create(client, &zxdg_surface_v6_interface, 1, id);
+ cs->shell.data = sd;
+ wl_resource_set_implementation(cs->shell.surface, &shell_surface_interface, cs, shell_surface_impl_destroy);
+ sd->surfaces = eina_list_append(sd->surfaces, cs);
+}
+
+static void
+shell_pong(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t serial EINA_UNUSED)
+{
+ Shell_Data *sd = wl_resource_get_user_data(resource);
+
+ sd->ping = 0;
+}
+
+static const struct zxdg_shell_v6_interface shell_interface =
+{
+ resource_destroy,
+ shell_positioner_create,
+ shell_surface_create,
+ shell_pong
+};
+
+static void
+shell_unbind(struct wl_resource *resource)
+{
+ Shell_Data *sd = wl_resource_get_user_data(resource);
+
+ sd->c->shells = eina_inlist_remove(sd->c->shells, EINA_INLIST_GET(sd));
+ while (sd->surfaces)
+ {
+ Comp_Surface *cs = eina_list_data_get(sd->surfaces);
+ if (cs->shell.surface)
+ {
+ if (cs->role)
+ wl_resource_destroy(cs->role);
+ wl_resource_destroy(cs->shell.surface);
+ }
+ }
+ free(sd);
+}
+
+static void
+shell_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ Comp *c = data;
+ struct wl_resource *res;
+ Shell_Data *sd;
+
+ sd = calloc(1, sizeof(Shell_Data));
+ sd->c = c;
+ c->shells = eina_inlist_append(c->shells, EINA_INLIST_GET(sd));
+
+ res = wl_resource_create(client, &zxdg_shell_v6_interface, version, id);
+ sd->res = res;
+ wl_resource_set_implementation(res, &shell_interface, sd, shell_unbind);
+}
+
+/////////////////////////////////////////////////////////////////
+
+static void
+seat_update_caps(Comp_Seat *s, struct wl_resource *res)
+{
+ enum wl_seat_capability caps = 0;
+ Eina_List *l;
+
+ if (s->pointer)
+ caps |= WL_SEAT_CAPABILITY_POINTER;
+ if (s->keyboard)
+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ if (s->touch)
+ caps |= WL_SEAT_CAPABILITY_TOUCH;
+
+ if (!caps) return;
+ if (res)
+ wl_seat_send_capabilities(res, caps);
+ else
+ EINA_LIST_FOREACH(s->resources, l, res)
+ wl_seat_send_capabilities(res, caps);
+}
+
+static void
+seat_keymap_update(Comp_Seat *s)
+{
+ char *str;
+ Eina_Tmpstr *file;
+ struct wl_resource *res;
+ Eina_Iterator *it;
+ xkb_mod_mask_t latched = 0, locked = 0;
+
+ if (s->kbd.keymap_mem) munmap(s->kbd.keymap_mem, s->kbd.keymap_mem_size);
+ if (s->kbd.keymap_fd > -1) close(s->kbd.keymap_fd);
+
+ if (s->kbd.state)
+ {
+ latched = xkb_state_serialize_mods(s->kbd.state, XKB_STATE_MODS_LATCHED);
+ locked = xkb_state_serialize_mods(s->kbd.state, XKB_STATE_MODS_LOCKED);
+ xkb_state_unref(s->kbd.state);
+ }
+ if (!s->kbd.keymap)
+ {
+ s->kbd.state = NULL;
+ s->kbd.keymap_fd = -1;
+ s->kbd.keymap_mem = NULL;
+ return;
+ }
+
+ s->kbd.state = xkb_state_new(s->kbd.keymap);
+ xkb_state_update_mask(s->kbd.state, 0, latched, locked, 0, 0, 0);
+
+ str = xkb_map_get_as_string(s->kbd.keymap);
+ s->kbd.keymap_mem_size = strlen(str) + 1;
+ s->kbd.keymap_fd = eina_file_mkstemp("comp-keymapXXXXXX", &file);
+ {
+ int flags = fcntl(s->kbd.keymap_fd, F_GETFD);
+ fcntl(s->kbd.keymap_fd, F_SETFD, flags | FD_CLOEXEC);
+ }
+ ftruncate(s->kbd.keymap_fd, s->kbd.keymap_mem_size);
+ eina_file_unlink(file);
+ eina_tmpstr_del(file);
+ s->kbd.keymap_mem =
+ mmap(NULL, s->kbd.keymap_mem_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED, s->kbd.keymap_fd, 0);
+
+ memcpy(s->kbd.keymap_mem, str, s->kbd.keymap_mem_size);
+ s->kbd.keymap_mem[s->kbd.keymap_mem_size] = 0;
+ free(str);
+
+ it = eina_hash_iterator_data_new(s->kbd.resources);
+ EINA_ITERATOR_FOREACH(it, res)
+ wl_keyboard_send_keymap(res, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, s->kbd.keymap_fd, s->kbd.keymap_mem_size);
+ eina_iterator_free(it);
+}
+
+static inline void
+seat_kbd_repeat_rate_update(Comp_Seat *s)
+{
+ double rate, delay;
+
+ if (s->seat)
+ {
+ if (ecore_wl2_input_keyboard_repeat_get(s->seat, &rate, &delay))
+ {
+ s->kbd.repeat_rate = lround(1 / rate);
+ s->kbd.repeat_delay = lround(delay * 1000);
+ }
+ else
+ s->kbd.repeat_rate = s->kbd.repeat_delay = 0;
+ }
+ else
+ {
+ s->kbd.repeat_rate = 40;
+ s->kbd.repeat_delay = 400;
+ }
+}
+
+static void
+seat_keymap_create(Comp_Seat *s)
+{
+ struct xkb_rule_names names;
+
+ memset(&names, 0, sizeof(names));
+ names.rules = "evdev";
+ names.model = "pc105";
+ names.layout = "us";
+ s->kbd.context = xkb_context_new(0);
+ s->kbd.keymap = xkb_map_new_from_names(s->kbd.context, &names, 0);
+}
+
+static Eina_Bool
+seat_mods_update(Comp_Seat *s)
+{
+ xkb_mod_mask_t mod;
+ xkb_layout_index_t grp;
+
+ mod = xkb_state_serialize_mods(s->kbd.state, XKB_STATE_DEPRESSED);
+ s->kbd.mods.changed |= mod != s->kbd.mods.depressed;
+ s->kbd.mods.depressed = mod;
+ mod = xkb_state_serialize_mods(s->kbd.state, XKB_STATE_MODS_LATCHED);
+ s->kbd.mods.changed |= mod != s->kbd.mods.latched;
+ s->kbd.mods.latched = mod;
+ mod = xkb_state_serialize_mods(s->kbd.state, XKB_STATE_MODS_LOCKED);
+ s->kbd.mods.changed |= mod != s->kbd.mods.locked;
+ s->kbd.mods.locked = mod;
+ grp = xkb_state_serialize_layout(s->kbd.state, XKB_STATE_LAYOUT_EFFECTIVE);
+ s->kbd.mods.changed |= grp != s->kbd.mods.group;
+ s->kbd.mods.group = grp;
+ return s->kbd.mods.changed;
+}
+
+static const struct wl_keyboard_interface seat_kbd_interface =
+{
+ resource_destroy
+};
+
+static void
+seat_kbd_unbind(struct wl_resource *resource)
+{
+ Comp_Seat *s = wl_resource_get_user_data(resource);
+ struct wl_client *client = wl_resource_get_client(resource);
+
+ eina_hash_list_remove(s->kbd.resources, &client, resource);
+}
+
+static void
+seat_kbd_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
+{
+ Comp_Seat *s = wl_resource_get_user_data(resource);
+ struct wl_resource *res;
+ Eina_List *l, *ll;
+ uint32_t serial;
+
+ res = wl_resource_create(client, &wl_keyboard_interface, wl_resource_get_version(resource), id);
+ wl_resource_set_implementation(res, &seat_kbd_interface, s, seat_kbd_unbind);
+ if (!s->kbd.resources) s->kbd.resources = eina_hash_pointer_new(NULL);
+ eina_hash_list_append(s->kbd.resources, &client, res);
+
+ wl_keyboard_send_keymap(res, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, s->kbd.keymap_fd, s->kbd.keymap_mem_size);
+
+ if (wl_resource_get_version(res) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION)
+ wl_keyboard_send_repeat_info(res, s->kbd.repeat_rate, s->kbd.repeat_delay);
+
+ if (s->active_client != client) return;
+ l = seat_kbd_active_resources_get(s);
+ if (!l) return;
+ serial = wl_display_next_serial(s->c->display);
+ EINA_LIST_FOREACH(l, ll, res)
+ {
+ if (s->c->active_surface)
+ wl_keyboard_send_enter(res, serial, s->c->active_surface->res, &s->kbd.keys);
+ comp_seat_send_modifiers(s, res, serial);
+ }
+}
+
+static void
+seat_ptr_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Comp_Seat *s = data;
+
+ evas_object_event_callback_del_full(s->ptr.efl.obj, EVAS_CALLBACK_DEL, seat_ptr_del, s);
+ s->ptr.efl.obj = NULL;
+}
+
+static void
+seat_ptr_inherit(Comp_Seat *s, Eo *dev)
+{
+ ecore_evas_cursor_device_get(ecore_evas_ecore_evas_get(s->c->evas), dev, &s->ptr.efl.obj,
+ &s->ptr.efl.layer, &s->ptr.efl.x, &s->ptr.efl.y);
+ if (s->ptr.efl.obj)
+ evas_object_event_callback_add(s->ptr.efl.obj, EVAS_CALLBACK_DEL, seat_ptr_del, s);
+}
+
+static void
+seat_ptr_set_cursor(struct wl_client *client, struct wl_resource *resource, uint32_t serial, struct wl_resource *surface_resource, int32_t x, int32_t y)
+{
+ Comp_Seat *s = wl_resource_get_user_data(resource);
+ Comp_Surface *cs = NULL;
+
+ if (!s->active_client) return;
+ if (surface_resource && (s->active_client != wl_resource_get_client(surface_resource))) return;
+ if (s->ptr.enter_serial - serial > UINT32_MAX / 2) return;
+ if (surface_resource)
+ cs = wl_resource_get_user_data(surface_resource);
+ if (cs && cs->role)
+ {
+ wl_resource_post_error(surface_resource,
+ WL_POINTER_ERROR_ROLE, "surface already has role");
+ return;
+ }
+ if (s->ptr.cursor.surface == cs) return;
+ if (cs)
+ {
+ cs->cursor = 1;
+ evas_object_pass_events_set(cs->obj, 1);
+ }
+ if (s->ptr.cursor.surface) s->ptr.cursor.surface->cursor = 0;
+
+ if (s->ptr.in)
+ {
+ const Eina_List *l;
+ Eo *dev;
+ Ecore_Evas *ee = ecore_evas_ecore_evas_get(s->c->evas);
+
+ EINA_LIST_FOREACH(evas_device_list(s->c->evas, s->dev), l, dev)
+ if (evas_device_class_get(dev) == EVAS_DEVICE_CLASS_MOUSE)
+ {
+ if ((!s->ptr.efl.obj) && (!s->ptr.cursor.surface))
+ seat_ptr_inherit(s, dev);
+ ecore_evas_cursor_device_unset(ee, dev);
+ if (cs)
+ ecore_evas_object_cursor_device_set(ee, dev, cs->obj, EVAS_LAYER_MAX, x, y);
+ }
+ }
+ if (cs)
+ evas_object_smart_member_del(cs->obj);
+ s->ptr.cursor.surface = cs;
+ s->ptr.cursor.x = x;
+ s->ptr.cursor.y = y;
+}
+
+static const struct wl_pointer_interface seat_ptr_interface =
+{
+ seat_ptr_set_cursor,
+ resource_destroy
+};
+
+static void
+seat_ptr_unbind(struct wl_resource *resource)
+{
+ Comp_Seat *s = wl_resource_get_user_data(resource);
+ struct wl_client *client = wl_resource_get_client(resource);
+
+ eina_hash_list_remove(s->ptr.resources, &client, resource);
+}
+
+static void
+seat_ptr_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
+{
+ Comp_Seat *s = wl_resource_get_user_data(resource);
+ struct wl_resource *res;
+ Comp_Surface *cs;
+ int x, y;
+
+ res = wl_resource_create(client, &wl_pointer_interface, wl_resource_get_version(resource), id);
+ wl_resource_set_implementation(res, &seat_ptr_interface, s, seat_ptr_unbind);
+ if (!s->ptr.resources) s->ptr.resources = eina_hash_pointer_new(NULL);
+ eina_hash_list_append(s->ptr.resources, &client, res);
+ if (!s->ptr.enter) return;
+ cs = s->ptr.enter;
+ if (wl_resource_get_client(cs->res) != client) return;
+ s->ptr.enter = NULL;
+ evas_pointer_canvas_xy_get(s->c->evas, &x, &y);
+ comp_surface_send_pointer_enter(cs, s, x, y);
+}
+
+static const struct wl_touch_interface seat_tch_interface =
+{
+ resource_destroy
+};
+
+static void
+seat_tch_unbind(struct wl_resource *resource)
+{
+ Comp_Seat *s = wl_resource_get_user_data(resource);
+ struct wl_client *client = wl_resource_get_client(resource);
+
+ eina_hash_list_remove(s->tch.resources, &client, resource);
+}
+
+static void
+seat_tch_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
+{
+ Comp_Seat *s = wl_resource_get_user_data(resource);
+ struct wl_resource *res;
+
+ res = wl_resource_create(client, &wl_pointer_interface, wl_resource_get_version(resource), id);
+ wl_resource_set_implementation(res, &seat_tch_interface, s, seat_tch_unbind);
+ if (!s->tch.resources) s->tch.resources = eina_hash_pointer_new(NULL);
+ eina_hash_list_append(s->tch.resources, &client, res);
+}
+
+static const struct wl_seat_interface seat_interface =
+{
+ seat_ptr_create,
+ seat_kbd_create,
+ seat_tch_create,
+ resource_destroy,
+};
+
+static void
+seat_unbind(struct wl_resource *resource)
+{
+ Comp_Seat *s = wl_resource_get_user_data(resource);
+
+ s->resources = eina_list_remove(s->resources, resource);
+}
+
+static void
+seat_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *res;
+ Comp_Seat *s = data;
+
+ res = wl_resource_create(client, &wl_seat_interface, version, id);
+ s->resources = eina_list_append(s->resources, res);
+ if (s->c->active_surface)
+ s->active_client = wl_resource_get_client(s->c->active_surface->res);
+
+ wl_resource_set_implementation(res, &seat_interface, s, seat_unbind);
+
+ seat_update_caps(s, res);
+ if (s->name && (version >= WL_SEAT_NAME_SINCE_VERSION))
+ wl_seat_send_name(res, s->name);
+}
+
+static void
+seat_resource_hash_free(Eina_Hash *h)
+{
+ Eina_Iterator *it;
+ Eina_List *l;
+ void **key;
+
+ if (!h) return;
+ while (eina_hash_population(h))
+ {
+ it = eina_hash_iterator_key_new(h);
+ EINA_ITERATOR_FOREACH(it, key)
+ {
+ struct wl_resource *res;
+ l = eina_hash_set(h, key, NULL);
+
+ EINA_LIST_FREE(l, res)
+ wl_resource_destroy(res);
+ break;
+ }
+ eina_iterator_free(it);
+ }
+ eina_hash_free(h);
+}
+
+static void
+seat_destroy(Comp_Seat *s)
+{
+ Eina_Stringshare *type;
+ seat_resource_hash_free(s->kbd.resources);
+ seat_resource_hash_free(s->ptr.resources);
+ seat_resource_hash_free(s->tch.resources);
+ while (s->resources)
+ wl_resource_destroy(eina_list_data_get(s->resources));
+ eina_stringshare_del(s->name);
+ if (s->kbd.state) xkb_state_unref(s->kbd.state);
+ if (s->kbd.keymap) xkb_keymap_unref(s->kbd.keymap);
+ if (s->kbd.context) xkb_context_unref(s->kbd.context);
+ if (s->kbd.keymap_mem) munmap(s->kbd.keymap_mem, s->kbd.keymap_mem_size);
+ if (s->kbd.keymap_fd > -1) close(s->kbd.keymap_fd);
+ wl_array_release(&s->kbd.keys);
+ efl_unref(s->dev);
+ s->c->seats = eina_inlist_remove(s->c->seats, EINA_INLIST_GET(s));
+ eina_hash_free(s->data_devices);
+ EINA_LIST_FREE(s->drag.x11_types, type) eina_stringshare_del(type);
+ while (s->ptr.events)
+ {
+ Input_Sequence *ev = EINA_INLIST_CONTAINER_GET(s->ptr.events, Input_Sequence);
+ s->ptr.events = eina_inlist_remove(s->ptr.events, s->ptr.events);
+ free(ev);
+ }
+ while (s->tch.events)
+ {
+ Input_Sequence *ev = EINA_INLIST_CONTAINER_GET(s->tch.events, Input_Sequence);
+ s->tch.events = eina_inlist_remove(s->tch.events, s->tch.events);
+ free(ev);
+ }
+ wl_global_destroy(s->global);
+ free(s);
+
+}
+
+/////////////////////////////////////////////////////////////////
+
+static void
+comp_gl_shutdown(Comp *c)
+{
+ if (c->glapi->evasglUnbindWaylandDisplay)
+ c->glapi->evasglUnbindWaylandDisplay(c->gl, c->display);
+ evas_gl_surface_destroy(c->gl, c->glsfc);
+ evas_gl_context_destroy(c->gl, c->glctx);
+ evas_gl_free(c->gl);
+ evas_gl_config_free(c->glcfg);
+ c->glsfc = NULL;
+ c->glctx = NULL;
+ c->glcfg = NULL;
+ c->gl = NULL;
+}
+
+static void
+comp_gl_init(Comp *c)
+{
+ c->glctx = evas_gl_context_create(c->gl, NULL);
+ c->glcfg = evas_gl_config_new();
+ c->glsfc = evas_gl_surface_create(c->gl, c->glcfg, 1, 1);
+ evas_gl_make_current(c->gl, c->glsfc, c->glctx);
+ c->glapi = evas_gl_context_api_get(c->gl, c->glctx);
+ if (c->glapi->evasglBindWaylandDisplay)
+ c->glapi->evasglBindWaylandDisplay(c->gl, c->display);
+ else
+ comp_gl_shutdown(c);
+}
+
+static void
+comp_render_pre(Comp *c, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Comp_Surface *cs;
+ Eina_List *l, *ll;
+
+ c->rendering = 1;
+ EINA_LIST_FOREACH_SAFE(c->render_queue, l, ll, cs)
+ {
+ Comp_Buffer *buffer;
+//if (cs->subsurface) fprintf(stderr, "RENDER PRE\n");
+ cs->buffer[1] = cs->buffer[0];
+ cs->buffer[0] = NULL;
+ cs->render_queue = 0;
+ buffer = cs->buffer[1];
+
+ if (!buffer)
+ {
+ c->render_queue = eina_list_remove_list(c->render_queue, l);
+ evas_object_image_pixels_dirty_set(cs->img, 0);
+ continue;
+ }
+ //if (cs->proxies) fprintf(stderr, "RENDER %d\n", wl_resource_get_id(buffer->res));
+ cs->post_render_queue = 1;
+
+ evas_object_image_alpha_set(cs->img, comp_surface_is_alpha(cs, buffer));
+ evas_object_resize(cs->img, buffer->w, buffer->h);
+ evas_object_image_size_set(cs->img, buffer->w, buffer->h);
+ }
+ c->post_render_queue = c->render_queue;
+ c->render_queue = NULL;
+}
+
+static void
+comp_render_pre_proxied(Evas_Object *o, Evas *e, void *event_info)
+{
+ Comp_Surface *cs = evas_object_data_get(o, "comp_surface");
+ Comp_Buffer *buffer = cs->buffer[!cs->render_queue];
+
+ //fprintf(stderr, "PROXY RENDER_PRE %d\n", buffer ? wl_resource_get_id(buffer->res) : -1);
+ if (buffer)
+ {
+ buffer->renders = eina_list_remove(buffer->renders, e);
+ buffer->post_renders = eina_list_append(buffer->post_renders, e);
+ evas_object_image_size_set(o, buffer->w, buffer->h);
+ evas_object_image_alpha_set(o, comp_surface_is_alpha(cs, buffer));
+ evas_object_resize(o, buffer->w, buffer->h);
+ }
+ else
+ evas_object_image_pixels_dirty_set(o, 0);
+}
+
+static void
+comp_render_post_proxied(Comp_Surface *cs, Evas *e, void *event_info)
+{
+ Comp_Buffer *buffer;
+
+ buffer = cs->buffer[!cs->render_queue];
+ //fprintf(stderr, "PROXY RENDER_POST %d\n", buffer ? wl_resource_get_id(buffer->res) : -1);
+ buffer->post_renders = eina_list_remove(buffer->post_renders, e);
+ if (buffer->post_renders) return;
+ if (cs->buffer[0] || cs->dead) comp_surface_buffer_detach(&cs->buffer[1]);
+ comp_surface_buffer_post_render(cs);
+}
+
+static void
+comp_render_post(Comp *c, Evas *e, void *event_info EINA_UNUSED)
+{
+ Comp_Surface *cs;
+ Comp_Seat *s;
+
+ c->rendering = 0;
+ EINA_LIST_FREE(c->post_render_queue, cs)
+ {
+ //fprintf(stderr, "POST(%d)\n", wl_resource_get_id(cs->res));
+ cs->buffer[1]->renders = eina_list_remove(cs->buffer[1]->renders, e);
+ if ((!cs->buffer[1]->renders) && (!cs->buffer[1]->post_renders))
+ {
+ if (cs->buffer[0] || cs->dead) comp_surface_buffer_detach(&cs->buffer[1]);
+ comp_surface_buffer_post_render(cs);
+ }
+ cs->post_render_queue = 0;
+ if (!cs->dead) continue;
+ if (cs->res) wl_resource_destroy(cs->res);
+ else evas_object_del(cs->obj);
+ }
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ Input_Sequence *ev;
+ Eina_Inlist *l;
+
+ EINA_INLIST_FOREACH_SAFE(s->ptr.events, l, ev)
+ {
+ if (!ev->up_serial) continue;
+ if (!ev->pass++) continue;
+ s->ptr.events = eina_inlist_remove(s->ptr.events, EINA_INLIST_GET(ev));
+ free(ev);
+ }
+ EINA_INLIST_FOREACH_SAFE(s->tch.events, l, ev)
+ {
+ if (!ev->up_serial) continue;
+ if (!ev->pass++) continue;
+ s->tch.events = eina_inlist_remove(s->tch.events, EINA_INLIST_GET(ev));
+ free(ev);
+ }
+ }
+}
+
+static Evas_Smart *comp_smart = NULL;
+
+static void
+comp_seat_selection_update(Comp_Seat *s)
+{
+ Ecore_Wl2_Offer *offer;
+ Eina_Array *arr;
+ unsigned int i;
+ Eina_Array_Iterator it;
+ char *type;
+
+ if (!s->client_seat) return;
+ s->selection_changed = 0;
+ if (!s->selection_exists)
+ {
+ s->client_selection_serial = ecore_wl2_dnd_selection_clear(s->client_seat);
+ return;
+ }
+ if (!s->seat) return; /* x11_fixes_selection_notify() */
+ offer = ecore_wl2_dnd_selection_get(s->seat);
+ if (!offer) return;
+ arr = ecore_wl2_offer_mimes_get(offer);
+ {
+ char *types[eina_array_count(arr) + 1];
+
+ EINA_ARRAY_ITER_NEXT(arr, i, type, it)
+ types[i] = type;
+ types[i] = NULL;
+ s->client_selection_serial = ecore_wl2_dnd_selection_set(s->client_seat, (const char**)types);
+ }
+}
+
+static void
+seat_drag_offer_update(Comp_Seat *s)
+{
+ Eina_Array *arr;
+ char **types;
+ unsigned int i;
+ Eina_Array_Iterator it;
+ char *type;
+
+ // drag.id = probably double proxied
+ if (s->drag.id || (!s->client_offer)) return;
+ arr = ecore_wl2_offer_mimes_get(s->client_offer);
+ types = alloca(sizeof(void*) * (eina_array_count(arr) + 1));
+
+ EINA_ARRAY_ITER_NEXT(arr, i, type, it)
+ types[i] = type;
+ types[i] = NULL;
+
+ ecore_wl2_dnd_drag_types_set(s->client_seat, (const char**)types);
+ ecore_wl2_dnd_set_actions(s->client_seat);
+}
+
+static void
+seat_proxy_update(Comp_Seat *s)
+{
+ Eina_Iterator *it;
+ Ecore_Wl2_Input *input;
+
+ if (!s->name) return;
+ it = ecore_wl2_display_inputs_get(s->c->client_disp);
+ EINA_ITERATOR_FOREACH(it, input)
+ {
+ const char *name = ecore_wl2_input_name_get(input);
+
+ if (!name) continue;
+ if (!eina_streq(s->name, name)) continue;
+ s->client_seat = input;
+ if (s->selection_changed)
+ comp_seat_selection_update(s);
+ if (s->client_offer)
+ seat_drag_offer_update(s);
+ break;
+ }
+ eina_iterator_free(it);
+}
+
+static void
+comp_seat_proxy_update(Comp_Seat *s)
+{
+ Eina_Iterator *it;
+ Ecore_Wl2_Input *input;
+ Eina_Bool drm;
+
+ if (s->seat) return;
+ drm = !ecore_evas_wayland2_window_get(ecore_evas_ecore_evas_get(s->c->evas));
+ it = ecore_wl2_display_inputs_get(s->c->parent_disp);
+ EINA_ITERATOR_FOREACH(it, input)
+ {
+ if (drm)
+ {
+ if (eina_streq(ecore_wl2_input_name_get(input), evas_device_name_get(s->dev)))
+ {
+ s->seat = input;
+ break;
+ }
+ }
+ else
+ {
+ if (ecore_wl2_input_seat_id_get(input) == evas_device_seat_id_get(s->dev))
+ {
+ s->seat = input;
+ break;
+ }
+ }
+ }
+ eina_iterator_free(it);
+}
+
+static void
+comp_device_caps_update(Comp_Seat *s)
+{
+ const Eina_List *l;
+ Eo *dev;
+ Eina_Bool kbd;
+
+ if (!s) return;
+ kbd = s->keyboard;
+ s->keyboard = s->touch = s->pointer = 0;
+ EINA_LIST_FOREACH(evas_device_list(s->c->evas, s->dev), l, dev)
+ {
+ Evas_Device_Class clas = evas_device_class_get(dev);
+ s->keyboard |= clas == EVAS_DEVICE_CLASS_KEYBOARD;
+ s->touch |= clas == EVAS_DEVICE_CLASS_TOUCH;
+ s->pointer |= (clas == EVAS_DEVICE_CLASS_MOUSE ||
+ clas == EVAS_DEVICE_CLASS_TOUCH ||
+ clas == EVAS_DEVICE_CLASS_PEN ||
+ clas == EVAS_DEVICE_CLASS_POINTER ||
+ clas == EVAS_DEVICE_CLASS_WAND);
+ }
+ if (s->keyboard != kbd)
+ {
+ if (s->keyboard)
+ {
+ seat_keymap_create(s);
+ seat_kbd_repeat_rate_update(s);
+ }
+ else
+ {
+ xkb_keymap_unref(s->kbd.keymap);
+ s->kbd.keymap = NULL;
+ }
+ seat_keymap_update(s);
+ s->keyboard = !!s->kbd.state;
+ }
+ seat_update_caps(s, NULL);
+}
+
+static void
+comp_seats_proxy(Comp *c)
+{
+ const Eina_List *l, *ll;
+ Eo *dev;
+
+ l = evas_device_list(c->evas, NULL);
+ EINA_LIST_FOREACH(l, ll, dev)
+ {
+ Comp_Seat *s;
+ Eina_Bool skip = EINA_FALSE;
+
+ if (evas_device_class_get(dev) != EVAS_DEVICE_CLASS_SEAT) continue;
+ EINA_INLIST_FOREACH(c->seats, s)
+ if (s->dev == dev)
+ {
+ skip = EINA_TRUE;
+ if (c->parent_disp)
+ comp_seat_proxy_update(s);
+ break;
+ }
+ if (skip) continue;
+
+ s = calloc(1, sizeof(Comp_Seat));
+ s->c = c;
+ s->dev = dev;
+ efl_ref(s->dev);
+ if (c->parent_disp)
+ comp_seat_proxy_update(s);
+ s->name = eina_stringshare_ref(evas_device_name_get(dev));
+ s->data_devices = eina_hash_pointer_new(NULL);
+ c->seats = eina_inlist_append(c->seats, EINA_INLIST_GET(s));
+ wl_array_init(&s->kbd.keys);
+ if (s->seat)
+ {
+ Ecore_Wl2_Seat_Capabilities caps = ecore_wl2_input_seat_capabilities_get(s->seat);
+ s->keyboard = !!(caps & ECORE_WL2_SEAT_CAPABILITIES_KEYBOARD);
+ s->pointer = !!(caps & ECORE_WL2_SEAT_CAPABILITIES_POINTER);
+ s->touch = !!(caps & ECORE_WL2_SEAT_CAPABILITIES_TOUCH);
+ if (s->keyboard)
+ {
+ if (s->seat)
+ {
+ s->kbd.keymap = ecore_wl2_input_keymap_get(s->seat);
+ if (s->kbd.keymap) xkb_keymap_ref(s->kbd.keymap);
+ }
+ else
+ seat_keymap_create(s);
+ seat_kbd_repeat_rate_update(s);
+ seat_keymap_update(s);
+ s->keyboard = !!s->kbd.state;
+ }
+
+ }
+ else if (!c->parent_disp)
+ comp_device_caps_update(s);
+ s->global = wl_global_create(c->display, &wl_seat_interface, 4, s, seat_bind);
+ if (ecore_wl2_display_sync_is_done(c->client_disp))
+ seat_proxy_update(s);
+ }
+}
+
+static void
+comp_wayland_features_init(Comp *c)
+{
+ if (ecore_wl2_display_dmabuf_get(c->parent_disp))
+ linux_dmabuf_setup(c->display, c);
+}
+
+static Eina_Bool
+comp_sync_done(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Sync_Done *ev)
+{
+ Eina_List *l;
+ Comp *c;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ if (c->parent_disp == ev->display)
+ {
+ c->client_disp = ecore_wl2_display_connect(c->env);
+ if (!c->seats)
+ comp_seats_proxy(c);
+ comp_wayland_features_init(c);
+ }
+ else if (c->client_disp == ev->display)
+ {
+ Eina_Iterator *it;
+ Ecore_Wl2_Input *input;
+
+ it = ecore_wl2_display_inputs_get(c->client_disp);
+ EINA_ITERATOR_FOREACH(it, input)
+ {
+ Comp_Seat *s;
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ if (!eina_streq(s->name, ecore_wl2_input_name_get(input)))
+ continue;
+ s->client_seat = input;
+ if (s->selection_changed)
+ comp_seat_selection_update(s);
+ if (s->client_offer)
+ seat_drag_offer_update(s);
+ break;
+ }
+ }
+ eina_iterator_free(it);
+ }
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+comp_seat_selection_changed(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Seat_Selection *ev)
+{
+ Eina_List *l;
+ Comp *c;
+ Comp_Seat *s;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ if (c->parent_disp == ev->display)
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ if (s->seat && (ecore_wl2_input_seat_id_get(s->seat) != ev->seat)) continue;
+ s->selection_changed = 1;
+ s->selection_exists = !!ecore_wl2_dnd_selection_get(s->seat);
+ if (!s->focused) continue;
+ comp_seat_selection_update(s);
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+comp_seat_caps_handler(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Seat_Capabilities *ev)
+{
+ Eina_List *l;
+ Comp *c;
+ Comp_Seat *s;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ if (c->parent_disp == ev->display)
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ if (ecore_wl2_input_seat_id_get(s->seat) != ev->id) continue;
+ s->pointer = ev->pointer_enabled;
+ if (s->keyboard != ev->keyboard_enabled)
+ {
+ if (ev->keyboard_enabled)
+ {
+ s->kbd.keymap = ecore_wl2_input_keymap_get(s->seat);
+ if (s->kbd.keymap) xkb_keymap_ref(s->kbd.keymap);
+ seat_kbd_repeat_rate_update(s);
+ }
+ else
+ {
+ xkb_keymap_unref(s->kbd.keymap);
+ s->kbd.keymap = NULL;
+ }
+ seat_keymap_update(s);
+ }
+ s->keyboard = !!ev->keyboard_enabled && !!s->kbd.state;
+ s->touch = ev->touch_enabled;
+ seat_update_caps(s, NULL);
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+comp_seat_name_changed(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Seat_Name *ev)
+{
+ Eina_List *l;
+ Comp *c;
+ Comp_Seat *s;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ if (c->parent_disp == ev->display)
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ Eina_List *lll;
+ struct wl_resource *res;
+
+ if (ecore_wl2_input_seat_id_get(s->seat) != ev->id) continue;
+ eina_stringshare_replace(&s->name, ev->name);
+ EINA_LIST_FOREACH(s->resources, lll, res)
+ if (wl_resource_get_version(res) >= WL_SEAT_NAME_SINCE_VERSION)
+ wl_seat_send_name(res, s->name);
+ seat_proxy_update(s);
+ break;
+ }
+ else if (c->client_disp == ev->display)
+ EINA_INLIST_FOREACH(c->seats, s)
+ if (!s->client_seat) seat_proxy_update(s);
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+comp_seat_keymap_changed(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Seat_Keymap_Changed *ev)
+{
+ Eina_List *l;
+ Comp *c;
+ Comp_Seat *s;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ if (c->parent_disp == ev->display)
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ struct xkb_keymap *keymap;
+
+ if (ecore_wl2_input_seat_id_get(s->seat) != ev->id) continue;
+
+ if (s->kbd.keymap) xkb_map_unref(s->kbd.keymap);
+ s->kbd.keymap = NULL;
+ keymap = ecore_wl2_input_keymap_get(s->seat);
+ if (keymap) s->kbd.keymap = xkb_keymap_ref(keymap);
+ seat_keymap_update(s);
+ if (s->keyboard != (!!s->kbd.state))
+ {
+ s->keyboard = !!s->kbd.state;
+ seat_update_caps(s, NULL);
+ }
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+comp_seat_keyboard_repeat_changed(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Seat_Keyboard_Repeat_Changed *ev)
+{
+ Eina_List *l;
+ Comp *c;
+ Comp_Seat *s;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ if (c->parent_disp == ev->display)
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ Eina_List *ll, *lll;
+ struct wl_resource *res;
+ Eina_Iterator *it;
+
+ if (ecore_wl2_input_seat_id_get(s->seat) != ev->id) continue;
+
+ seat_kbd_repeat_rate_update(s);
+ it = eina_hash_iterator_data_new(s->kbd.resources);
+ EINA_ITERATOR_FOREACH(it, ll)
+ EINA_LIST_FOREACH(ll, lll, res)
+ if (wl_resource_get_version(res) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION)
+ wl_keyboard_send_repeat_info(res, s->kbd.repeat_rate, s->kbd.repeat_delay);
+ eina_iterator_free(it);
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void
+comp_seat_key_update(Comp_Seat *s, xkb_keycode_t key, int dir, unsigned int timestamp)
+{
+ Eina_List *l, *ll;
+ struct wl_resource *res;
+ uint32_t serial = wl_display_next_serial(s->c->display);
+ uint32_t xkb[] = { XKB_KEY_DOWN, XKB_KEY_UP };
+ uint32_t wl[] = { WL_KEYBOARD_KEY_STATE_PRESSED, WL_KEYBOARD_KEY_STATE_RELEASED };
+ Eina_Bool mods = EINA_FALSE;
+
+ if (xkb_state_update_key(s->kbd.state, key + 8, xkb[dir]))
+ mods = seat_mods_update(s);
+ if (!s->focused) return;
+ l = seat_kbd_active_resources_get(s);
+
+ EINA_LIST_FOREACH(l, ll, res)
+ {
+ wl_keyboard_send_key(res, serial, timestamp, key, wl[dir]);
+ if (mods) comp_seat_send_modifiers(s, res, serial);
+ }
+}
+
+static Eina_Bool
+comp_key_down(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Key *ev)
+{
+ uint32_t keycode;
+ Comp *c;
+ Eina_List *l;
+ Comp_Seat *s;
+ const Eo *dev = evas_device_parent_get(ev->dev);
+
+ keycode = (ev->keycode - 8);
+ EINA_LIST_FOREACH(comps, l, c)
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ uint32_t *end, *k;
+
+ if (c->parent_disp && (dev != s->dev)) continue;
+ end = (uint32_t*)s->kbd.keys.data + (s->kbd.keys.size / sizeof(uint32_t));
+
+ for (k = s->kbd.keys.data; k < end; k++)
+ if (*k == keycode) return ECORE_CALLBACK_RENEW;
+
+ s->kbd.keys.size = (char*)end - (char*)s->kbd.keys.data;
+ k = wl_array_add(&s->kbd.keys, sizeof(uint32_t));
+ *k = keycode;
+ comp_seat_key_update(s, keycode, 0, ev->timestamp);
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+comp_key_up(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Key *ev)
+{
+ uint32_t keycode;
+ Comp *c;
+ Eina_List *l;
+ Comp_Seat *s;
+ const Eo *dev = evas_device_parent_get(ev->dev);
+
+ keycode = (ev->keycode - 8);
+ EINA_LIST_FOREACH(comps, l, c)
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ uint32_t *end, *k;
+
+ if (c->parent_disp && (dev != s->dev)) continue;
+ end = (uint32_t*)s->kbd.keys.data + (s->kbd.keys.size / sizeof(uint32_t));
+
+ for (k = s->kbd.keys.data; k < end; k++)
+ if (*k == keycode)
+ {
+ *k = end[-1];
+ s->kbd.keys.size = (char*)end - (char*)s->kbd.keys.data - 1;
+ break;
+ }
+
+ comp_seat_key_update(s, keycode, 1, ev->timestamp);
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void
+comp_device_add(void *d, const Efl_Event *ev)
+{
+ Comp *c = d;
+
+ if (efl_input_device_type_get(ev->info) == EFL_INPUT_DEVICE_TYPE_SEAT)
+ comp_seats_proxy(c);
+ else if (!c->parent_disp)
+ comp_device_caps_update(comp_seat_find(c, ev->info));
+}
+
+static void
+comp_device_del(void *d, const Efl_Event *ev)
+{
+ Comp *c = d;
+ Eo *seat;
+ Eina_Inlist *l;
+ Comp_Seat *s;
+
+ if (efl_input_device_type_get(ev->info) == EFL_INPUT_DEVICE_TYPE_SEAT)
+ seat = ev->info;
+ else
+ seat = efl_input_device_seat_get(ev->info);
+ EINA_INLIST_FOREACH_SAFE(c->seats, l, s)
+ if (seat == s->dev)
+ {
+ if (ev->info == seat)
+ seat_destroy(s);
+ else if (!c->parent_disp)
+ comp_device_caps_update(s);
+ }
+}
+
+static Eina_Bool
+comp_data_device_dnd_leave(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Dnd_Leave *ev)
+{
+ Comp *c;
+ Comp_Seat *s;
+ Eina_List *l;
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ if (ev->display != c->parent_disp) continue;
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ if (ecore_wl2_input_seat_id_get(s->seat) != ev->seat) continue;
+ if (s->drag.id) continue; // probably double proxied
+ if (s->drag.source)
+ wl_data_source_send_cancelled(s->drag.source->res);
+ if (s->client_seat && s->drag.res)
+ {
+ ecore_wl2_dnd_drag_end(s->client_seat);
+ s->client_offer = NULL;
+ }
+ if (s->drag.proxy_win)
+ {
+ if (s->seat)
+ ecore_wl2_dnd_drag_end(s->seat);
+ ecore_evas_free(s->drag.proxy_win);
+ }
+ seat_drag_end(s);
+ s->drag.source = NULL;
+ }
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+comp_data_device_dnd_end(void *d, int t, Ecore_Wl2_Event_Data_Source_End *ev)
+{
+ Comp *c;
+ Comp_Seat *s;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ if (ev->display != c->parent_disp) continue;
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ if (ecore_wl2_input_seat_id_get(s->seat) != ev->seat) continue;
+ if (!s->drag.source) continue;
+ if (s->drag.source->proxy_serial != ev->serial) continue;
+ if (ev->cancelled)
+ wl_data_source_send_cancelled(s->drag.source->res);
+ else
+ data_source_notify_finish(s->drag.source);
+ if (s->drag.proxy_win)
+ {
+ if (s->seat)
+ ecore_wl2_dnd_drag_end(s->seat);
+ ecore_evas_free(s->drag.proxy_win);
+ }
+ seat_drag_end(s);
+ s->drag.source = NULL;
+ s->client_offer = NULL;
+ }
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+comp_data_device_dnd_enter(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Dnd_Enter *ev)
+{
+ Comp *c;
+ Comp_Seat *s;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ if (ev->display != c->parent_disp) continue;
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ if (ecore_wl2_input_seat_id_get(s->seat) != ev->seat) continue;
+ s->client_offer = ev->offer;
+ seat_drag_offer_update(s);
+ }
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+comp_data_device_dnd_motion(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Dnd_Motion *ev)
+{
+ Comp *c;
+ Comp_Seat *s;
+ Eina_List *l;
+ int x, y, w, h, ex, ey;
+ Eina_Bool found = EINA_FALSE;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ int fx, fy;
+
+ if (ev->display != c->parent_disp) continue;
+ evas_output_framespace_get(c->evas, &fx, &fy, NULL, NULL);
+ ex = ev->x - fx;
+ ey = ev->y - fy;
+ evas_object_geometry_get(c->obj, &x, &y, &w, &h);
+ if (!COORDS_INSIDE(ex, ey, x, y, w, h)) continue;
+ found = EINA_TRUE;
+ break;
+ }
+ if (!found) return ECORE_CALLBACK_RENEW;
+
+ EINA_INLIST_FOREACH(c->seats, s)
+ if (ecore_wl2_input_seat_id_get(s->seat) == ev->seat)
+ {
+ dnd_motion(s, ex, ey);
+ break;
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+comp_data_device_dnd_drop(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Dnd_Drop *ev)
+{
+ Comp *c;
+ Comp_Seat *s;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ if (c->parent_disp != ev->display) continue;
+ EINA_INLIST_FOREACH(c->seats, s)
+ if (ecore_wl2_input_seat_id_get(s->seat) == ev->seat)
+ {
+ if (s->drag.enter)
+ {
+ drag_grab_button(s, 0, s->drag.id, WL_POINTER_BUTTON_STATE_RELEASED);
+ if (s->drag.proxy_win)
+ {
+ ecore_wl2_dnd_drag_end(s->seat);
+ ecore_evas_free(s->drag.proxy_win);
+ }
+ return ECORE_CALLBACK_RENEW;
+ }
+ }
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void
+comp_handlers_init(void)
+{
+ Ecore_Event_Handler *h;
+
+ h = ecore_event_handler_add(ECORE_WL2_EVENT_SEAT_CAPABILITIES_CHANGED,
+ (Ecore_Event_Handler_Cb)comp_seat_caps_handler, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_WL2_EVENT_SEAT_KEYMAP_CHANGED,
+ (Ecore_Event_Handler_Cb)comp_seat_keymap_changed, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_WL2_EVENT_SEAT_NAME_CHANGED,
+ (Ecore_Event_Handler_Cb)comp_seat_name_changed, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_WL2_EVENT_SYNC_DONE,
+ (Ecore_Event_Handler_Cb)comp_sync_done, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_WL2_EVENT_SEAT_KEYBOARD_REPEAT_CHANGED,
+ (Ecore_Event_Handler_Cb)comp_seat_keyboard_repeat_changed, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_WL2_EVENT_SEAT_SELECTION,
+ (Ecore_Event_Handler_Cb)comp_seat_selection_changed, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_WL2_EVENT_DND_MOTION,
+ (Ecore_Event_Handler_Cb)comp_data_device_dnd_motion, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_WL2_EVENT_DND_ENTER,
+ (Ecore_Event_Handler_Cb)comp_data_device_dnd_enter, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_WL2_EVENT_DND_LEAVE,
+ (Ecore_Event_Handler_Cb)comp_data_device_dnd_leave, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_END,
+ (Ecore_Event_Handler_Cb)comp_data_device_dnd_end, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_WL2_EVENT_DND_DROP,
+ (Ecore_Event_Handler_Cb)comp_data_device_dnd_drop, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN,
+ (Ecore_Event_Handler_Cb)comp_key_down, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_EVENT_KEY_UP,
+ (Ecore_Event_Handler_Cb)comp_key_up, NULL);
+ handlers = eina_list_append(handlers, h);
+}
+
+static void
+comp_focus_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Comp *c = data;
+ //Efl_Input_Focus_Data *ev = event_info;
+ Comp_Seat *s;
+
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ Eina_List *l, *ll;
+ struct wl_resource *res;
+ uint32_t serial;
+
+ //if (ev->device != s->dev) continue;
+ if (evas_seat_focus_get(c->evas, s->dev) != c->obj) continue;
+ s->focused = 1;
+ if (s->selection_changed)
+ comp_seat_selection_update(s);
+ if (!s->kbd.keymap) continue;
+ if (!seat_mods_update(s)) continue;
+ l = seat_kbd_active_resources_get(s);
+ if (!l) continue;
+ serial = wl_display_next_serial(s->c->display);
+ EINA_LIST_FOREACH(l, ll, res)
+ comp_seat_send_modifiers(s, res, serial);
+ }
+}
+
+static void
+comp_focus_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Comp *c = data;
+ //Efl_Input_Focus_Data *ev = event_info;
+ Comp_Seat *s;
+
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ //if (ev->device != s->dev) continue;
+ if (evas_seat_focus_get(c->evas, s->dev) != c->obj)
+ s->focused = 0;
+ }
+}
+
+static void
+comp_mouse_in(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ Comp *c = data;
+ Evas_Event_Mouse_In *ev = event_info;
+ Comp_Seat *s;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ s = comp_seat_find(c, ev->dev);
+ if (s->drag.proxy_win)
+ {
+ ecore_evas_free(s->drag.proxy_win);
+#ifdef HAVE_ECORE_X
+ if (ecore_x_display_get())
+ {
+ ecore_x_dnd_abort(ecore_evas_window_get(ecore_evas_ecore_evas_get(s->c->evas)));
+ s->drag.x11_owner = 0;
+ }
+#endif
+ if (s->ptr.cursor.surface)
+ evas_object_show(s->ptr.cursor.surface->obj);
+ }
+ if (s->drag.surface)
+ evas_object_show(s->drag.surface->obj);
+ s->ptr.in = 1;
+ seat_ptr_inherit(s, ev->dev);
+ ecore_evas_cursor_device_unset(ecore_evas_ecore_evas_get(e), ev->dev);
+ if (s->ptr.efl.obj) evas_object_hide(s->ptr.efl.obj);
+ if (s->ptr.cursor.surface)
+ ecore_evas_object_cursor_device_set(ecore_evas_ecore_evas_get(e), ev->dev,
+ s->ptr.cursor.surface->obj, EVAS_LAYER_MAX, s->ptr.cursor.x, s->ptr.cursor.y);
+}
+
+static void
+comp_multi_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Comp *c = data;
+ Evas_Event_Multi_Move *ev = event_info;
+ Comp_Seat *s;
+ int w, h;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ s = comp_seat_find(c, ev->dev);
+ s->tch.pos.x = ev->cur.canvas.x;
+ s->tch.pos.y = ev->cur.canvas.y;
+ if ((!s->drag.tch) || (!s->drag.surface)) return;
+ evas_object_geometry_get(s->drag.surface->obj, NULL, NULL, &w, &h);
+ evas_object_move(s->drag.surface->obj, ev->cur.canvas.x, ev->cur.canvas.y);
+}
+
+static void
+comp_mouse_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Comp *c = data;
+ Evas_Event_Mouse_Move *ev = event_info;
+ Comp_Seat *s;
+ int w, h;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ s = comp_seat_find(c, ev->dev);
+ s->ptr.pos.x = ev->cur.canvas.x;
+ s->ptr.pos.y = ev->cur.canvas.y;
+ if (s->drag.tch || (!s->drag.surface)) return;
+ evas_object_geometry_get(s->drag.surface->obj, NULL, NULL, &w, &h);
+ evas_object_move(s->drag.surface->obj, ev->cur.canvas.x, ev->cur.canvas.y);
+}
+
+static void
+comp_mouse_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info)
+{
+ Comp *c = data;
+ Evas_Event_Mouse_Out *ev = event_info;
+ Comp_Seat *s;
+ Eina_List *l;
+ const char **types, *type;
+ unsigned int i = 0;
+
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+ s = comp_seat_find(c, ev->dev);
+ s->ptr.in = 0;
+ ecore_evas_cursor_device_unset(ecore_evas_ecore_evas_get(e), ev->dev);
+ if (s->ptr.efl.obj)
+ {
+ ecore_evas_object_cursor_device_set(ecore_evas_ecore_evas_get(e), ev->dev,
+ s->ptr.efl.obj, s->ptr.efl.layer, s->ptr.efl.x, s->ptr.efl.y);
+ seat_ptr_del(s, NULL, NULL, NULL);
+ }
+ if (s->ptr.cursor.surface) evas_object_hide(s->ptr.cursor.surface->obj);
+ if ((!s->drag.res) || (!s->drag.source) || s->drag.source->proxy) return;
+ if (s->drag.enter) comp_surface_send_data_device_leave(s->drag.enter, s);
+ if (s->drag.surface)
+ seat_drag_proxy_win_add(s);
+ types = alloca(sizeof(void*) * (eina_list_count(s->drag.source->mime_types) + 1));
+ EINA_LIST_FOREACH(s->drag.source->mime_types, l, type)
+ types[i++] = type;
+ types[i] = NULL;
+ if (s->c->parent_disp)
+ {
+ Ecore_Wl2_Window *win = NULL;
+
+ if (s->drag.proxy_win)
+ win = ecore_evas_wayland2_window_get(s->drag.proxy_win);
+ ecore_wl2_dnd_drag_types_set(s->seat, types);
+ s->drag.source->proxy_serial = ecore_wl2_dnd_drag_start(s->seat,
+ ecore_evas_wayland2_window_get(ecore_evas_ecore_evas_get(s->c->evas)),
+ win);
+ s->drag.source->proxy_send_handler =
+ ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_SEND,
+ data_device_proxy_send_send, s->drag.source);
+ }
+#ifdef HAVE_ECORE_X
+ else if (ecore_x_display_get())
+ {
+ Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(s->c->evas));
+ Ecore_Window xwin = ecore_evas_window_get(s->drag.proxy_win);
+ Ecore_X_Atom actions[] = { ECORE_X_DND_ACTION_MOVE, ECORE_X_DND_ACTION_COPY };
+
+ if (s->drag.proxy_win && s->drag.surface)
+ {
+ int x, y, ex, ey;
+ ecore_evas_geometry_get(ecore_evas_ecore_evas_get(s->c->evas), &ex, &ey, NULL, NULL);
+ evas_object_geometry_get(s->drag.surface->obj, &x, &y, NULL, NULL);
+ x += ex, y += ey;
+ ecore_x_window_ignore_set(xwin, 1);
+ ecore_evas_move(s->drag.proxy_win, x, y);
+ }
+ s->drag.x11_owner = win;
+
+ ecore_x_dnd_types_set(win, types, i);
+ ecore_x_dnd_actions_set(win, actions, EINA_C_ARRAY_LENGTH(actions));
+ ecore_x_dnd_self_begin(win, NULL, 0);
+ ecore_x_dnd_callback_pos_update_set(x11_dnd_move, s->drag.proxy_win);
+ }
+#endif
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(comp_device_cbs,
+ { EFL_CANVAS_EVENT_DEVICE_ADDED, comp_device_add },
+ { EFL_CANVAS_EVENT_DEVICE_REMOVED, comp_device_del });
+
+static void
+comp_smart_add(Evas_Object *obj)
+{
+ Comp *c;
+ char *env;
+
+ c = calloc(1, sizeof(Comp));
+ c->wayland_time_base = ecore_loop_time_get();
+ c->obj = obj;
+ evas_object_smart_data_set(obj, c);
+ env = getenv("WAYLAND_DISPLAY");
+ if (env) env = strdup(env);
+ c->disp = ecore_wl2_display_create(NULL);
+ c->env = eina_strdup(getenv("WAYLAND_DISPLAY"));
+ if (env) setenv("WAYLAND_DISPLAY", env, 1);
+ else unsetenv("WAYLAND_DISPLAY");
+ c->display = ecore_wl2_display_get(c->disp);
+ c->client_surfaces = eina_hash_pointer_new(NULL);
+ c->evas = evas_object_evas_get(obj);
+ c->clip = evas_object_rectangle_add(c->evas);
+ evas_object_smart_member_add(c->clip, obj);
+ c->events = evas_object_rectangle_add(c->evas);
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_IN, comp_mouse_in, c);
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_MOVE, comp_mouse_move, c);
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_MULTI_MOVE, comp_multi_move, c);
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_OUT, comp_mouse_out, c);
+ evas_object_color_set(c->events, 0, 0, 0, 0);
+ evas_object_smart_member_add(c->events, obj);
+ evas_object_show(c->events);
+ evas_object_lower(c->events);
+
+ wl_global_create(c->display, &wl_compositor_interface, 4, c, comp_bind);
+ wl_global_create(c->display, &wl_subcompositor_interface, 1, c, subcomp_bind);
+ wl_global_create(c->display, &wl_output_interface, 2, c, output_bind);
+ wl_global_create(c->display, &zxdg_shell_v6_interface, 1, c, shell_bind);
+ wl_global_create(c->display, &wl_data_device_manager_interface, 3, c, data_device_manager_bind);
+ wl_display_init_shm(c->display);
+
+ if (env)
+ {
+ c->parent_disp = ecore_wl2_display_connect(env);
+ if (ecore_wl2_display_sync_is_done(c->parent_disp))
+ {
+ c->client_disp = ecore_wl2_display_connect(c->env);
+ comp_seats_proxy(c);
+ comp_wayland_features_init(c);
+ }
+ }
+ else
+ {
+ c->client_disp = ecore_wl2_display_connect(c->env);
+ comp_seats_proxy(c);
+#ifdef HAVE_ECORE_X
+ if (ecore_x_display_get())
+ {
+ // if proxiedallowed
+ ecore_x_dnd_aware_set(ecore_evas_window_get(ecore_evas_ecore_evas_get(c->evas)), EINA_TRUE);
+ if (!comps) x11_init();
+ }
+#endif
+ }
+
+ c->gl = evas_gl_new(c->evas);
+ if (c->gl) comp_gl_init(c);
+ evas_event_callback_add(c->evas, EVAS_CALLBACK_RENDER_PRE, (Evas_Event_Cb)comp_render_pre, c);
+ evas_event_callback_add(c->evas, EVAS_CALLBACK_RENDER_POST, (Evas_Event_Cb)comp_render_post, c);
+ efl_event_callback_array_add(c->evas, comp_device_cbs(), c);
+
+ evas_object_event_callback_add(c->obj, EVAS_CALLBACK_FOCUS_IN, comp_focus_in, c);
+ evas_object_event_callback_add(c->obj, EVAS_CALLBACK_FOCUS_OUT, comp_focus_out, c);
+
+ if (!comps) comp_handlers_init();
+ comps = eina_list_append(comps, c);
+ free(env);
+}
+
+static void
+comp_smart_del(Evas_Object *obj)
+{
+ Comp *c = evas_object_smart_data_get(obj);
+
+ evas_object_del(c->clip);
+ evas_object_del(c->events);
+ free(c->env);
+ if (c->gl) comp_gl_shutdown(c);
+ while (c->shells)
+ {
+ Shell_Data *sd = EINA_INLIST_CONTAINER_GET(c->shells, Shell_Data);
+ wl_resource_destroy(sd->res);
+ }
+ while (c->seats)
+ {
+ Comp_Seat *s = EINA_INLIST_CONTAINER_GET(c->seats, Comp_Seat);
+ seat_destroy(s);
+ }
+ while (c->surfaces)
+ {
+ Comp_Surface *cs = EINA_INLIST_CONTAINER_GET(c->surfaces, Comp_Surface);
+ evas_object_del(cs->obj);
+ }
+ eina_hash_free(c->client_surfaces);
+ if (c->parent_disp) ecore_wl2_display_disconnect(c->parent_disp);
+ ecore_wl2_display_destroy(c->disp);
+ ecore_wl2_display_disconnect(c->client_disp);
+
+ evas_event_callback_del_full(c->evas, EVAS_CALLBACK_RENDER_PRE, (Evas_Event_Cb)comp_render_pre, c);
+ evas_event_callback_del_full(c->evas, EVAS_CALLBACK_RENDER_POST, (Evas_Event_Cb)comp_render_post, c);
+ efl_event_callback_array_del(c->evas, comp_device_cbs(), c);
+ comps = eina_list_remove(comps, c);
+ free(c);
+ if (!comps)
+ {
+ void *h;
+ EINA_LIST_FREE(handlers, h)
+ ecore_event_handler_del(h);
+#ifdef HAVE_ECORE_X
+ x11_shutdown();
+#endif
+ }
+ ecore_wl2_shutdown();
+}
+
+static void
+comp_smart_move(Evas_Object *obj, int x, int y)
+{
+ Comp_Surface *cs;
+ Comp *c = evas_object_smart_data_get(obj);
+ int dx, dy;
+ evas_object_geometry_get(obj, &dx, &dy, NULL, NULL);
+ dx = x - dx;
+ dy = y - dy;
+ evas_object_move(c->clip, x, y);
+ evas_object_move(c->events, x, y);
+ EINA_INLIST_FOREACH(c->surfaces, cs)
+ {
+ int cx, cy;
+ evas_object_geometry_get(cs->obj, &cx, &cy, NULL, NULL);
+ evas_object_move(cs->obj, cx + dx, cy + dy);
+ }
+}
+
+static void
+comp_smart_resize(Evas_Object *obj, int w, int h)
+{
+ Comp *c = evas_object_smart_data_get(obj);
+ Eina_List *l;
+ Comp_Surface *cs;
+ struct wl_resource *res;
+
+ evas_object_resize(c->clip, w, h);
+ evas_object_resize(c->events, w, h);
+ EINA_LIST_FOREACH(c->output_resources, l, res)
+ output_resize(c, res);
+ //fprintf(stderr, "COMP %dx%d\n", w, h);
+ EINA_INLIST_FOREACH(c->surfaces, cs)
+ if (cs->shell.surface && cs->role)
+ shell_surface_send_configure(cs);
+}
+
+static void
+comp_smart_show(Evas_Object *obj)
+{
+ Comp *c = evas_object_smart_data_get(obj);
+ evas_object_show(c->clip);
+}
+
+static void
+comp_smart_hide(Evas_Object *obj)
+{
+ Comp *c = evas_object_smart_data_get(obj);
+ evas_object_hide(c->clip);
+}
+
+static void
+comp_smart_color_set(Evas_Object *obj, int r, int g, int b, int a)
+{
+ Comp *c = evas_object_smart_data_get(obj);
+ evas_object_color_set(c->clip, r, g, b, a);
+}
+
+static void
+comp_smart_clip_set(Evas_Object *obj, Evas_Object *clip)
+{
+ Comp *c = evas_object_smart_data_get(obj);
+ evas_object_clip_set(c->clip, clip);
+}
+
+static void
+comp_smart_clip_unset(Evas_Object *obj)
+{
+ Comp *c = evas_object_smart_data_get(obj);
+ evas_object_clip_unset(c->clip);
+}
+
+static void
+comp_smart_member_add(Evas_Object *obj, Evas_Object *child)
+{
+ Comp *c = evas_object_smart_data_get(obj);
+ if (child != c->clip) evas_object_clip_set(child, c->clip);
+}
+
+static void
+comp_smart_member_del(Evas_Object *obj, Evas_Object *child)
+{
+ Comp *c = evas_object_smart_data_get(obj);
+ if (child != c->clip) evas_object_clip_unset(child);
+}
+
+static void
+comp_smart_init(void)
+{
+ if (comp_smart) return;
+ {
+ static const Evas_Smart_Class sc =
+ {
+ "comp",
+ EVAS_SMART_CLASS_VERSION,
+ comp_smart_add,
+ comp_smart_del,
+ comp_smart_move,
+ comp_smart_resize,
+ comp_smart_show,
+ comp_smart_hide,
+ comp_smart_color_set,
+ comp_smart_clip_set,
+ comp_smart_clip_unset,
+ NULL,
+ comp_smart_member_add,
+ comp_smart_member_del,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ comp_smart = evas_smart_class_new(&sc);
+ }
+}
+
+__attribute__ ((visibility("hidden"))) Eina_Bool
+comp_dmabuf_test(struct linux_dmabuf_buffer *dmabuf)
+{
+ Evas_Native_Surface ns;
+ Evas_Object *test;
+ int size;
+ void *data;
+ Comp *c = dmabuf->compositor;
+
+ if (c->gl)
+ {
+ Eina_Bool ret;
+ ns.type = EVAS_NATIVE_SURFACE_WL_DMABUF;
+ ns.version = EVAS_NATIVE_SURFACE_VERSION;
+ ns.data.wl_dmabuf.attr = &dmabuf->attributes;
+ ns.data.wl_dmabuf.resource = NULL;
+ test = evas_object_image_add(c->evas);
+ evas_object_image_native_surface_set(test, &ns);
+ ret = evas_object_image_load_error_get(test) == EVAS_LOAD_ERROR_NONE;
+ evas_object_del(test);
+ if (!ns.data.wl_dmabuf.attr) return EINA_FALSE;
+ return ret;
+ }
+
+ /* TODO: Software rendering for multi-plane formats */
+ if (dmabuf->attributes.n_planes != 1) return EINA_FALSE;
+ if (dmabuf->attributes.format != DRM_FORMAT_ARGB8888 &&
+ dmabuf->attributes.format != DRM_FORMAT_XRGB8888) return EINA_FALSE;
+
+ /* This is only legit for ARGB8888 */
+ size = dmabuf->attributes.height * dmabuf->attributes.stride[0];
+ data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf->attributes.fd[0], 0);
+ if (data == MAP_FAILED) return EINA_FALSE;
+ munmap(data, size);
+
+ return EINA_TRUE;
+}
+
+Evas_Object *
+efl_wl_add(Evas *e)
+{
+ comp_smart_init();
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(!ecore_wl2_init(), NULL);
+ return evas_object_smart_add(e, comp_smart);
+}
+
+Ecore_Exe *
+efl_wl_run(Evas_Object *obj, const char *cmd)
+{
+ char *env, *disp, *gl = NULL;
+ Comp *c;
+ Ecore_Exe *exe;
+
+ if (!eina_streq(evas_object_type_get(obj), "comp")) abort();
+ c = evas_object_smart_data_get(obj);
+ disp = getenv("DISPLAY");
+ if (disp) disp = strdup(disp);
+ unsetenv("DISPLAY");
+ env = getenv("WAYLAND_DISPLAY");
+ if (env) env = strdup(env);
+ setenv("WAYLAND_DISPLAY", c->env, 1);
+ if (c->gl)
+ {
+ gl = getenv("ELM_ACCEL");
+ if (gl) gl = strdup(gl);
+ setenv("ELM_ACCEL", "gl", 1);
+ }
+ exe = ecore_exe_run(cmd, c);
+ if (disp) setenv("DISPLAY", disp, 1);
+ if (env) setenv("WAYLAND_DISPLAY", env, 1);
+ else unsetenv("WAYLAND_DISPLAY");
+ if (c->gl)
+ {
+ if (gl) setenv("ELM_ACCEL", gl, 1);
+ else unsetenv("ELM_ACCEL");
+ free(gl);
+ }
+ free(env);
+ free(disp);
+ return exe;
+}
+
+Eina_Bool
+efl_wl_next(Evas_Object *obj)
+{
+ Comp *c;
+ Comp_Surface *cs;
+
+ if (!eina_streq(evas_object_type_get(obj), "comp")) abort();
+ c = evas_object_smart_data_get(obj);
+ if (c->surfaces_count < 2) return EINA_FALSE;
+ EINA_INLIST_REVERSE_FOREACH(c->surfaces, cs)
+ {
+ if (cs->shell.activated) continue;
+ if ((!cs->role) || (!cs->shell.surface) || cs->dead) continue;
+ cs->shell.activated = 1;
+ shell_surface_send_configure(cs);
+ return EINA_TRUE;
+ }
+
+ return EINA_FALSE;
+}
+
+Eina_Bool
+efl_wl_prev(Evas_Object *obj)
+{
+ Comp *c;
+ Comp_Surface *cs;
+
+ if (!eina_streq(evas_object_type_get(obj), "comp")) abort();
+ c = evas_object_smart_data_get(obj);
+ if (c->surfaces_count < 2) return EINA_FALSE;
+ EINA_INLIST_FOREACH(c->surfaces, cs)
+ {
+ if (cs->shell.activated) continue;
+ if ((!cs->role) || (!cs->shell.surface) || cs->dead) continue;
+ cs->shell.activated = 1;
+ shell_surface_send_configure(cs);
+ return EINA_TRUE;
+ }
+
+ return EINA_FALSE;
+}
+
+void
+efl_wl_rotate(Evas_Object *obj, Efl_Wl_Rotation rot, Eina_Bool rtl)
+{
+ Comp *c;
+ Eina_List *l;
+ struct wl_resource *res;
+
+ if (!eina_streq(evas_object_type_get(obj), "comp")) abort();
+ c = evas_object_smart_data_get(obj);
+ c->rtl = !!rtl;
+ c->rotation = rot;
+ EINA_LIST_FOREACH(c->output_resources, l, res)
+ output_resize(c, res);
+}
+
+void
+efl_wl_scale_set(Evas_Object *obj, double scale)
+{
+ Comp *c;
+ Eina_List *l;
+ struct wl_resource *res;
+
+ if (!eina_streq(evas_object_type_get(obj), "comp")) abort();
+ c = evas_object_smart_data_get(obj);
+ c->scale = scale;
+
+ EINA_LIST_FOREACH(c->output_resources, l, res)
+ if (wl_resource_get_version(res) >= WL_OUTPUT_SCALE_SINCE_VERSION)
+ wl_output_send_scale(res, lround(c->scale));
+}
diff --git a/src/lib/efl_wl/x11.x b/src/lib/efl_wl/x11.x
new file mode 100644
index 0000000000..f554b8eaaf
--- /dev/null
+++ b/src/lib/efl_wl/x11.x
@@ -0,0 +1,563 @@
+#define WL_TEXT_STR "text/plain;charset=utf-8"
+#define INCR_CHUNK_SIZE 1 << 17
+
+static void (*xconvertselection)(Ecore_X_Display *, Ecore_X_Atom, Ecore_X_Atom, Ecore_X_Atom, Ecore_X_Window, Ecore_X_Time);
+static Ecore_X_Atom string_atom;
+static Ecore_X_Atom comp_dnd_atom;
+static Ecore_X_Atom timestamp_atom;
+static Ecore_X_Atom incr_atom;
+static Ecore_X_Atom int_atom;
+
+static Eina_Hash *pipes;
+
+typedef struct
+{
+ Ecore_Fd_Handler *fdh;
+ Comp_Data_Device_Source *source;
+ Ecore_X_Window win;
+ Ecore_X_Atom atom;
+ Ecore_X_Atom selection;
+ Ecore_X_Atom property;
+ Eina_Binbuf *buf;
+ Eina_Bool incr : 1;
+} Pipe;
+
+static void
+_pipe_free(Pipe *p)
+{
+ close(ecore_main_fd_handler_fd_get(p->fdh));
+ ecore_main_fd_handler_del(p->fdh);
+ eina_binbuf_free(p->buf);
+ free(p);
+}
+
+static void
+_incr_update(Pipe *p, Eina_Bool success)
+{
+ ecore_x_selection_notify_send(p->win, p->selection, p->atom, (!!success) * p->property, 0);
+}
+
+static void
+_incr_upload(Pipe *p)
+{
+ size_t size;
+
+ size = eina_binbuf_length_get(p->buf);
+ size = MIN(size, INCR_CHUNK_SIZE);
+ ecore_x_window_prop_property_set(p->win, p->property, p->atom, 8, (void*)eina_binbuf_string_get(p->buf), size);
+ eina_binbuf_free(p->buf);
+ p->buf = NULL;
+}
+
+static Eina_Bool
+x11_offer_write(void *data, Ecore_Fd_Handler *fdh)
+{
+ Comp_Data_Device_Transfer *dt = data;
+ int len = -1;
+
+ if (ecore_main_fd_handler_active_get(fdh, ECORE_FD_WRITE))
+ {
+ len = write(ecore_main_fd_handler_fd_get(fdh),
+ eina_binbuf_string_get(dt->source->reader_data) + dt->offset,
+ eina_binbuf_length_get(dt->source->reader_data) - dt->offset);
+ if (len > 0) dt->offset += len;
+ }
+
+ if ((len <= 0) || (dt->offset == eina_binbuf_length_get(dt->source->reader_data)))
+ {
+ fdh_del(fdh);
+ eina_stringshare_del(dt->mime_type);
+ dt->source->transfers = eina_inlist_remove(dt->source->transfers, EINA_INLIST_GET(dt));
+
+ if (dt->source->x11_owner)
+ {
+ Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(dt->source->seat->c->evas));
+ if (dt->type == COMP_DATA_DEVICE_OFFER_TYPE_DND)
+ ecore_x_client_message32_send(dt->source->x11_owner,
+ ECORE_X_ATOM_XDND_FINISHED, ECORE_X_EVENT_MASK_NONE,
+ win, (len >= 0), action_convert(dt->source->current_dnd_action), 0, 0);
+ ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_TYPE_LIST);
+ if (dt->source == dt->source->seat->drag.source)
+ dt->source->seat->drag.source = NULL;
+ }
+
+ if ((!dt->source->transfers) && (dt->source->seat->selection_source != dt->source))
+ comp_data_device_source_reader_clear(dt->source);
+ free(dt);
+ }
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void
+x11_send_send(Comp_Data_Device_Source *source, const char* mime_type, int32_t fd, Comp_Data_Device_Offer_Type type)
+{
+ Ecore_X_Atom t, sel = ECORE_X_ATOM_SELECTION_CLIPBOARD;
+ Comp_Data_Device_Transfer *dt;
+ Ecore_Window win;
+ win = ecore_evas_window_get(ecore_evas_ecore_evas_get(source->seat->c->evas));
+ if (type == COMP_DATA_DEVICE_OFFER_TYPE_DND)
+ sel = ECORE_X_ATOM_SELECTION_XDND;
+
+ if (eina_streq(mime_type, WL_TEXT_STR))
+ t = string_atom;
+ else
+ t = ecore_x_atom_get(mime_type);
+
+ dt = calloc(1, sizeof(Comp_Data_Device_Transfer));
+ dt->type = type;
+ dt->fdh = ecore_main_fd_handler_add(fd, 0, x11_offer_write, dt, NULL, NULL);
+ dt->source = source;
+ dt->mime_type = eina_stringshare_add(ecore_x_atom_name_get(t));
+ dt->source->transfers = eina_inlist_append(dt->source->transfers, EINA_INLIST_GET(dt));
+ xconvertselection(ecore_x_display_get(), sel, t, comp_dnd_atom, win, ecore_x_current_time_get());
+}
+
+static Eina_Bool
+x11_fixes_selection_notify(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Fixes_Selection_Notify *ev)
+{
+ Ecore_X_Window win;
+ Eina_List *l;
+ Comp *c;
+ Comp_Seat *s;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ win = ecore_evas_window_get(ecore_evas_ecore_evas_get(c->evas));
+ if (ev->owner == win)
+ return ECORE_CALLBACK_RENEW;
+ c->x11_selection = 0;
+ }
+ if (ev->atom == ECORE_X_ATOM_SELECTION_CLIPBOARD)
+ {
+ EINA_LIST_FOREACH(comps, l, c)
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ s->x11_selection_owner = ev->owner;
+ if (ev->owner) s->selection_source = NULL;
+ }
+ if (ev->owner)
+ {
+ xconvertselection(ecore_x_display_get(), ECORE_X_ATOM_SELECTION_CLIPBOARD,
+ ECORE_X_ATOM_SELECTION_TARGETS, comp_dnd_atom, ev->win, 0);
+ }
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+x11_selection_notify(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Selection_Notify *ev)
+{
+ Ecore_X_Selection_Data *sd;
+ Eina_List *l, *lc = NULL;
+ Comp *c;
+ Comp_Seat *s;
+ Comp_Data_Device_Transfer *dt;
+ Eina_Binbuf *buf = NULL;
+
+ if ((ev->selection != ECORE_X_SELECTION_XDND) && (ev->selection != ECORE_X_SELECTION_CLIPBOARD))
+ {
+ EINA_LIST_FOREACH(comps, l, c)
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ ecore_evas_free(s->drag.proxy_win);
+ s->drag.proxy_win = NULL;
+ }
+ return ECORE_CALLBACK_RENEW;
+ }
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ Ecore_X_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(c->evas));
+ if (win == ev->win) lc = eina_list_append(lc, c);
+ }
+ if (ev->selection == ECORE_X_SELECTION_CLIPBOARD)
+ {
+ if (eina_streq(ev->target, "TARGETS"))
+ {
+ Ecore_X_Selection_Data_Targets *tgs = ev->data;
+ int i, j;
+ const char **types;
+
+ types = alloca((tgs->num_targets + 1) * sizeof(void*));
+ for (i = j = 0; i < tgs->num_targets; i++)
+ if (tgs->targets[i])
+ types[j++] = tgs->targets[i];
+ types[j] = NULL;
+ EINA_LIST_FREE(lc, c)
+ EINA_INLIST_FOREACH(c->seats, s)
+ s->client_selection_serial = ecore_wl2_dnd_selection_set(s->client_seat, (const char**)types);
+ return ECORE_CALLBACK_RENEW;
+ }
+ }
+ if (ev->property != comp_dnd_atom) return ECORE_CALLBACK_RENEW;
+ EINA_LIST_FREE(lc, c)
+ {
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+
+ if (s->drag.source && s->drag.source->transfers &&
+ (ev->selection == ECORE_X_SELECTION_XDND))
+ {
+ EINA_INLIST_FOREACH(s->drag.source->transfers, dt)
+ if (eina_streq(ev->target, dt->mime_type))
+ {
+ if (!s->drag.source->reader_data)
+ s->drag.source->reader_data = eina_binbuf_new();
+ buf = s->drag.source->reader_data;
+ break;
+ }
+ }
+ else if (s->selection_source && s->selection_source->transfers &&
+ (ev->selection == ECORE_X_SELECTION_CLIPBOARD))
+ {
+ EINA_INLIST_FOREACH(s->selection_source->transfers, dt)
+ if (eina_streq(ev->target, dt->mime_type))
+ {
+ if (!s->selection_source->reader_data)
+ s->selection_source->reader_data = eina_binbuf_new();
+ buf = s->selection_source->reader_data;
+ break;
+ }
+ }
+ if (buf) break;
+ }
+ if (buf) break;
+ }
+ eina_list_free(lc);
+
+ if (!buf) return ECORE_CALLBACK_RENEW;
+ sd = ev->data;
+ eina_binbuf_append_length(buf, sd->data, sd->length);
+ ecore_main_fd_handler_active_set(dt->fdh, ECORE_FD_WRITE | ECORE_FD_ERROR);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+x11_pipe_read(void *data, Ecore_Fd_Handler *fdh)
+{
+ Pipe *p = data;
+ ssize_t len;
+ unsigned char *buf;
+
+ buf = malloc(INCR_CHUNK_SIZE);
+ len = read(ecore_main_fd_handler_fd_get(fdh), (void*)buf, INCR_CHUNK_SIZE);
+ if (len < 0)
+ {
+ free(buf);
+ _incr_update(p, 0);
+ eina_hash_del_by_key(pipes, &p->win);
+ }
+ if (len == INCR_CHUNK_SIZE)
+ {
+ p->buf = eina_binbuf_manage_new(buf, len, 0);
+ if (p->incr)
+ _incr_upload(p);
+ else
+ {
+ unsigned long size = INCR_CHUNK_SIZE;
+
+ p->incr = 1;
+ ecore_x_window_prop_property_set(p->win, p->atom, incr_atom, 32, &size, 1);
+ _incr_update(p, 1);
+ }
+ ecore_main_fd_handler_active_set(p->fdh, 0);
+ return ECORE_CALLBACK_RENEW;
+ }
+ if (len)
+ p->buf = eina_binbuf_manage_new(buf, len, 0);
+ _incr_upload(p);
+ if (p->incr)
+ ecore_main_fd_handler_active_set(p->fdh, 0);
+ else
+ {
+ _incr_update(p, 1);
+ eina_hash_del_by_key(pipes, &p->win);
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+x11_selection_request(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Selection_Request *ev)
+{
+ Comp_Data_Device_Source *source;
+ Eina_List *l;
+ Comp *c;
+ const char *type;
+ Comp_Seat *s = NULL;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(c->evas));
+ if (c->x11_selection || ((ev->owner == win) && (ev->selection == ECORE_X_ATOM_SELECTION_XDND)))
+ {
+ s = EINA_INLIST_CONTAINER_GET(c->seats, Comp_Seat);
+ break;
+ }
+ }
+ if (!s) return ECORE_CALLBACK_RENEW;
+ if (s->drag.source)
+ source = s->drag.source;
+ else if (s->selection_source)
+ source = s->selection_source;
+ else
+ return ECORE_CALLBACK_RENEW;
+
+ if (ev->target == ECORE_X_ATOM_SELECTION_TARGETS)
+ {
+ Ecore_X_Atom *atoms;
+ int i = 0;
+
+ atoms = alloca((2 + eina_list_count(source->mime_types)) * sizeof(void*));
+ EINA_LIST_FOREACH(source->mime_types, l, type)
+ atoms[i++] = ecore_x_atom_get(type);
+ atoms[i++] = timestamp_atom;
+ atoms[i++] = ECORE_X_ATOM_SELECTION_TARGETS;
+ ecore_x_window_prop_property_set(ev->requestor, ev->property, ECORE_X_ATOM_ATOM, 32, atoms, i);
+ ecore_x_selection_notify_send(ev->requestor, ev->selection, ev->target, ev->property, 0);
+ }
+ else if (ev->target == timestamp_atom)
+ {
+ Ecore_X_Time timestamp;
+
+ timestamp = ecore_x_current_time_get();
+ ecore_x_window_prop_property_set(ev->requestor, ev->property, int_atom, 32, (void*)&timestamp, 1);
+ ecore_x_selection_notify_send(ev->requestor, ev->selection, ev->target, ev->property, 0);
+ }
+ else
+ {
+ const char *name;
+ Pipe *p;
+
+ name = ecore_x_atom_name_get(ev->target);
+ EINA_LIST_FOREACH(source->mime_types, l, type)
+ if (eina_streq(name, type))
+ {
+ int fds[2];
+
+ p = calloc(1, sizeof(Pipe));
+ socketpair(AF_UNIX, (SOCK_STREAM | SOCK_CLOEXEC), 0, fds);
+ fcntl(fds[0], F_SETFL, O_NONBLOCK);
+ p->fdh = ecore_main_fd_handler_add(fds[0], ECORE_FD_READ, x11_pipe_read, p, NULL, NULL);
+ p->win = ev->requestor;
+ p->source = source;
+ wl_data_source_send_send(source->res, type, fds[1]);
+ close(fds[1]);
+ p->atom = ev->target;
+ p->selection = ev->selection;
+ p->property = ev->property;
+ ecore_x_window_sniff(ev->requestor);
+ eina_hash_add(pipes, &p->win, p);
+ break;
+ }
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+x11_property(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Window_Property *ev)
+{
+ Pipe *p;
+
+ if (!ev->state) return ECORE_CALLBACK_RENEW;
+ p = eina_hash_find(pipes, &ev->win);
+ if (!p) return ECORE_CALLBACK_RENEW;
+ /* FIXME: WHO FORGOT THE FUCKING STATE FLAG???? */
+ ecore_main_fd_handler_active_set(p->fdh, ECORE_FD_READ);
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+x11_dnd_enter(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Xdnd_Enter *ev)
+{
+ Comp_Seat *s;
+ Comp *c;
+ Eina_List *l;
+ const char **types;
+ int i;
+
+ types = alloca(sizeof(void*) * (ev->num_types + 1));
+ for (i = 0; i < ev->num_types; i++)
+ types[i] = ev->types[i];
+ types[ev->num_types] = NULL;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(c->evas));
+ if (win != ev->win) continue;
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ if (s->drag.x11_owner == ev->source) continue;
+ s->drag.x11_owner = ev->source;
+ for (i = 0; i < ev->num_types; i++)
+ s->drag.x11_types = eina_list_append(s->drag.x11_types, eina_stringshare_add(ev->types[i]));
+ ecore_wl2_dnd_drag_types_set(s->client_seat, (const char**)types);
+ ecore_wl2_dnd_set_actions(s->client_seat);
+ }
+ }
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+x11_dnd_leave(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Xdnd_Leave *ev)
+{
+ Comp_Seat *s;
+ Comp *c;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(c->evas));
+ if (win != ev->win) continue;
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ if (s->drag.x11_owner == win) continue; //self drag
+ if (s->drag.source)
+ wl_data_source_send_cancelled(s->drag.source->res);
+ if (s->client_seat)
+ ecore_wl2_dnd_drag_end(s->client_seat);
+ s->drag.source = NULL;
+ s->drag.res = NULL;
+ s->drag.enter = NULL;
+ s->drag.x11_owner = 0;
+ }
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+x11_dnd_position(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Xdnd_Position *ev)
+{
+ Comp *c;
+ Eina_List *l;
+ Comp_Seat *s;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ int x, y, cx, cy, cw, ch;
+ Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(c->evas));
+ if (win != ev->win) continue;
+ ecore_evas_geometry_get(ecore_evas_ecore_evas_get(c->evas), &x, &y, NULL, NULL);
+ evas_object_geometry_get(c->obj, &cx, &cy, &cw, &ch);
+ if (!COORDS_INSIDE(ev->position.x, ev->position.y, x + cx, y + cy, cw, ch)) continue;
+ EINA_INLIST_FOREACH(c->seats, s)
+ dnd_motion(s, ev->position.x - x - cx, ev->position.y - y - cy);
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+x11_dnd_drop(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Xdnd_Drop *ev)
+{
+ Comp *c;
+ Eina_List *l;
+ Comp_Seat *s;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ int x, y, cx, cy, cw, ch;
+ Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(c->evas));
+ if (win != ev->win) continue;
+ ecore_evas_geometry_get(ecore_evas_ecore_evas_get(c->evas), &x, &y, NULL, NULL);
+ evas_object_geometry_get(c->obj, &cx, &cy, &cw, &ch);
+ if (!COORDS_INSIDE(ev->position.x, ev->position.y, x + cx, y + cy, cw, ch)) continue;
+ EINA_INLIST_FOREACH(c->seats, s)
+ drag_grab_button(s, 0, s->drag.id, WL_POINTER_BUTTON_STATE_RELEASED);
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+x11_dnd_finished(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Xdnd_Finished *ev)
+{
+ Comp *c;
+ Eina_List *l;
+ Comp_Seat *s;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(c->evas));
+ if (win != ev->win) continue;
+ EINA_INLIST_FOREACH(c->seats, s)
+ {
+ Comp_Data_Device_Source *source = s->drag.source;
+ if (s->drag.x11_owner != win) continue;
+ if (s->drag.source) s->drag.source->accepted = ev->completed;
+ drag_grab_button(s, 0, s->drag.id, WL_POINTER_BUTTON_STATE_RELEASED);
+ data_source_notify_finish(source);
+ }
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+x11_dnd_mouse_up(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Mouse_Button *ev)
+{
+ Comp *c;
+ Eina_List *l;
+ Comp_Seat *s;
+
+ EINA_LIST_FOREACH(comps, l, c)
+ {
+ Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(c->evas));
+ if (win != ev->event_window) continue;
+ EINA_INLIST_FOREACH(c->seats, s)
+ if (s->drag.id == ev->buttons)
+ {
+ ecore_x_dnd_self_drop();
+ ecore_x_pointer_ungrab();
+ ecore_evas_free(s->drag.proxy_win);
+ }
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void
+x11_dnd_move(void *data, Ecore_X_Xdnd_Position *pos)
+{
+ evas_object_move(data, pos->position.x, pos->position.y);
+}
+
+static void
+x11_init(void)
+{
+ Ecore_Event_Handler *h;
+
+ ecore_x_fixes_selection_notification_request(ecore_x_atom_get("CLIPBOARD"));
+ ecore_x_fixes_selection_notification_request(ECORE_X_ATOM_SELECTION_XDND);
+ h = ecore_event_handler_add(ECORE_X_EVENT_FIXES_SELECTION_NOTIFY, (Ecore_Event_Handler_Cb)x11_fixes_selection_notify, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_X_EVENT_SELECTION_NOTIFY, (Ecore_Event_Handler_Cb)x11_selection_notify, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_X_EVENT_SELECTION_REQUEST, (Ecore_Event_Handler_Cb)x11_selection_request, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_X_EVENT_WINDOW_PROPERTY, (Ecore_Event_Handler_Cb)x11_property, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_X_EVENT_XDND_ENTER, (Ecore_Event_Handler_Cb)x11_dnd_enter, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_X_EVENT_XDND_LEAVE, (Ecore_Event_Handler_Cb)x11_dnd_leave, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_X_EVENT_XDND_POSITION, (Ecore_Event_Handler_Cb)x11_dnd_position, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_X_EVENT_XDND_DROP, (Ecore_Event_Handler_Cb)x11_dnd_drop, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_X_EVENT_XDND_FINISHED, (Ecore_Event_Handler_Cb)x11_dnd_finished, NULL);
+ handlers = eina_list_append(handlers, h);
+ h = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP, (Ecore_Event_Handler_Cb)x11_dnd_mouse_up, NULL);
+ handlers = eina_list_append(handlers, h);
+ xconvertselection = dlsym(NULL, "XConvertSelection");
+ string_atom = ecore_x_atom_get("UTF8_STRING");
+ timestamp_atom = ecore_x_atom_get("TIMESTAMP");
+ int_atom = ecore_x_atom_get("INTEGER");
+ incr_atom = ecore_x_atom_get("TIMESTAMP");
+ comp_dnd_atom = ecore_x_atom_get("SIRCMPWIDG_ATOM");
+ pipes = eina_hash_int32_new((Eina_Free_Cb)_pipe_free);
+}
+
+static void
+x11_shutdown(void)
+{
+ eina_hash_free(pipes);
+ pipes = NULL;
+}