From c2fde93c9ef1108c0809a538cf2ec482ed8369a9 Mon Sep 17 00:00:00 2001 From: Mike Blumenkrantz Date: Fri, 30 Jun 2017 14:59:21 -0400 Subject: [PATCH] efl_wl: a multiseat wayland compositor in an evas smart object build when wayland support is enabled and provide two test/demo cases beta api @feature Reviewed-By: Cedric BAIL --- configure.ac | 38 + src/Makefile.am | 1 + src/Makefile_efl_wl.am | 50 + src/bin/efl_wl/efl_wl_test.c | 65 + src/bin/efl_wl/efl_wl_test_stack.c | 87 + src/lib/efl_wl/Efl_Wl.h | 111 + src/lib/efl_wl/copiedfromweston.x | 358 ++ src/lib/efl_wl/dmabuf.c | 513 +++ src/lib/efl_wl/dmabuf.h | 92 + src/lib/efl_wl/efl_wl.c | 5240 ++++++++++++++++++++++++++++ src/lib/efl_wl/x11.x | 563 +++ 11 files changed, 7118 insertions(+) create mode 100644 src/Makefile_efl_wl.am create mode 100644 src/bin/efl_wl/efl_wl_test.c create mode 100644 src/bin/efl_wl/efl_wl_test_stack.c create mode 100644 src/lib/efl_wl/Efl_Wl.h create mode 100644 src/lib/efl_wl/copiedfromweston.x create mode 100644 src/lib/efl_wl/dmabuf.c create mode 100644 src/lib/efl_wl/dmabuf.h create mode 100644 src/lib/efl_wl/efl_wl.c create mode 100644 src/lib/efl_wl/x11.x 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..2de79e2e96 --- /dev/null +++ b/src/lib/efl_wl/Efl_Wl.h @@ -0,0 +1,111 @@ +#ifdef EFL_BETA_API_SUPPORT + +#ifndef EFL_WL_H +# define EFL_WL_H +#include +#include + +#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 */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif + +/** + * @defgroup Efl_Wl_Group EFL Wayland + * + * A multiseat Wayland compositor in an Evas object. + * All toplevel windows will be sized to the size of the compositor object. + * @since 1.20 + * @{ + */ + +/** + * @typedef Efl_Wl_Rotation + * The rotation to apply to the compositor's internal wl_output + */ +typedef enum +{ + EFL_WL_ROTATION_0, + EFL_WL_ROTATION_90, + EFL_WL_ROTATION_180, + EFL_WL_ROTATION_270 +} Efl_Wl_Rotation; + +/** + * Add a compositor widget to the given canvas. + * + * @param e The canvas + * @return The compositor object, @c NULL on failure + */ +EAPI Evas_Object *efl_wl_add(Evas *e); + +/** + * Run a command in the compositor widget. + * + * @note If GL is available, the ELM_ACCEL environment variable will be + * set to "gl" while executing the command. + * + * @param obj The compositor widget + * @param cmd The command to run + * @return The Ecore_Exe from the executed process, @c NULL on failure + */ +EAPI Ecore_Exe *efl_wl_run(Evas_Object *obj, const char *cmd); + +/** + * Put the bottom-most toplevel window on top and apply focus to it + * + * @param obj The compositor widget + * @return EINA_TRUE if the window stacking was changed + */ +EAPI Eina_Bool efl_wl_next(Evas_Object *obj); + +/** + * Put the second top-most toplevel window on top and apply focus to it + * + * @param obj The compositor widget + * @return EINA_TRUE if the window stacking was changed + */ +EAPI Eina_Bool efl_wl_prev(Evas_Object *obj); + +/** + * Set rotation and flip for the compositor's output + * + * @param obj The compositor widget + * @param rot The rotation to apply + * @param rtl If set, the output will apply a flip around the Y axis + * @note rtl is equivalent to WL_OUTPUT_TRANSFORM_FLIPPED and rotations are applied + * on top + */ +EAPI void efl_wl_rotate(Evas_Object *obj, Efl_Wl_Rotation rot, Eina_Bool rtl); + +/** + * Set the scale factor for the compositor's output + * + * @param obj The compositor widget + * @param scale The scale factor to set + */ +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 + +#include + +#include "linux-dmabuf-unstable-v1-server-protocol.h" +#include +#include +#include +#include "dmabuf.h" +#include + +__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 + +#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..d1760a39e5 --- /dev/null +++ b/src/lib/efl_wl/efl_wl.c @@ -0,0 +1,5240 @@ +#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 +#include +#include +#include +#include + +#include +#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 +#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); + } +} + +# ifdef __GNUC__ +# if __GNUC__ >= 4 +__attribute__ ((visibility("hidden"))) +# endif +#endif +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*)×tamp, 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; +}