From 5497fadce4adb2d14aa0680fd9cb50341b17b6d0 Mon Sep 17 00:00:00 2001 From: Mike Blumenkrantz Date: Fri, 11 Nov 2016 12:28:49 -0500 Subject: [PATCH] xdg6 support --- src/bin/e_comp_canvas.c | 28 +- src/bin/e_comp_wl.c | 142 ++- src/bin/e_comp_wl.h | 10 + src/modules/Makefile_wl_desktop_shell.mk | 6 +- src/modules/wl_desktop_shell/e_mod_main.c | 26 +- src/modules/wl_desktop_shell/e_mod_main.h | 6 +- src/modules/wl_desktop_shell/xdg5.c | 4 +- src/modules/wl_desktop_shell/xdg6.c | 1348 +++++++++++++++++++++ 8 files changed, 1553 insertions(+), 17 deletions(-) create mode 100644 src/modules/wl_desktop_shell/xdg6.c diff --git a/src/bin/e_comp_canvas.c b/src/bin/e_comp_canvas.c index deab362f0..be5f5816a 100644 --- a/src/bin/e_comp_canvas.c +++ b/src/bin/e_comp_canvas.c @@ -170,7 +170,14 @@ _e_comp_cb_mouse_up(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Mouse_Bu { if ((e_comp->comp_type == E_PIXMAP_TYPE_X) && (ev->event_window != e_comp->root)) return ECORE_CALLBACK_PASS_ON; - return !e_bindings_mouse_down_ecore_event_handle(E_BINDING_CONTEXT_MANAGER, E_OBJECT(e_comp), ev); + if (e_bindings_mouse_down_ecore_event_handle(E_BINDING_CONTEXT_MANAGER, E_OBJECT(e_comp), ev)) + return ECORE_CALLBACK_DONE; + +#ifdef HAVE_WAYLAND + return e_comp_wl_grab_client_mouse_button(ev); +#else + return ECORE_CALLBACK_RENEW; +#endif } static Eina_Bool @@ -178,7 +185,13 @@ _e_comp_cb_mouse_down(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Mouse_ { if ((e_comp->comp_type == E_PIXMAP_TYPE_X) && (ev->event_window != e_comp->root)) return ECORE_CALLBACK_PASS_ON; - return !e_bindings_mouse_down_ecore_event_handle(E_BINDING_CONTEXT_MANAGER, E_OBJECT(e_comp), ev); + if (e_bindings_mouse_down_ecore_event_handle(E_BINDING_CONTEXT_MANAGER, E_OBJECT(e_comp), ev)) + return ECORE_CALLBACK_DONE; +#ifdef HAVE_WAYLAND + return e_comp_wl_grab_client_mouse_button(ev); +#else + return ECORE_CALLBACK_RENEW; +#endif } static Eina_Bool @@ -189,6 +202,14 @@ _e_comp_cb_mouse_wheel(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Mouse return !e_bindings_wheel_ecore_event_handle(E_BINDING_CONTEXT_MANAGER, E_OBJECT(e_comp), ev); } +#ifdef HAVE_WAYLAND +static Eina_Bool +_e_comp_cb_mouse_move(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Mouse_Move *ev) +{ + return e_comp_wl_grab_client_mouse_move(ev); +} +#endif + //////////////////////////////////// static Eina_Bool @@ -794,6 +815,9 @@ e_comp_canvas_intercept(void) E_LIST_HANDLER_APPEND(handlers, ECORE_EVENT_MOUSE_BUTTON_DOWN, _e_comp_cb_mouse_down, NULL); E_LIST_HANDLER_APPEND(handlers, ECORE_EVENT_MOUSE_BUTTON_UP, _e_comp_cb_mouse_up, NULL); E_LIST_HANDLER_APPEND(handlers, ECORE_EVENT_MOUSE_WHEEL, _e_comp_cb_mouse_wheel, NULL); +#ifdef HAVE_WAYLAND + E_LIST_HANDLER_APPEND(handlers, ECORE_EVENT_MOUSE_MOVE, _e_comp_cb_mouse_move, NULL); +#endif E_LIST_HANDLER_APPEND(handlers, ECORE_EVENT_KEY_DOWN, _e_comp_cb_key_down, NULL); E_LIST_HANDLER_APPEND(handlers, ECORE_EVENT_KEY_UP, _e_comp_cb_key_up, NULL); } diff --git a/src/bin/e_comp_wl.c b/src/bin/e_comp_wl.c index b31dde91d..e9d000b8f 100644 --- a/src/bin/e_comp_wl.c +++ b/src/bin/e_comp_wl.c @@ -42,6 +42,9 @@ static double _last_event_time = 0.0; static int64_t surface_id = 0; +static Eina_List *grab_clients; +static Eina_List *grab_cbs; + /* local functions */ static Eina_Bool @@ -116,13 +119,16 @@ _e_comp_wl_surface_outputs_update(E_Client *ec) static void _e_comp_wl_configure_send(E_Client *ec, Eina_Bool edges) { - int w, h; + int w = 0, h = 0; if (e_object_is_del(E_OBJECT(ec))) return; - if (e_comp_object_frame_exists(ec->frame)) - w = ec->client.w, h = ec->client.h; - else - w = ec->w, h = ec->h; + if (e_pixmap_usable_get(ec->pixmap)) + { + if (e_comp_object_frame_exists(ec->frame)) + w = ec->client.w, h = ec->client.h; + else + w = ec->w, h = ec->h; + } ec->comp_data->shell.configure_send(ec->comp_data->shell.surface, edges * e_comp_wl->resize.edges, w, h); @@ -1334,6 +1340,26 @@ _e_comp_wl_surface_state_commit(E_Client *ec, E_Comp_Wl_Surface_State *state) if (ec->comp_data->shell.surface) { + if (ec->comp_data->shell.set.min_size.w) + ec->icccm.min_w = ec->comp_data->shell.set.min_size.w; + ec->comp_data->shell.set.min_size.w = 0; + if (ec->comp_data->shell.set.min_size.h) + ec->icccm.min_h = ec->comp_data->shell.set.min_size.h; + ec->comp_data->shell.set.min_size.h = 0; + if (ec->comp_data->shell.set.max_size.w) + ec->icccm.max_w = ec->comp_data->shell.set.max_size.w; + ec->comp_data->shell.set.max_size.w = 0; + if (ec->comp_data->shell.set.max_size.h) + ec->icccm.max_h = ec->comp_data->shell.set.max_size.h; + ec->comp_data->shell.set.max_size.h = 0; + if (ec->icccm.min_w && ec->icccm.max_w && (ec->icccm.min_w > ec->icccm.max_w)) + wl_resource_post_error(ec->comp_data->shell.surface, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "min surface width cannot be larger than max surface width"); + if (ec->icccm.min_h && ec->icccm.max_h && (ec->icccm.min_h > ec->icccm.max_h)) + wl_resource_post_error(ec->comp_data->shell.surface, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "min surface height cannot be larger than max surface height"); if (ec->comp_data->shell.set.fullscreen && (!ec->fullscreen)) { e_client_fullscreen(ec, E_FULLSCREEN_RESIZE); @@ -1473,6 +1499,8 @@ _e_comp_wl_surface_state_commit(E_Client *ec, E_Comp_Wl_Surface_State *state) ec->want_focus |= ec->icccm.accepts_focus && (!ec->override); } } + else if (first && ec->comp_data->shell.surface) + _e_comp_wl_configure_send(ec, 0); state->sx = 0; state->sy = 0; @@ -1839,6 +1867,11 @@ _e_comp_wl_compositor_cb_surface_create(struct wl_client *client, struct wl_reso ec->client.w = ec->client.h = 1; ec->comp_data->surface = res; ec->netwm.pid = pid; + if (!ec->internal) + { + ec->icccm.min_w = ec->icccm.min_h = + ec->icccm.max_w = ec->icccm.max_h = 0; + } if (client != e_comp_wl->xwl_client) ec->internal = pid == getpid(); @@ -3342,3 +3375,102 @@ e_comp_wl_xwayland_client_queue(E_Client *ec) { e_comp_wl->xwl_pending = eina_list_append(e_comp_wl->xwl_pending, ec); } + +E_API void +e_comp_wl_grab_client_add(E_Client *ec, E_Comp_Wl_Grab_End_Cb cb) +{ + E_Client *gec, *parent = e_client_util_top_parent_get(ec); + E_Comp_Wl_Grab_End_Cb grabcb; + + if (grab_clients && (parent != e_client_util_top_parent_get(eina_list_data_get(grab_clients)))) + { + /* dismiss grabs in order when grabbing from new toplevel */ + EINA_LIST_FREE(grab_clients, gec) + { + grabcb = eina_list_data_get(grab_cbs); + if (grabcb) grabcb(gec); + grab_cbs = eina_list_remove_list(grab_cbs, grab_cbs); + } + } + grab_clients = eina_list_prepend(grab_clients, ec); + grab_cbs = eina_list_prepend(grab_cbs, cb); + ec->comp_data->grab = 1; + if (eina_list_count(grab_clients) == 1) e_bindings_disabled_set(1); +} + +E_API void +e_comp_wl_grab_client_del(E_Client *ec, Eina_Bool dismiss) +{ + Eina_List *l, *ll; + E_Client *gec; + int n = -1; + E_Comp_Wl_Grab_End_Cb cb; + + EINA_LIST_FOREACH(grab_clients, l, gec) + { + n++; + if (gec != ec) continue; + ll = eina_list_nth_list(grab_cbs, n); + cb = eina_list_data_get(ll); + if (dismiss && cb) cb(gec); + grab_cbs = eina_list_remove_list(grab_cbs, ll); + grab_clients = eina_list_remove_list(grab_clients, l); + break; + } + if (!grab_clients) + e_bindings_disabled_set(0); +} + +E_API Eina_Bool +e_comp_wl_client_is_grabbed(const E_Client *ec) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(ec, EINA_FALSE); + if (e_object_is_del(E_OBJECT(ec))) return EINA_FALSE; + return ec->comp_data->grab && evas_object_visible_get(ec->frame); +} + +static Eina_Bool +_check_grab_coords(E_Client *ec, int x, int y) +{ + if (e_comp_object_coords_inside_input_area(ec->frame, x, y)) return EINA_TRUE; + while (ec->parent) + { + ec = ec->parent; + if (e_comp_object_coords_inside_input_area(ec->frame, x, y)) return EINA_TRUE; + } + return EINA_FALSE; +} + +E_API Eina_Bool +e_comp_wl_grab_client_mouse_move(const Ecore_Event_Mouse_Move *ev) +{ + E_Client *ec; + if (e_comp->comp_type != E_PIXMAP_TYPE_WL) return ECORE_CALLBACK_RENEW; + ec = e_client_focused_get(); + if (!ec) return ECORE_CALLBACK_RENEW; + if (!e_client_util_is_popup(ec)) return ECORE_CALLBACK_RENEW; + if (!e_comp_wl_client_is_grabbed(ec)) return ECORE_CALLBACK_RENEW; + /* reject mouse moves from outside the popup */ + if (_check_grab_coords(ec, ev->x, ev->y)) return ECORE_CALLBACK_RENEW; + /* manually move the pointer since we're about to block the event globally */ + evas_object_move(e_comp->pointer->o_ptr, ev->x, ev->y); + return ECORE_CALLBACK_DONE; +} + +E_API Eina_Bool +e_comp_wl_grab_client_mouse_button(const Ecore_Event_Mouse_Button *ev) +{ + E_Client *ec; + + if (e_comp->comp_type != E_PIXMAP_TYPE_WL) return ECORE_CALLBACK_RENEW; + ec = e_client_focused_get(); + if (!ec) return ECORE_CALLBACK_RENEW; + if (!e_client_util_is_popup(ec)) return ECORE_CALLBACK_RENEW; + if (!e_comp_wl_client_is_grabbed(ec)) return ECORE_CALLBACK_RENEW; + /* reject mouse moves from outside the popup */ + if (_check_grab_coords(ec, ev->x, ev->y)) return ECORE_CALLBACK_RENEW; + e_comp_wl_grab_client_del(ec, 1); + while (grab_clients) + e_comp_wl_grab_client_del(eina_list_last_data_get(grab_clients), 1); + return ECORE_CALLBACK_DONE; +} diff --git a/src/bin/e_comp_wl.h b/src/bin/e_comp_wl.h index 2ee3aedca..48ff97ad6 100644 --- a/src/bin/e_comp_wl.h +++ b/src/bin/e_comp_wl.h @@ -49,6 +49,7 @@ typedef struct _E_Comp_Wl_Client_Data E_Comp_Wl_Client_Data; typedef struct _E_Comp_Wl_Data E_Comp_Wl_Data; typedef struct _E_Comp_Wl_Output E_Comp_Wl_Output; typedef struct E_Shell_Data E_Shell_Data; +typedef void (*E_Comp_Wl_Grab_End_Cb)(E_Client*); struct _E_Comp_Wl_Buffer { @@ -287,6 +288,8 @@ struct _E_Comp_Wl_Client_Data E_Shell_Data *data; struct { + Evas_Coord_Size min_size; + Evas_Coord_Size max_size; Eina_Bool fullscreen : 1; Eina_Bool unfullscreen : 1; Eina_Bool maximize : 1; @@ -329,6 +332,7 @@ struct _E_Comp_Wl_Client_Data Eina_Bool maximizing : 1; Eina_Bool in_commit : 1; Eina_Bool is_xdg_surface : 1; + Eina_Bool grab; }; struct _E_Comp_Wl_Output @@ -369,6 +373,12 @@ E_API Eina_Bool e_comp_wl_evas_handle_mouse_button(E_Client *ec, uint32_t timest E_API extern int E_EVENT_WAYLAND_GLOBAL_ADD; +E_API void e_comp_wl_grab_client_add(E_Client *ec, E_Comp_Wl_Grab_End_Cb cb); +E_API void e_comp_wl_grab_client_del(E_Client *ec, Eina_Bool dismiss); +E_API Eina_Bool e_comp_wl_client_is_grabbed(const E_Client *ec); +E_API Eina_Bool e_comp_wl_grab_client_mouse_move(const Ecore_Event_Mouse_Move *ev); +E_API Eina_Bool e_comp_wl_grab_client_mouse_button(const Ecore_Event_Mouse_Button *ev); + # ifndef HAVE_WAYLAND_ONLY EINTERN void e_comp_wl_xwayland_client_queue(E_Client *ec); static inline E_Comp_X_Client_Data * diff --git a/src/modules/Makefile_wl_desktop_shell.mk b/src/modules/Makefile_wl_desktop_shell.mk index 399579913..6b5b74e87 100644 --- a/src/modules/Makefile_wl_desktop_shell.mk +++ b/src/modules/Makefile_wl_desktop_shell.mk @@ -13,6 +13,8 @@ wl_desktop_shellpkg_LTLIBRARIES = src/modules/wl_desktop_shell/module.la wl_desktop_shell_wayland_sources = \ src/modules/wl_desktop_shell/xdg-shell-unstable-v5-protocol.c \ src/modules/wl_desktop_shell/xdg-shell-unstable-v5-server-protocol.h \ + src/modules/wl_desktop_shell/xdg-shell-unstable-v6-protocol.c \ + src/modules/wl_desktop_shell/xdg-shell-unstable-v6-server-protocol.h \ src/modules/wl_desktop_shell/input-method-unstable-v1-protocol.c \ src/modules/wl_desktop_shell/input-method-unstable-v1-server-protocol.h @@ -27,7 +29,8 @@ src/modules/wl_desktop_shell/e_mod_main.c \ src/modules/wl_desktop_shell/e_mod_main.h \ src/modules/wl_desktop_shell/e_mod_input_panel.c \ src/modules/wl_desktop_shell/wl_shell.c \ -src/modules/wl_desktop_shell/xdg5.c +src/modules/wl_desktop_shell/xdg5.c \ +src/modules/wl_desktop_shell/xdg6.c nodist_src_modules_wl_desktop_shell_module_la_SOURCES = \ @@ -38,6 +41,7 @@ MAINTAINERCLEANFILES += \ src/modules/wl_desktop_shell/e_mod_main.c: \ src/modules/wl_desktop_shell/xdg-shell-unstable-v5-server-protocol.h \ + src/modules/wl_desktop_shell/xdg-shell-unstable-v6-server-protocol.h \ src/modules/wl_desktop_shell/input-method-unstable-v1-server-protocol.h PHONIES += wl_desktop_shell install-wl_desktop_shell diff --git a/src/modules/wl_desktop_shell/e_mod_main.c b/src/modules/wl_desktop_shell/e_mod_main.c index 8d7a897ee..c818b9f04 100644 --- a/src/modules/wl_desktop_shell/e_mod_main.c +++ b/src/modules/wl_desktop_shell/e_mod_main.c @@ -16,15 +16,27 @@ e_shell_surface_destroy(struct wl_resource *resource) /* get the client for this resource */ ec = wl_resource_get_user_data(resource); if (!ec) return; - if (!e_object_unref(E_OBJECT(ec))) return; if (e_object_is_del(E_OBJECT(ec))) return; + if (ec->comp_data->grab) + { + e_comp_wl_grab_client_del(ec, 0); + ec->comp_data->grab = 0; + } + shd = ec->comp_data->shell.data; if (shd) { E_FREE_LIST(shd->pending, free); - E_FREE(ec->comp_data->shell.data); + if ((resource == ec->comp_data->shell.surface) || (resource == shd->surface)) + { + if (ec->comp_data->shell.surface == resource) + ec->comp_data->shell.surface = NULL; + else + shd->surface = NULL; + E_FREE(ec->comp_data->shell.data); + } } if (ec->comp_data->mapped) @@ -38,8 +50,8 @@ e_shell_surface_destroy(struct wl_resource *resource) ec->parent->transients = eina_list_remove(ec->parent->transients, ec); } - /* wl_resource_destroy(ec->comp_data->shell.surface); */ - ec->comp_data->shell.surface = NULL; + + e_object_unref(E_OBJECT(ec)); } EINTERN void @@ -140,6 +152,8 @@ E_API E_Module_Api e_modapi = { E_MODULE_API_VERSION, "Wl_Desktop_Shell" }; E_API void * e_modapi_init(E_Module *m) { + Eina_Bool have_shell; + /* try to create global shell interface */ if (!wl_global_create(e_comp_wl->wl.disp, &wl_shell_interface, 1, NULL, wl_shell_cb_bind)) @@ -148,7 +162,9 @@ e_modapi_init(E_Module *m) return NULL; } - if (!e_xdg_shell_init()) return NULL; + have_shell = e_xdg_shell_v5_init(); + have_shell &= e_xdg_shell_v6_init(); + if (!have_shell) return NULL; #ifdef HAVE_WL_TEXT_INPUT if (!e_input_panel_init()) diff --git a/src/modules/wl_desktop_shell/e_mod_main.h b/src/modules/wl_desktop_shell/e_mod_main.h index 24a7fc85e..77c404c50 100644 --- a/src/modules/wl_desktop_shell/e_mod_main.h +++ b/src/modules/wl_desktop_shell/e_mod_main.h @@ -12,7 +12,8 @@ EINTERN void e_shell_surface_cb_destroy(struct wl_resource *resource); EINTERN void e_shell_surface_parent_set(E_Client *ec, struct wl_resource *parent_resource); EINTERN void e_shell_surface_mouse_down_helper(E_Client *ec, E_Binding_Event_Mouse_Button *ev, Eina_Bool move); -EINTERN Eina_Bool e_xdg_shell_init(void); +EINTERN Eina_Bool e_xdg_shell_v5_init(void); +EINTERN Eina_Bool e_xdg_shell_v6_init(void); EINTERN void wl_shell_cb_bind(struct wl_client *client, void *data EINA_UNUSED, uint32_t version, uint32_t id); struct E_Shell_Data @@ -21,10 +22,11 @@ struct E_Shell_Data int32_t width; int32_t height; Eina_List *pending; + struct wl_resource *surface; + void *shell; Eina_Bool fullscreen : 1; Eina_Bool maximized : 1; Eina_Bool activated : 1; }; - #endif diff --git a/src/modules/wl_desktop_shell/xdg5.c b/src/modules/wl_desktop_shell/xdg5.c index 2af720648..577393b37 100644 --- a/src/modules/wl_desktop_shell/xdg5.c +++ b/src/modules/wl_desktop_shell/xdg5.c @@ -860,7 +860,7 @@ _e_xdg_shell_cb_dispatch(const void *implementation EINA_UNUSED, void *target, u return 1; } -EINTERN void +static void _e_xdg_shell_cb_bind(struct wl_client *client, void *data EINA_UNUSED, uint32_t version, uint32_t id) { struct wl_resource *res; @@ -877,7 +877,7 @@ _e_xdg_shell_cb_bind(struct wl_client *client, void *data EINA_UNUSED, uint32_t } EINTERN Eina_Bool -e_xdg_shell_init(void) +e_xdg_shell_v5_init(void) { /* try to create global xdg_shell interface */ if (!wl_global_create(e_comp_wl->wl.disp, &xdg_shell_interface, 1, diff --git a/src/modules/wl_desktop_shell/xdg6.c b/src/modules/wl_desktop_shell/xdg6.c new file mode 100644 index 000000000..274b453ac --- /dev/null +++ b/src/modules/wl_desktop_shell/xdg6.c @@ -0,0 +1,1348 @@ +#define E_COMP_WL +#include "e.h" +#include "e_mod_main.h" + +#include "xdg-shell-unstable-v6-server-protocol.h" + +#define XDG_SERVER_VERSION 6 + +typedef enum +{ + STATE_MAXIMIZED = (1 << 0), + STATE_UNMAXIMIZED = (1 << 1), + STATE_FULLSCREEN = (1 << 2), + STATE_UNFULLSCREEN = (1 << 3), +} State; + +typedef struct Pending_State +{ + State state; + uint32_t serial; +} Pending_State; + +typedef struct v6_Shell_Data +{ + Eina_List *surfaces; + Eina_List *positioners; +} v6_Shell_Data; + +typedef struct Positioner +{ + v6_Shell_Data *v; + 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; +} Positioner; + + +static void +_e_xdg_shell_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + v6_Shell_Data *v = wl_resource_get_user_data(resource); + if (v->surfaces) + wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES, "shell destroyed before surfaces"); + wl_resource_destroy(resource); +} + +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 +_e_xdg_positioner_set_size(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, int32_t w, int32_t h) +{ + 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 +_e_xdg_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) +{ + 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 +_e_xdg_positioner_set_anchor(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, enum zxdg_positioner_v6_anchor anchor) +{ + 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 +_e_xdg_positioner_set_gravity(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, enum zxdg_positioner_v6_gravity gravity) +{ + 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 +_e_xdg_positioner_set_constraint_adjustment(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, enum zxdg_positioner_v6_constraint_adjustment constraint_adjustment) +{ + Positioner *p = wl_resource_get_user_data(resource); + + p->constrain = constraint_adjustment; +} + +static void +_e_xdg_positioner_set_offset(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y) +{ + Positioner *p = wl_resource_get_user_data(resource); + + p->offset.x = x; + p->offset.y = y; +} + +static void +_e_xdg_positioner_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct zxdg_positioner_v6_interface _e_xdg_positioner_interface = +{ + .destroy = _e_xdg_positioner_destroy, + .set_size = _e_xdg_positioner_set_size, + .set_anchor_rect = _e_xdg_positioner_set_anchor_rect, + .set_anchor = _e_xdg_positioner_set_anchor, + .set_gravity = _e_xdg_positioner_set_gravity, + .set_constraint_adjustment = _e_xdg_positioner_set_constraint_adjustment, + .set_offset = _e_xdg_positioner_set_offset, +}; + +static void +_e_xdg_shell_positioner_destroy(struct wl_resource *resource) +{ + Positioner *p; + + p = wl_resource_get_user_data(resource); + if (p->v) p->v->positioners = eina_list_remove(p->v->positioners, p); + free(p); +} + +static void +_e_xdg_shell_cb_positioner_create(struct wl_client *client, struct wl_resource *resource, uint32_t id) +{ + struct wl_resource *res; + v6_Shell_Data *v; + Positioner *p; + + v = wl_resource_get_user_data(resource); + res = wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id); + p = E_NEW(Positioner, 1); + p->v = v; + wl_resource_set_implementation(res, &_e_xdg_positioner_interface, p, _e_xdg_shell_positioner_destroy); + wl_resource_set_user_data(res, p); +} + +static void +_e_xdg_shell_surface_configure(struct wl_resource *resource, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) +{ + E_Client *ec; + + /* DBG("XDG_SHELL: Surface Configure: %d \t%d %d %d %d", */ + /* wl_resource_get_id(resource), x, y, w, h); */ + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (!e_object_is_del(E_OBJECT(ec))) + e_client_util_move_resize_without_frame(ec, x, y, w, h); +} + +static void +_e_xdg_shell_surface_ping(struct wl_resource *resource) +{ + E_Client *ec; + uint32_t serial; + struct wl_client *client; + struct wl_resource *xdg_shell; + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + client = wl_resource_get_client(resource); + xdg_shell = eina_hash_find(xdg_shell_resources, &client); + + if (!xdg_shell) return; + serial = wl_display_next_serial(e_comp_wl->wl.disp); + zxdg_shell_v6_send_ping(xdg_shell, serial); +} + +static void +_e_xdg_shell_surface_map(struct wl_resource *resource) +{ + E_Client *ec; + + /* DBG("XDG_SHELL: Map Surface: %d", wl_resource_get_id(resource)); */ + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + if ((!ec->comp_data->mapped) && (e_pixmap_usable_get(ec->pixmap))) + { + /* map this surface if needed */ + ec->visible = EINA_TRUE; + evas_object_show(ec->frame); + ec->comp_data->mapped = EINA_TRUE; + + /* FIXME: sometimes popup surfaces Do Not raise above their + * respective parents... */ + /* if (e_client_util_is_popup(ec)) */ + /* e_client_raise_latest_set(ec); */ + } +} + +static void +_e_xdg_shell_surface_unmap(struct wl_resource *resource) +{ + E_Client *ec; + + /* DBG("XDG_SHELL: Unmap Surface: %d", wl_resource_get_id(resource)); */ + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + if (ec->comp_data->mapped) + { + ec->visible = EINA_FALSE; + evas_object_hide(ec->frame); + ec->comp_data->mapped = EINA_FALSE; + } +} + +static void +_e_xdg_surface_state_add(struct wl_resource *resource, struct wl_array *states, uint32_t state) +{ + uint32_t *s; + + s = wl_array_add(states, sizeof(*s)); + if (s) + *s = state; + else + wl_resource_post_no_memory(resource); +} + +static void +_xdg_shell_surface_send_configure(struct wl_resource *resource, Eina_Bool fullscreen, Eina_Bool maximized, uint32_t edges, int32_t width, int32_t height) +{ + struct wl_array states; + uint32_t serial; + E_Client *focused, *ec; + E_Shell_Data *shd; + State pending = 0; + Eina_Bool activated = EINA_FALSE; + + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_client_util_is_popup(ec)) return; + focused = e_client_focused_get(); + if (ec == focused) + activated = 1; + else if (focused && focused->parent) + { + do + { + if (focused->parent != ec) + focused = focused->parent; + else + activated = 1; + } while (focused && (!activated)); + } + shd = ec->comp_data->shell.data; + if ((shd->edges == edges) && (shd->width == width) && (shd->height == height) && + (shd->fullscreen == fullscreen) && + (shd->maximized == maximized) && + (shd->activated == activated)) return; + shd->edges = edges; + shd->width = width; + shd->height = height; + if (shd->fullscreen != fullscreen) + { + if (fullscreen) + pending |= STATE_FULLSCREEN; + else + pending |= STATE_UNFULLSCREEN; + } + shd->fullscreen = fullscreen; + if (shd->maximized != maximized) + { + if (maximized) + pending |= STATE_MAXIMIZED; + else + pending |= STATE_UNMAXIMIZED; + } + shd->maximized = maximized; + shd->activated = activated; + wl_array_init(&states); + + if (fullscreen) + _e_xdg_surface_state_add(resource, &states, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN); + else if (maximized) + _e_xdg_surface_state_add(resource, &states, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED); + if (edges) + _e_xdg_surface_state_add(resource, &states, ZXDG_TOPLEVEL_V6_STATE_RESIZING); + if (activated) + _e_xdg_surface_state_add(resource, &states, ZXDG_TOPLEVEL_V6_STATE_ACTIVATED); + + serial = wl_display_next_serial(e_comp_wl->wl.disp); + zxdg_toplevel_v6_send_configure(resource, width, height, &states); + { + Pending_State *ps; + + ps = E_NEW(Pending_State, 1); + ps->state = pending; + ps->serial = serial; + shd->pending = eina_list_append(shd->pending, ps); + } + zxdg_surface_v6_send_configure(shd->surface, serial); + + wl_array_release(&states); +} + +static void +_e_xdg_shell_surface_configure_send(struct wl_resource *resource, uint32_t edges, int32_t width, int32_t height) +{ + E_Client *ec; + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + if (e_client_util_is_popup(ec)) + { + E_Shell_Data *shd; + uint32_t serial; + + shd = ec->comp_data->shell.data; + serial = wl_display_next_serial(e_comp_wl->wl.disp); + zxdg_popup_v6_send_configure(resource, ec->x - ec->parent->x, ec->y - ec->parent->y, width, height); + zxdg_surface_v6_send_configure(shd->surface, serial); + return; + } + + _xdg_shell_surface_send_configure(resource, ec->fullscreen, !!ec->maximized || ec->comp_data->max, edges, width, height); +} + +static void +_e_xdg_popup_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +_e_xdg_popup_grab_dismiss(E_Client *ec) +{ + zxdg_popup_v6_send_popup_done(ec->comp_data->shell.surface); + ec->comp_data->grab = 0; +} + +static void +_e_xdg_popup_cb_grab(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial) +{ + E_Client *ec; + + ec = wl_resource_get_user_data(resource); + if ((!ec) || e_object_is_del(E_OBJECT(ec))) + { + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (ec->comp_data->mapped) + { + wl_resource_post_error(resource, ZXDG_POPUP_V6_ERROR_INVALID_GRAB, + "grab requested on mapped popup"); + return; + } + if (e_client_util_is_popup(ec->parent) && (!ec->parent->comp_data->grab)) + { + wl_resource_post_error(resource, ZXDG_POPUP_V6_ERROR_INVALID_GRAB, + "grab requested on ungrabbed nested popup"); + return; + } + e_comp_wl_grab_client_add(ec, _e_xdg_popup_grab_dismiss); +} + +static const struct zxdg_popup_v6_interface _e_xdg_popup_interface = { + _e_xdg_popup_cb_destroy, + _e_xdg_popup_cb_grab, +}; + +static void +_e_xdg_surface_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +_e_xdg_surface_cb_ack_configure(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t serial) +{ + E_Client *ec; + Pending_State *ps; + E_Shell_Data *shd; + Eina_List *l, *ll; + + ec = wl_resource_get_user_data(resource); + if (!ec) + { + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + shd = ec->comp_data->shell.data; + EINA_LIST_FOREACH_SAFE(shd->pending, l, ll, ps) + { + if (ps->serial > serial) break; + if (ps->state & STATE_FULLSCREEN) + { + ec->comp_data->shell.set.fullscreen = 1; + ec->comp_data->shell.set.unfullscreen = 0; + } + if (ps->state & STATE_UNFULLSCREEN) + { + ec->comp_data->shell.set.unfullscreen = 1; + ec->comp_data->shell.set.fullscreen = 0; + } + if (ps->state & STATE_MAXIMIZED) + { + ec->comp_data->shell.set.maximize = 1; + ec->comp_data->shell.set.unmaximize = 0; + } + if (ps->state & STATE_UNMAXIMIZED) + { + ec->comp_data->shell.set.unmaximize = 1; + ec->comp_data->shell.set.maximize = 0; + } + shd->pending = eina_list_remove_list(shd->pending, l); + free(ps); + } +} + +static void +_e_xdg_surface_cb_window_geometry_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h) +{ + E_Client *ec; + + ec = wl_resource_get_user_data(resource); + if (!ec) + { + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + EINA_RECTANGLE_SET(&ec->comp_data->shell.window, x, y, w, h); + //DBG("XDG_SHELL: Window Geom Set: %d \t%d %d, %d %d", wl_resource_get_id(resource), x, y, w, h); +} + +static void +_e_xdg_toplevel_cb_maximized_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + E_Client *ec; + int w, h; + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + if (ec->lock_user_maximize) return; + if (e_config->window_maximize_animate && (!ec->maximize_anims_disabled)) + w = ec->w, h = ec->h; + else + { + switch (e_config->maximize_policy & E_MAXIMIZE_TYPE) + { + case E_MAXIMIZE_FULLSCREEN: + w = ec->zone->w, h = ec->zone->h; + break; + default: + e_zone_useful_geometry_get(ec->zone, NULL, NULL, &w, &h); + } + } + _xdg_shell_surface_send_configure(resource, ec->fullscreen, 1, 0, w, h); +} + +static void +_e_xdg_toplevel_cb_maximized_unset(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + E_Client *ec; + int w, h; + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + if (ec->lock_user_maximize) return; + ec->comp_data->unmax = (e_config->maximize_policy & E_MAXIMIZE_TYPE) | E_MAXIMIZE_BOTH; + if (e_config->window_maximize_animate && (!ec->maximize_anims_disabled)) + w = ec->w, h = ec->h; + else + w = ec->saved.w, h = ec->saved.h; + _xdg_shell_surface_send_configure(resource, ec->fullscreen, 0, 0, w, h); +} + +static void +_e_xdg_toplevel_cb_fullscreen_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *output_resource EINA_UNUSED) +{ + E_Client *ec; + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + + if (ec->lock_user_fullscreen) return; + _xdg_shell_surface_send_configure(resource, 1, !!ec->maximized || ec->comp_data->max, 0, ec->zone->w, ec->zone->h); +} + +static void +_e_xdg_toplevel_cb_fullscreen_unset(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + E_Client *ec; + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + if (ec->lock_user_fullscreen) return; + _xdg_shell_surface_send_configure(resource, 0, !!ec->maximized || ec->comp_data->max, 0, ec->saved.w, ec->saved.h); +} + +static void +_e_xdg_toplevel_cb_minimized_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + E_Client *ec; + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + if (ec->lock_user_iconify) return; + ec->comp_data->shell.set.minimize = 1; + ec->comp_data->max = (e_config->maximize_policy & E_MAXIMIZE_TYPE) | E_MAXIMIZE_BOTH; +} + +static int +_apply_positioner_x(int x, Positioner *p, 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 (p->anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) + an |= ZXDG_POSITIONER_V6_ANCHOR_RIGHT; + else if (p->anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) + an |= ZXDG_POSITIONER_V6_ANCHOR_LEFT; + if (p->gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) + grav |= ZXDG_POSITIONER_V6_GRAVITY_RIGHT; + else if (p->gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + grav |= ZXDG_POSITIONER_V6_GRAVITY_LEFT; + } + else + { + an = p->anchor; + grav = p->gravity; + } + + /* left edge */ + if (an & ZXDG_POSITIONER_V6_ANCHOR_LEFT) + x += p->anchor_rect.x; + /* right edge */ + else if (an & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) + x += p->anchor_rect.x + p->anchor_rect.w; + /* center */ + else + x += p->anchor_rect.x + (p->anchor_rect.w / 2); + + /* flip left over anchor */ + if (grav & ZXDG_POSITIONER_V6_GRAVITY_LEFT) + x -= p->size.w; + /* center on anchor */ + else if (!(an & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) + x -= p->size.w / 2; + return x; +} + +static int +_apply_positioner_y(int y, Positioner *p, 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 (p->anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) + an |= ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; + else if (p->anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) + an |= ZXDG_POSITIONER_V6_ANCHOR_TOP; + if (p->gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) + grav |= ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; + else if (p->gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) + grav |= ZXDG_POSITIONER_V6_GRAVITY_TOP; + } + else + { + an = p->anchor; + grav = p->gravity; + } + + /* up edge */ + if (an & ZXDG_POSITIONER_V6_ANCHOR_TOP) + y += p->anchor_rect.y; + /* bottom edge */ + else if (an & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) + y += p->anchor_rect.y + p->anchor_rect.h; + /* center */ + else + y += p->anchor_rect.y + (p->anchor_rect.h / 2); + + /* flip up over anchor */ + if (grav & ZXDG_POSITIONER_V6_GRAVITY_TOP) + y -= p->size.h; + /* center on anchor */ + else if (!(an & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)) + y -= p->size.h / 2; + return y; +} + +static void +_apply_positioner(E_Client *ec, Positioner *p) +{ + int x, y; + int zx, zy, zw, zh; + /* apply base geometry: + * coords are relative to parent + */ + x = ec->x = ec->parent->x + p->offset.x; + y = ec->y = ec->parent->y + p->offset.y; + + if (p->size.w && p->size.h) + { + ec->w = p->size.w; + ec->h = p->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) + */ + ec->x = _apply_positioner_x(ec->x, p, 0); + ec->y = _apply_positioner_y(ec->y, p, 0); + + e_zone_useful_geometry_get(ec->parent->zone, &zx, &zy, &zw, &zh); + +#define CONSTRAINED(EC, X, Y) \ + !E_CONTAINS((X), (Y), (EC)->w, (EC)->h, zx, zy, zw, zh) + + if (!CONSTRAINED(ec, ec->x, ec->y)) return; + + /* assume smart placement: + - flip + - slide + - resize + */ + if ((p->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X) && + (!E_CONTAINS(ec->x, zy, ec->w, 1, zx, zy, zw, zh))) + { + int fx; + + fx = _apply_positioner_x(x, p, 1); + if (E_CONTAINS(fx, zy, ec->w, 1, zx, zy, zw, zh)) + ec->x = fx; + } + if (!CONSTRAINED(ec, ec->x, ec->y)) return; + if ((p->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y) && + (!E_CONTAINS(zx, ec->y, 1, ec->h, zx, zy, zw, zh))) + { + int fy; + + fy = _apply_positioner_y(y, p, 1); + if (E_CONTAINS(zx, fy, 1, ec->h, zx, zy, zw, zh)) + ec->y = fy; + } + if (!CONSTRAINED(ec, ec->x, ec->y)) return; + + if ((p->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X) && + (!E_CONTAINS(ec->x, zy, ec->w, 1, zx, zy, zw, zh))) + { + int sx = ec->x; + + if (p->gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) + { + if (ec->x + ec->w > zx + zw) + sx = MAX(zx + zw - ec->w, ec->parent->x); + else if (ec->x + ec->w < zx) + sx = zx; + } + else if (p->gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + { + if (ec->x + ec->w < zx) + sx = zx; + else if (ec->x + ec->w > zx + zw) + sx = MAX(zx + zw - ec->w, ec->parent->x); + } + if (E_CONTAINS(sx, zy, ec->w, 1, zx, zy, zw, zh)) + ec->x = sx; + } + if (!CONSTRAINED(ec, ec->x, ec->y)) return; + if ((p->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y) && + (!E_CONTAINS(zx, ec->y, 1, ec->h, zx, zy, zw, zh))) + { + int sy = ec->y; + + if (p->gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) + { + if (ec->y + ec->h > zy + zh) + sy = MAX(zy + zh - ec->h, ec->parent->y); + else if (ec->y + ec->h < zy) + sy = zy; + } + else if (p->gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) + { + if (ec->y + ec->h < zy) + sy = zy; + else if (ec->y + ec->h > zy + zh) + sy = MAX(zy + zh - ec->h, ec->parent->y); + } + if (E_CONTAINS(zx, sy, 1, ec->h, zx, zy, zw, zh)) + ec->y = sy; + } + if (!CONSTRAINED(ec, ec->x, ec->y)) return; + + if ((p->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X) && + (!E_CONTAINS(ec->x, zy, ec->w, 1, zx, zy, zw, zh))) + { + ec->w = zx + zw - ec->x; + ec->changes.size = 1; + if (!CONSTRAINED(ec, ec->x, ec->y)) return; + } + if ((p->constrain & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y) && + (!E_CONTAINS(zx, ec->y, 1, ec->h, zx, zy, zw, zh))) + { + ec->h = zy + zh - ec->y; + ec->changes.size = 1; + } +} + +static void +_e_xdg_surface_cb_popup_get(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *parent_resource, struct wl_resource *positioner_resource) +{ + E_Client *ec; + E_Comp_Client_Data *cdata; + Positioner *p; + + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Pixmap Set On Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + p = wl_resource_get_user_data(positioner_resource); + if (!p) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "invalid positioner"); + return; + } + + if ((!p->size.w) || (!p->anchor_rect.w)) + { + wl_resource_post_error(resource, + ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER, + "invalid positioner"); + return; + } + + cdata = ec->comp_data; + + if (cdata->shell.surface) + { + wl_resource_post_error(resource, + ZXDG_SHELL_V6_ERROR_ROLE, + "surface already has assigned role"); + return; + } + + /* check for the parent surface */ + if (!parent_resource) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "Popup requires a parent shell surface"); + return; + } + + /* try to create a shell surface */ + if (!(cdata->shell.surface = + wl_resource_create(client, &zxdg_popup_v6_interface, 1, id))) + { + ERR("Could not create xdg shell surface"); + wl_resource_post_no_memory(resource); + return; + } + + wl_resource_set_implementation(cdata->shell.surface, + &_e_xdg_popup_interface, ec, e_shell_surface_destroy); + + e_object_ref(E_OBJECT(ec)); + + cdata->shell.configure_send = _e_xdg_shell_surface_configure_send; + cdata->shell.configure = _e_xdg_shell_surface_configure; + cdata->shell.map = _e_xdg_shell_surface_map; + cdata->shell.unmap = _e_xdg_shell_surface_unmap; + + if (!ec->internal) + ec->borderless = !ec->internal_elm_win; + ec->lock_border = EINA_TRUE; + if (!ec->internal) + ec->border.changed = ec->changes.border = !ec->borderless; + ec->changes.icon = !!ec->icccm.class; + ec->netwm.type = E_WINDOW_TYPE_POPUP_MENU; + ec->placed = ec->comp_data->set_win_type = EINA_TRUE; + + /* set this client as a transient for parent */ + e_shell_surface_parent_set(ec, parent_resource); + + _apply_positioner(ec, p); + ec->client.x = ec->x; + ec->client.y = ec->y; + + if (ec->internal_elm_win && evas_object_visible_get(ec->internal_elm_win)) + _e_xdg_shell_surface_map(resource); +} + +static void +_e_xdg_toplevel_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} +static void +_e_xdg_toplevel_cb_parent_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *parent_resource) +{ + E_Client *ec; + + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + /* set this client as a transient for parent */ + e_shell_surface_parent_set(ec, parent_resource); +} + +static void +_e_xdg_toplevel_cb_title_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *title) +{ + E_Client *ec; + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + /* set title */ + eina_stringshare_replace(&ec->icccm.title, title); + if (ec->frame) e_comp_object_frame_title_set(ec->frame, title); +} + +static void +_e_xdg_toplevel_cb_app_id_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *id) +{ + E_Client *ec; + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + /* use the wl_client to get the pid * and set it in the netwm props */ + wl_client_get_credentials(client, &ec->netwm.pid, NULL, NULL); + + /* set class */ + eina_stringshare_replace(&ec->icccm.class, id); + /* eina_stringshare_replace(&ec->netwm.name, id); */ + ec->changes.icon = !!ec->icccm.class; + EC_CHANGED(ec); +} + +static void +_e_xdg_toplevel_cb_window_menu_show(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource EINA_UNUSED, uint32_t serial EINA_UNUSED, int32_t x, int32_t y) +{ + E_Client *ec; + double timestamp; + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + timestamp = ecore_loop_time_get(); + e_int_client_menu_show(ec, ec->x + x, ec->y + y, 0, timestamp); +} + +static void +_e_xdg_toplevel_cb_move(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource EINA_UNUSED, uint32_t serial EINA_UNUSED) +{ + E_Client *ec; + E_Binding_Event_Mouse_Button ev; + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + if ((ec->maximized) || (ec->fullscreen)) return; + + switch (e_comp_wl->ptr.button) + { + case BTN_LEFT: + ev.button = 1; + break; + case BTN_MIDDLE: + ev.button = 2; + break; + case BTN_RIGHT: + ev.button = 3; + break; + default: + ev.button = e_comp_wl->ptr.button; + break; + } + + e_comp_object_frame_xy_unadjust(ec->frame, + wl_fixed_to_int(e_comp_wl->ptr.x), + wl_fixed_to_int(e_comp_wl->ptr.y), + &ev.canvas.x, &ev.canvas.y); + + e_shell_surface_mouse_down_helper(ec, &ev, EINA_TRUE); +} + +static void +_e_xdg_toplevel_cb_resize(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource EINA_UNUSED, uint32_t serial EINA_UNUSED, uint32_t edges) +{ + E_Client *ec; + E_Binding_Event_Mouse_Button ev; + + /* DBG("XDG_SHELL: Surface Resize: %d\tEdges: %d", */ + /* wl_resource_get_id(resource), edges); */ + + /* get the client for this resource */ + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + if ((edges == 0) || (edges > 15) || + ((edges & 3) == 3) || ((edges & 12) == 12)) return; + + if ((ec->maximized) || (ec->fullscreen)) return; + + e_comp_wl->resize.resource = resource; + e_comp_wl->resize.edges = edges; + e_comp_wl->ptr.grab_x = e_comp_wl->ptr.x - wl_fixed_from_int(ec->client.x); + e_comp_wl->ptr.grab_y = e_comp_wl->ptr.y - wl_fixed_from_int(ec->client.y); + + switch (e_comp_wl->ptr.button) + { + case BTN_LEFT: + ev.button = 1; + break; + case BTN_MIDDLE: + ev.button = 2; + break; + case BTN_RIGHT: + ev.button = 3; + break; + default: + ev.button = e_comp_wl->ptr.button; + break; + } + + e_comp_object_frame_xy_unadjust(ec->frame, + wl_fixed_to_int(e_comp_wl->ptr.x), + wl_fixed_to_int(e_comp_wl->ptr.y), + &ev.canvas.x, &ev.canvas.y); + + e_shell_surface_mouse_down_helper(ec, &ev, EINA_FALSE); +} + +static void +_e_xdg_toplevel_cb_max_size_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t width, int32_t height) +{ + E_Client *ec; + + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + ec->comp_data->shell.set.max_size.w = width; + ec->comp_data->shell.set.max_size.h = height; +} + +static void +_e_xdg_toplevel_cb_min_size_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t width, int32_t height) +{ + E_Client *ec; + + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Client For Shell Surface"); + return; + } + ec->comp_data->shell.set.min_size.w = width; + ec->comp_data->shell.set.min_size.h = height; +} + +static const struct zxdg_toplevel_v6_interface _e_xdg_toplevel_interface = { + _e_xdg_toplevel_cb_destroy, + _e_xdg_toplevel_cb_parent_set, + _e_xdg_toplevel_cb_title_set, + _e_xdg_toplevel_cb_app_id_set, + _e_xdg_toplevel_cb_window_menu_show, + _e_xdg_toplevel_cb_move, + _e_xdg_toplevel_cb_resize, + _e_xdg_toplevel_cb_max_size_set, + _e_xdg_toplevel_cb_min_size_set, + _e_xdg_toplevel_cb_maximized_set, + _e_xdg_toplevel_cb_maximized_unset, + _e_xdg_toplevel_cb_fullscreen_set, + _e_xdg_toplevel_cb_fullscreen_unset, + _e_xdg_toplevel_cb_minimized_set, +}; + +static void +_e_xdg_surface_cb_toplevel_get(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t id) +{ + E_Client *ec; + E_Comp_Client_Data *cdata; + + if (!(ec = wl_resource_get_user_data(resource))) + { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "resource does not have xdg_shell surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + cdata = ec->comp_data; + if (cdata->shell.surface) + { + wl_resource_post_error(resource, + ZXDG_SHELL_V6_ERROR_ROLE, + "surface already has assigned role"); + return; + } + + if (!(cdata->shell.surface = + wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id))) + { + ERR("Could not create xdg shell surface"); + wl_resource_post_no_memory(resource); + return; + } + + wl_resource_set_implementation(cdata->shell.surface, + &_e_xdg_toplevel_interface, ec, + e_shell_surface_cb_destroy); + + e_object_ref(E_OBJECT(ec)); + + cdata->shell.configure_send = _e_xdg_shell_surface_configure_send; + cdata->shell.configure = _e_xdg_shell_surface_configure; + cdata->shell.map = _e_xdg_shell_surface_map; + cdata->shell.unmap = _e_xdg_shell_surface_unmap; + + /* set toplevel client properties */ + ec->icccm.accepts_focus = 1; + if (!ec->internal) + ec->borderless = 1; + ec->lock_border = EINA_TRUE; + if ((!ec->internal) || (!ec->borderless)) + ec->border.changed = ec->changes.border = !ec->borderless; + ec->netwm.type = E_WINDOW_TYPE_NORMAL; + ec->comp_data->set_win_type = EINA_TRUE; + + if (ec->internal_elm_win && evas_object_visible_get(ec->internal_elm_win)) + _e_xdg_shell_surface_map(resource); +} + +static const struct zxdg_surface_v6_interface _e_xdg_surface_interface = +{ + _e_xdg_surface_cb_destroy, + _e_xdg_surface_cb_toplevel_get, + _e_xdg_surface_cb_popup_get, + _e_xdg_surface_cb_window_geometry_set, + _e_xdg_surface_cb_ack_configure, +}; + +static void +_e_xdg_shell_surface_cb_destroy(struct wl_resource *resource) +{ + E_Shell_Data *shd; + E_Client *ec = wl_resource_get_user_data(resource); + + if (e_object_is_del(E_OBJECT(ec))) return; + + shd = ec->comp_data->shell.data; + if (shd) + ((v6_Shell_Data*)shd->shell)->surfaces = eina_list_remove(((v6_Shell_Data*)shd->shell)->surfaces, resource); + e_shell_surface_cb_destroy(resource); +} + +static void +_e_xdg_shell_cb_surface_get(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) +{ + E_Client *ec; + E_Comp_Client_Data *cdata; + E_Shell_Data *shd; + v6_Shell_Data *v; + + /* DBG("XDG_SHELL: Surface Get %d", wl_resource_get_id(surface_resource)); */ + + /* get the pixmap from this surface so we can find the client */ + if (!(ec = wl_resource_get_user_data(surface_resource))) + { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "No Pixmap Set On Surface"); + return; + } + if (e_object_is_del(E_OBJECT(ec))) return; + + ec->netwm.ping = 1; + cdata = ec->comp_data; + + /* check for existing shell surface */ + if (cdata->shell.data) + { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "Client already has XDG shell surface"); + return; + } + shd = cdata->shell.data = E_NEW(E_Shell_Data, 1); + shd->width = shd->height = -1; + + /* try to create a shell surface */ + if (!(shd->surface = + wl_resource_create(client, &zxdg_surface_v6_interface, 1, id))) + { + ERR("Could not create xdg shell surface"); + wl_resource_post_no_memory(surface_resource); + return; + } + + wl_resource_set_implementation(shd->surface, + &_e_xdg_surface_interface, ec, + _e_xdg_shell_surface_cb_destroy); + v = wl_resource_get_user_data(resource); + v->surfaces = eina_list_append(v->surfaces, shd->surface); + shd->shell = v; + + e_object_ref(E_OBJECT(ec)); + + cdata->shell.ping = _e_xdg_shell_surface_ping; + cdata->is_xdg_surface = EINA_TRUE; + + if (!ec->internal) + e_client_ping(ec); +} + +static void +_e_xdg_shell_cb_pong(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t serial EINA_UNUSED) +{ + E_Client *ec; + + if ((ec = wl_resource_get_user_data(resource))) + { + ec->ping_ok = EINA_TRUE; + ec->hung = EINA_FALSE; + } +} + +static const struct zxdg_shell_v6_interface _e_xdg_shell_interface = +{ + _e_xdg_shell_cb_destroy, + _e_xdg_shell_cb_positioner_create, + _e_xdg_shell_cb_surface_get, + _e_xdg_shell_cb_pong +}; + +static void +_e_xdg_shell_cb_unbind(struct wl_resource *resource) +{ + v6_Shell_Data *v; + struct wl_client *client = wl_resource_get_client(resource); + + v = wl_resource_get_user_data(resource); + eina_hash_set(shell_resources, &client, NULL); + E_FREE_LIST(v->surfaces, e_shell_surface_cb_destroy); + v->positioners = eina_list_free(v->positioners); + free(v); +} + +static void +_e_xdg_shell_cb_bind(struct wl_client *client, void *data EINA_UNUSED, uint32_t version, uint32_t id) +{ + struct wl_resource *res; + v6_Shell_Data *v; + + if (!(res = wl_resource_create(client, &zxdg_shell_v6_interface, version, id))) + { + wl_client_post_no_memory(client); + return; + } + + eina_hash_set(xdg_shell_resources, &client, res); + v = E_NEW(v6_Shell_Data, 1); + wl_resource_set_user_data(res, v); + wl_resource_set_implementation(res, &_e_xdg_shell_interface, + v, _e_xdg_shell_cb_unbind); +} + +EINTERN Eina_Bool +e_xdg_shell_v6_init(void) +{ + /* try to create global xdg_shell interface */ + if (!wl_global_create(e_comp_wl->wl.disp, &zxdg_shell_v6_interface, 1, + NULL, _e_xdg_shell_cb_bind)) + { + ERR("Could not create xdg_shell global"); + return EINA_FALSE; + } + return EINA_TRUE; +}