#include "e.h" #include "e_comp_wl.h" #include "e_surface.h" #include "e_mod_main.h" #include "e_desktop_shell_protocol.h" /* shell function prototypes */ static void _e_wl_shell_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED); static void _e_wl_shell_cb_bind(struct wl_client *client, void *data, unsigned int version EINA_UNUSED, unsigned int id); static void _e_wl_shell_cb_ping(E_Wayland_Surface *ews, unsigned int serial); static void _e_wl_shell_cb_pointer_focus(struct wl_listener *listener EINA_UNUSED, void *data); static void _e_wl_shell_grab_start(E_Wayland_Shell_Grab *grab, E_Wayland_Shell_Surface *ewss, struct wl_pointer *pointer, const struct wl_pointer_grab_interface *interface, enum e_desktop_shell_cursor cursor); static void _e_wl_shell_grab_end(E_Wayland_Shell_Grab *ewsg); static void _e_wl_shell_grab_cb_surface_destroy(struct wl_listener *listener, void *data EINA_UNUSED); /* shell interface prototypes */ static void _e_wl_shell_cb_shell_surface_get(struct wl_client *client, struct wl_resource *resource, unsigned int id, struct wl_resource *surface_resource); /* desktop shell function prototypes */ static void _e_wl_desktop_shell_cb_bind(struct wl_client *client, void *data, unsigned int version EINA_UNUSED, unsigned int id); static void _e_wl_desktop_shell_cb_unbind(struct wl_resource *resource); /* desktop shell interface prototypes */ static void _e_wl_desktop_shell_cb_shell_grab_surface_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *surface_resource); /* shell surface function prototypes */ static E_Wayland_Shell_Surface *_e_wl_shell_shell_surface_create(void *shell EINA_UNUSED, E_Wayland_Surface *ews, const void *client EINA_UNUSED); static void _e_wl_shell_shell_surface_create_toplevel(E_Wayland_Surface *ews); static void _e_wl_shell_shell_surface_create_popup(E_Wayland_Surface *ews); static void _e_wl_shell_shell_surface_destroy(struct wl_resource *resource); static void _e_wl_shell_shell_surface_configure(E_Wayland_Surface *ews, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h); static void _e_wl_shell_shell_surface_map(E_Wayland_Surface *ews, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h); static void _e_wl_shell_shell_surface_unmap(E_Wayland_Surface *ews); static void _e_wl_shell_shell_surface_type_set(E_Wayland_Shell_Surface *ewss); static void _e_wl_shell_shell_surface_type_reset(E_Wayland_Shell_Surface *ewss); static void _e_wl_shell_shell_surface_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED); static int _e_wl_shell_shell_surface_cb_ping_timeout(void *data); static void _e_wl_shell_shell_surface_cb_ee_resize(Ecore_Evas *ee); static void _e_wl_shell_shell_surface_cb_render_post(void *data, Evas *evas EINA_UNUSED, void *event EINA_UNUSED); static void _e_wl_shell_shell_surface_cb_focus_in(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED); static void _e_wl_shell_shell_surface_cb_focus_out(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED); static void _e_wl_shell_shell_surface_cb_mouse_in(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED); static void _e_wl_shell_shell_surface_cb_mouse_out(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED); static void _e_wl_shell_shell_surface_cb_mouse_move(void *data, Evas_Object *obj EINA_UNUSED, void *event); static void _e_wl_shell_shell_surface_cb_mouse_up(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED); static void _e_wl_shell_shell_surface_cb_mouse_down(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED); static void _e_wl_shell_shell_surface_cb_key_up(void *data, Evas_Object *obj EINA_UNUSED, void *event); static void _e_wl_shell_shell_surface_cb_key_down(void *data, Evas_Object *obj EINA_UNUSED, void *event); static void _e_wl_shell_shell_surface_cb_bd_move_end(void *data, void *bd); static void _e_wl_shell_shell_surface_cb_bd_resize_update(void *data, void *bd); static void _e_wl_shell_shell_surface_cb_bd_resize_end(void *data, void *bd); static void _e_wl_shell_mouse_down_helper(E_Border *bd, int button, Evas_Point *output, E_Binding_Event_Mouse_Button *ev, Eina_Bool move); static void _e_wl_shell_mouse_up_helper(E_Border *bd, int button, Evas_Point *output, E_Binding_Event_Mouse_Button *ev EINA_UNUSED); /* shell surface interface prototypes */ static void _e_wl_shell_shell_surface_cb_pong(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, unsigned int serial); static void _e_wl_shell_shell_surface_cb_move(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource, unsigned int serial); static void _e_wl_shell_shell_surface_cb_resize(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource, unsigned int serial, unsigned int edges); static void _e_wl_shell_shell_surface_cb_toplevel_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource); static void _e_wl_shell_shell_surface_cb_transient_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *parent_resource, int x, int y, unsigned int flags); static void _e_wl_shell_shell_surface_cb_fullscreen_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, unsigned int method EINA_UNUSED, unsigned int framerate EINA_UNUSED, struct wl_resource *output_resource EINA_UNUSED); static void _e_wl_shell_shell_surface_cb_popup_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource, unsigned int serial, struct wl_resource *parent_resource, int x, int y, unsigned int flags EINA_UNUSED); static void _e_wl_shell_shell_surface_cb_maximized_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *output_resource EINA_UNUSED); static void _e_wl_shell_shell_surface_cb_title_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *title); static void _e_wl_shell_shell_surface_cb_class_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *clas); /* shell move_grab interface prototypes */ static void _e_wl_shell_move_grab_cb_focus(struct wl_pointer_grab *grab, struct wl_resource *surface EINA_UNUSED, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED); static void _e_wl_shell_move_grab_cb_motion(struct wl_pointer_grab *grab, unsigned int timestamp EINA_UNUSED, wl_fixed_t x, wl_fixed_t y); static void _e_wl_shell_move_grab_cb_button(struct wl_pointer_grab *grab, unsigned int timestamp EINA_UNUSED, unsigned int button EINA_UNUSED, unsigned int state); /* shell resize_grab interface prototypes */ static void _e_wl_shell_resize_grab_cb_focus(struct wl_pointer_grab *grab, struct wl_resource *surface EINA_UNUSED, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED); static void _e_wl_shell_resize_grab_cb_motion(struct wl_pointer_grab *grab EINA_UNUSED, unsigned int timestamp EINA_UNUSED, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED); static void _e_wl_shell_resize_grab_cb_button(struct wl_pointer_grab *grab, unsigned int timestamp EINA_UNUSED, unsigned int button EINA_UNUSED, unsigned int state); /* shell popup_grab interface prototypes */ static void _e_wl_shell_popup_grab_cb_focus(struct wl_pointer_grab *grab, struct wl_resource *surface, wl_fixed_t x, wl_fixed_t y); static void _e_wl_shell_popup_grab_cb_motion(struct wl_pointer_grab *grab EINA_UNUSED, unsigned int timestamp EINA_UNUSED, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED); static void _e_wl_shell_popup_grab_cb_button(struct wl_pointer_grab *grab, unsigned int timestamp, unsigned int button, unsigned int state); /* shell popup function prototypes */ static void _e_wl_shell_popup_grab_end(struct wl_pointer *pointer); /* shell busy_grab interface prototypes */ static void _e_wl_shell_busy_grab_cb_focus(struct wl_pointer_grab *grab, struct wl_resource *surface, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED); static void _e_wl_shell_busy_grab_cb_motion(struct wl_pointer_grab *grab EINA_UNUSED, unsigned int timestamp EINA_UNUSED, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED); static void _e_wl_shell_busy_grab_cb_button(struct wl_pointer_grab *grab, unsigned int timestamp EINA_UNUSED, unsigned int button, unsigned int state); /* local wayland interfaces */ static const struct wl_shell_interface _e_shell_interface = { _e_wl_shell_cb_shell_surface_get }; static const struct e_desktop_shell_interface _e_desktop_shell_interface = { NULL, // desktop_background_set NULL, // desktop_panel_set NULL, // desktop_lock_surface_set NULL, // desktop_unlock _e_wl_desktop_shell_cb_shell_grab_surface_set }; static const struct wl_shell_surface_interface _e_shell_surface_interface = { _e_wl_shell_shell_surface_cb_pong, _e_wl_shell_shell_surface_cb_move, _e_wl_shell_shell_surface_cb_resize, _e_wl_shell_shell_surface_cb_toplevel_set, _e_wl_shell_shell_surface_cb_transient_set, _e_wl_shell_shell_surface_cb_fullscreen_set, _e_wl_shell_shell_surface_cb_popup_set, _e_wl_shell_shell_surface_cb_maximized_set, _e_wl_shell_shell_surface_cb_title_set, _e_wl_shell_shell_surface_cb_class_set }; static const struct wl_pointer_grab_interface _e_move_grab_interface = { _e_wl_shell_move_grab_cb_focus, _e_wl_shell_move_grab_cb_motion, _e_wl_shell_move_grab_cb_button, }; static const struct wl_pointer_grab_interface _e_resize_grab_interface = { _e_wl_shell_resize_grab_cb_focus, _e_wl_shell_resize_grab_cb_motion, _e_wl_shell_resize_grab_cb_button, }; static const struct wl_pointer_grab_interface _e_popup_grab_interface = { _e_wl_shell_popup_grab_cb_focus, _e_wl_shell_popup_grab_cb_motion, _e_wl_shell_popup_grab_cb_button, }; static const struct wl_pointer_grab_interface _e_busy_grab_interface = { _e_wl_shell_busy_grab_cb_focus, _e_wl_shell_busy_grab_cb_motion, _e_wl_shell_busy_grab_cb_button, }; /* local variables */ /* external variables */ EAPI E_Module_Api e_modapi = { E_MODULE_API_VERSION, "Wl_Desktop_Shell" }; EAPI void * e_modapi_init(E_Module *m) { E_Wayland_Desktop_Shell *shell = NULL; struct wl_global *gshell = NULL; E_Wayland_Input *input = NULL; Eina_List *l = NULL; /* test for valid compositor */ if (!_e_wl_comp) return NULL; /* try to allocate space for the shell structure */ if (!(shell = E_NEW(E_Wayland_Desktop_Shell, 1))) return NULL; /* tell the shell what compositor to use */ shell->compositor = _e_wl_comp; /* setup shell destroy callback */ shell->wl.destroy_listener.notify = _e_wl_shell_cb_destroy; wl_signal_add(&_e_wl_comp->signals.destroy, &shell->wl.destroy_listener); /* setup compositor ping callback */ _e_wl_comp->ping_cb = _e_wl_shell_cb_ping; /* setup compositor shell interface functions */ _e_wl_comp->shell_interface.shell = shell; /* try to add this shell to the display's global list */ if (!(gshell = wl_display_add_global(_e_wl_comp->wl.display, &wl_shell_interface, shell, _e_wl_shell_cb_bind))) goto err; /* try to add the desktop shell interface to the display's global list */ if (!wl_display_add_global(_e_wl_comp->wl.display, &e_desktop_shell_interface, shell, _e_wl_desktop_shell_cb_bind)) goto err; /* for each input, we need to create a pointer focus listener */ EINA_LIST_FOREACH(_e_wl_comp->seats, l, input) { struct wl_listener *lst; /* skip this input if it has no pointer */ if (!input->has_pointer) continue; /* allocate space for the listener */ lst = malloc(sizeof(*lst)); /* set the listener callback function */ lst->notify = _e_wl_shell_cb_pointer_focus; /* add this listener to the pointer focus signal */ wl_signal_add(&input->wl.seat.pointer->focus_signal, lst); } return m; err: /* remove previously added shell global */ if (gshell) wl_display_remove_global(_e_wl_comp->wl.display, gshell); /* reset compositor shell interface */ _e_wl_comp->shell_interface.shell = NULL; /* free the allocated shell structure */ E_FREE(shell); return NULL; } EAPI int e_modapi_shutdown(E_Module *m EINA_UNUSED) { /* nothing to do here as shell will get the destroy callback from * the compositor and we can cleanup there */ return 1; } /* shell functions */ static void _e_wl_shell_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED) { E_Wayland_Desktop_Shell *shell = NULL; /* try to get the shell from the listener */ shell = container_of(listener, E_Wayland_Desktop_Shell, wl.destroy_listener); /* free the allocated shell structure */ E_FREE(shell); } static void _e_wl_shell_cb_bind(struct wl_client *client, void *data, unsigned int version EINA_UNUSED, unsigned int id) { E_Wayland_Desktop_Shell *shell = NULL; /* try to cast data to our shell */ if (!(shell = data)) return; /* try to add the shell to the client */ wl_client_add_object(client, &wl_shell_interface, &_e_shell_interface, id, shell); } static void _e_wl_shell_cb_ping(E_Wayland_Surface *ews, unsigned int serial) { E_Wayland_Shell_Surface *ewss = NULL; if (!ews) return; /* try to cast to our shell surface */ if (!(ewss = ews->shell_surface)) return; /* if we do not have a ping timer yet, create one */ if (!ewss->ping_timer) { E_Wayland_Ping_Timer *tmr = NULL; /* try to allocate space for a new ping timer */ if (!(tmr = E_NEW(E_Wayland_Ping_Timer, 1))) return; tmr->serial = serial; /* add a timer to the event loop */ tmr->source = wl_event_loop_add_timer(_e_wl_comp->wl.loop, _e_wl_shell_shell_surface_cb_ping_timeout, ewss); /* update timer source interval */ wl_event_source_timer_update(tmr->source, e_config->ping_clients_interval); ewss->ping_timer = tmr; /* send the ping to the shell surface */ wl_shell_surface_send_ping(ewss->wl.resource, serial); } } static void _e_wl_shell_cb_pointer_focus(struct wl_listener *listener EINA_UNUSED, void *data) { struct wl_pointer *ptr; E_Wayland_Surface *ews = NULL; if (!(ptr = data)) return; /* try to cast the current pointer focus to a wayland surface */ if (!(ews = (E_Wayland_Surface *)ptr->focus)) return; /* if this surface has a shell_surface and it is not active, then we * should set the busy cursor on it */ if ((ews->shell_surface) && (!ews->shell_surface->active)) { E_Wayland_Shell_Grab *grab = NULL; /* try to allocate space for our grab structure */ if (!(grab = E_NEW(E_Wayland_Shell_Grab, 1))) return; /* set grab properties */ grab->x = ptr->grab_x; grab->y = ptr->grab_y; /* set busy cursor */ _e_wl_shell_grab_start(grab, ews->shell_surface, ptr, &_e_busy_grab_interface, E_DESKTOP_SHELL_CURSOR_BUSY); } else { unsigned int serial = 0; serial = wl_display_next_serial(_e_wl_comp->wl.display); /* ping the surface */ _e_wl_shell_cb_ping(ews, serial); } } static void _e_wl_shell_grab_start(E_Wayland_Shell_Grab *grab, E_Wayland_Shell_Surface *ewss, struct wl_pointer *pointer, const struct wl_pointer_grab_interface *interface, enum e_desktop_shell_cursor cursor) { E_Wayland_Desktop_Shell *shell; /* safety check */ if ((!grab) || (!ewss)) return; /* end any popup grabs */ _e_wl_shell_popup_grab_end(pointer); /* setup grab properties */ grab->grab.interface = interface; grab->shell_surface = ewss; grab->shell_surface_destroy.notify = _e_wl_shell_grab_cb_surface_destroy; grab->pointer = pointer; grab->grab.focus = ewss->surface->wl.surface; /* add a listener in case this surface gets destroyed */ wl_signal_add(&ewss->wl.destroy_signal, &grab->shell_surface_destroy); /* start the pointer grab */ wl_pointer_start_grab(pointer, &grab->grab); shell = (E_Wayland_Desktop_Shell *)_e_wl_comp->shell_interface.shell; /* set cursor */ if (shell) e_desktop_shell_send_grab_cursor(shell->wl.resource, cursor); /* tell the pointer which surface is focused */ wl_pointer_set_focus(pointer, ewss->surface->wl.surface, 0, 0); } static void _e_wl_shell_grab_end(E_Wayland_Shell_Grab *ewsg) { /* safety */ if (!ewsg) return; /* remove the destroy listener */ if (ewsg->shell_surface) wl_list_remove(&ewsg->shell_surface_destroy.link); /* end the grab */ wl_pointer_end_grab(ewsg->pointer); } static void _e_wl_shell_grab_cb_surface_destroy(struct wl_listener *listener, void *data EINA_UNUSED) { E_Wayland_Shell_Grab *ewsg = NULL; /* try to get the grab from the listener */ ewsg = container_of(listener, E_Wayland_Shell_Grab, shell_surface_destroy); if (!ewsg) return; /* set the grab surface to null */ ewsg->shell_surface = NULL; } /* shell interface functions */ static void _e_wl_shell_cb_shell_surface_get(struct wl_client *client, struct wl_resource *resource, unsigned int id, struct wl_resource *surface_resource) { E_Wayland_Surface *ews = NULL; E_Wayland_Shell_Surface *ewss = NULL; /* try to cast the surface resource to our structure */ if (!(ews = wl_resource_get_user_data(surface_resource))) return; /* check if this surface already has a shell surface */ if ((ews->configure) && (ews->configure == _e_wl_shell_shell_surface_configure)) { wl_resource_post_error(surface_resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "Shell surface already requested for surface"); return; } /* try to create a shell surface for this surface */ if (!(ewss = _e_wl_shell_shell_surface_create(NULL, ews, NULL))) { wl_resource_post_no_memory(resource); return; } ewss->wl.resource = wl_client_add_object(client, &wl_shell_surface_interface, &_e_shell_surface_interface, id, ewss); wl_resource_set_destructor(ewss->wl.resource, _e_wl_shell_shell_surface_destroy); } /* desktop shell functions */ static void _e_wl_desktop_shell_cb_bind(struct wl_client *client, void *data, unsigned int version EINA_UNUSED, unsigned int id) { E_Wayland_Desktop_Shell *shell = NULL; struct wl_resource *res = NULL; /* try to cast data to our shell */ if (!(shell = data)) return; /* try to add the desktop shell to the client */ if (!(res = wl_client_add_object(client, &e_desktop_shell_interface, &_e_desktop_shell_interface, id, shell))) { wl_resource_post_error(res, WL_DISPLAY_ERROR_INVALID_OBJECT, "Permission Denied"); wl_resource_destroy(res); return; } shell->wl.resource = res; /* set desktop shell destroy callback */ wl_resource_set_destructor(res, _e_wl_desktop_shell_cb_unbind); } static void _e_wl_desktop_shell_cb_unbind(struct wl_resource *resource) { E_Wayland_Desktop_Shell *shell = NULL; shell = wl_resource_get_user_data(resource); shell->wl.resource = NULL; } /* desktop shell interface functions */ static void _e_wl_desktop_shell_cb_shell_grab_surface_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *surface_resource) { E_Wayland_Desktop_Shell *shell = NULL; /* try to get the shell */ if (!(shell = wl_resource_get_user_data(resource))) return; shell->grab_surface = wl_resource_get_user_data(surface_resource); } /* shell surface functions */ static E_Wayland_Shell_Surface * _e_wl_shell_shell_surface_create(void *shell EINA_UNUSED, E_Wayland_Surface *ews, const void *client EINA_UNUSED) { E_Wayland_Shell_Surface *ewss = NULL; /* try to allocate space for our shell surface structure */ if (!(ewss = E_NEW(E_Wayland_Shell_Surface, 1))) return NULL; /* set some function pointers on the surface */ ews->configure = _e_wl_shell_shell_surface_configure; ews->map = _e_wl_shell_shell_surface_map; ews->unmap = _e_wl_shell_shell_surface_unmap; /* set some properties on the shell surface */ ewss->surface = ews; ews->shell_surface = ewss; /* setup shell surface destroy */ wl_signal_init(&ewss->wl.destroy_signal); ewss->wl.surface_destroy.notify = _e_wl_shell_shell_surface_cb_destroy; wl_signal_add(&ews->wl.destroy_signal, &ewss->wl.surface_destroy); wl_list_init(&ewss->wl.link); /* set some defaults on this shell surface */ ewss->active = EINA_TRUE; ewss->type = E_WAYLAND_SHELL_SURFACE_TYPE_NONE; ewss->next_type = E_WAYLAND_SHELL_SURFACE_TYPE_NONE; return ewss; } static void _e_wl_shell_shell_surface_create_toplevel(E_Wayland_Surface *ews) { E_Container *con; /* get the current container */ con = e_container_current_get(e_manager_current_get()); /* create the ecore_evas */ ews->ee = ecore_evas_new(NULL, ews->geometry.x, ews->geometry.y, ews->geometry.w, ews->geometry.h, NULL); ecore_evas_alpha_set(ews->ee, EINA_TRUE); ecore_evas_borderless_set(ews->ee, EINA_TRUE); ecore_evas_callback_resize_set(ews->ee, _e_wl_shell_shell_surface_cb_ee_resize); ecore_evas_data_set(ews->ee, "surface", ews); /* ecore_evas_input_event_unregister(ews->ee); */ ews->evas_win = ecore_evas_window_get(ews->ee); /* get a reference to the canvas */ ews->evas = ecore_evas_get(ews->ee); /* setup a post_render callback */ evas_event_callback_add(ews->evas, EVAS_CALLBACK_RENDER_POST, _e_wl_shell_shell_surface_cb_render_post, ews); /* create a surface smart object */ ews->obj = e_surface_add(ews->evas); evas_object_move(ews->obj, 0, 0); evas_object_resize(ews->obj, ews->geometry.w, ews->geometry.h); evas_object_show(ews->obj); /* hook smart object callbacks */ evas_object_smart_callback_add(ews->obj, "mouse_in", _e_wl_shell_shell_surface_cb_mouse_in, ews); evas_object_smart_callback_add(ews->obj, "mouse_out", _e_wl_shell_shell_surface_cb_mouse_out, ews); evas_object_smart_callback_add(ews->obj, "mouse_move", _e_wl_shell_shell_surface_cb_mouse_move, ews); evas_object_smart_callback_add(ews->obj, "mouse_up", _e_wl_shell_shell_surface_cb_mouse_up, ews); evas_object_smart_callback_add(ews->obj, "mouse_down", _e_wl_shell_shell_surface_cb_mouse_down, ews); evas_object_smart_callback_add(ews->obj, "key_up", _e_wl_shell_shell_surface_cb_key_up, ews); evas_object_smart_callback_add(ews->obj, "key_down", _e_wl_shell_shell_surface_cb_key_down, ews); evas_object_smart_callback_add(ews->obj, "focus_in", _e_wl_shell_shell_surface_cb_focus_in, ews); evas_object_smart_callback_add(ews->obj, "focus_out", _e_wl_shell_shell_surface_cb_focus_out, ews); ecore_x_icccm_size_pos_hints_set(ews->evas_win, 0, ECORE_X_GRAVITY_NW, 0, 0, 9999, 9999, 0, 0, 1, 1, 0.0, 0.0); ecore_x_icccm_transient_for_unset(ews->evas_win); ecore_x_netwm_window_type_set(ews->evas_win, ECORE_X_WINDOW_TYPE_NORMAL); ecore_evas_lower(ews->ee); /* create the e border for this surface */ ews->bd = e_border_new(con, ews->evas_win, 1, 1); e_surface_border_input_set(ews->obj, ews->bd); ews->bd->re_manage = 0; ews->bd->internal = 1; ews->bd->internal_ecore_evas = ews->ee; ews->bd->internal_no_remember = 1; ews->bd->internal_no_reopen = 1; e_border_move_resize(ews->bd, ews->geometry.x, ews->geometry.y, ews->geometry.w, ews->geometry.h); e_border_show(ews->bd); ews->bd_hooks = eina_list_append(ews->bd_hooks, e_border_hook_add(E_BORDER_HOOK_MOVE_END, _e_wl_shell_shell_surface_cb_bd_move_end, ews)); ews->bd_hooks = eina_list_append(ews->bd_hooks, e_border_hook_add(E_BORDER_HOOK_RESIZE_END, _e_wl_shell_shell_surface_cb_bd_resize_end, ews)); ews->bd_hooks = eina_list_append(ews->bd_hooks, e_border_hook_add(E_BORDER_HOOK_RESIZE_UPDATE, _e_wl_shell_shell_surface_cb_bd_resize_update, ews)); ews->mapped = EINA_TRUE; } static void _e_wl_shell_shell_surface_create_popup(E_Wayland_Surface *ews) { E_Wayland_Shell_Surface *ewss = NULL; Ecore_X_Window parent = 0; struct wl_seat *seat; char opts[PATH_MAX]; /* try to get the shell surface */ if (!(ewss = ews->shell_surface)) return; if (ewss->parent) parent = ecore_evas_window_get(ewss->parent->ee); snprintf(opts, sizeof(opts), "parent=%d", parent); /* create an ecore evas to represent this 'popup' */ ews->ee = ecore_evas_new(NULL, ewss->popup.x, ewss->popup.y, ews->geometry.w, ews->geometry.h, opts); ecore_evas_alpha_set(ews->ee, EINA_TRUE); ecore_evas_borderless_set(ews->ee, EINA_TRUE); ecore_evas_override_set(ews->ee, EINA_TRUE); ecore_evas_callback_resize_set(ews->ee, _e_wl_shell_shell_surface_cb_ee_resize); ecore_evas_data_set(ews->ee, "surface", ews); ecore_evas_input_event_unregister(ews->ee); ews->evas_win = ecore_evas_window_get(ews->ee); /* get a reference to the canvas */ ews->evas = ecore_evas_get(ews->ee); /* setup a post_render callback */ evas_event_callback_add(ews->evas, EVAS_CALLBACK_RENDER_POST, _e_wl_shell_shell_surface_cb_render_post, ews); /* create a surface smart object */ ews->obj = e_surface_add(ews->evas); evas_object_move(ews->obj, 0, 0); evas_object_resize(ews->obj, ews->geometry.w, ews->geometry.h); evas_object_show(ews->obj); /* hook smart object callbacks */ evas_object_smart_callback_add(ews->obj, "mouse_in", _e_wl_shell_shell_surface_cb_mouse_in, ews); evas_object_smart_callback_add(ews->obj, "mouse_out", _e_wl_shell_shell_surface_cb_mouse_out, ews); evas_object_smart_callback_add(ews->obj, "mouse_move", _e_wl_shell_shell_surface_cb_mouse_move, ews); evas_object_smart_callback_add(ews->obj, "mouse_up", _e_wl_shell_shell_surface_cb_mouse_up, ews); evas_object_smart_callback_add(ews->obj, "mouse_down", _e_wl_shell_shell_surface_cb_mouse_down, ews); evas_object_smart_callback_add(ews->obj, "key_up", _e_wl_shell_shell_surface_cb_key_up, ews); evas_object_smart_callback_add(ews->obj, "key_down", _e_wl_shell_shell_surface_cb_key_down, ews); evas_object_smart_callback_add(ews->obj, "focus_in", _e_wl_shell_shell_surface_cb_focus_in, ews); evas_object_smart_callback_add(ews->obj, "focus_out", _e_wl_shell_shell_surface_cb_focus_out, ews); ecore_x_icccm_size_pos_hints_set(ews->evas_win, 0, ECORE_X_GRAVITY_NW, 0, 0, 9999, 9999, 0, 0, 1, 1, 0.0, 0.0); ecore_x_icccm_transient_for_set(ews->evas_win, parent); ecore_x_netwm_window_type_set(ews->evas_win, ECORE_X_WINDOW_TYPE_POPUP_MENU); /* ecore_evas_lower(ews->ee); */ ecore_evas_show(ews->ee); ews->mapped = EINA_TRUE; /* set popup properties */ ewss->popup.grab.interface = &_e_popup_grab_interface; ewss->popup.up = EINA_FALSE; if (ewss->parent) { /* TODO */ /* ewss->popup.parent_destroy.notify = ; */ /* add a signal callback to be raised when the parent gets destroyed */ /* wl_signal_add(&ewss->parent->wl.surface.resource.destroy_signal, */ /* &ewss->popup.parent_destroy); */ } seat = ewss->popup.seat; if (seat->pointer->grab_serial == ewss->popup.serial) wl_pointer_start_grab(seat->pointer, &ewss->popup.grab); else wl_shell_surface_send_popup_done(ewss->wl.resource); } static void _e_wl_shell_shell_surface_destroy(struct wl_resource *resource) { E_Wayland_Shell_Surface *ewss = NULL; E_Wayland_Ping_Timer *tmr = NULL; /* try to cast the resource to our shell surface */ if (!(ewss = wl_resource_get_user_data(resource))) return; /* if we have a popup grab, end it */ if (ewss->popup.grab.pointer) wl_pointer_end_grab(ewss->popup.grab.pointer); wl_list_remove(&ewss->wl.surface_destroy.link); ewss->surface->configure = NULL; /* try to cast the ping timer */ if ((tmr = (E_Wayland_Ping_Timer *)ewss->ping_timer)) { if (tmr->source) wl_event_source_remove(tmr->source); /* free the allocated space for ping timer */ E_FREE(tmr); ewss->ping_timer = NULL; } wl_list_remove(&ewss->wl.link); /* try to free our allocated structure */ /* E_FREE(ewss); */ } static void _e_wl_shell_shell_surface_configure(E_Wayland_Surface *ews, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) { E_Wayland_Shell_Surface *ewss = NULL; Eina_Bool changed_type = EINA_FALSE; if ((ews->configure == _e_wl_shell_shell_surface_configure)) { if (!(ewss = ews->shell_surface)) return; } else return; /* handle shell_surface type change */ if ((ewss->next_type != E_WAYLAND_SHELL_SURFACE_TYPE_NONE) && (ewss->type != ewss->next_type)) { /* set the shell surface type */ _e_wl_shell_shell_surface_type_set(ewss); changed_type = EINA_TRUE; } /* if this surface is not mapped yet, then we need to map it */ if (!ews->mapped) { /* if the surface has a map function, call that. Else we use a * default one for this shell */ if (ews->map) ews->map(ews, x, y, w, h); else _e_wl_shell_shell_surface_map(ews, x, y, w, h); } else if ((changed_type) || (x != 0) || (y != 0) || (ews->geometry.w != w) || (ews->geometry.h != h)) { Evas_Coord fx = 0, fy = 0; Evas_Coord tx = 0, ty = 0; fx += ews->geometry.x; fy += ews->geometry.y; tx = x + ews->geometry.x; ty = y + ews->geometry.y; ews->geometry.x = ews->geometry.x + tx - fx; ews->geometry.y = ews->geometry.y + ty - fy; ews->geometry.w = w; ews->geometry.h = h; ews->geometry.changed = EINA_TRUE; } } static void _e_wl_shell_shell_surface_map(E_Wayland_Surface *ews, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) { /* safety check */ if (!ews) return; /* update surface geometry */ ews->geometry.w = w; ews->geometry.h = h; ews->geometry.changed = EINA_TRUE; /* starting position */ switch (ews->shell_surface->type) { case E_WAYLAND_SHELL_SURFACE_TYPE_TOPLEVEL: ews->geometry.x = x; ews->geometry.y = y; ews->geometry.changed = EINA_TRUE; break; case E_WAYLAND_SHELL_SURFACE_TYPE_FULLSCREEN: /* center */ /* map fullscreen */ break; case E_WAYLAND_SHELL_SURFACE_TYPE_MAXIMIZED: /* maximize */ break; case E_WAYLAND_SHELL_SURFACE_TYPE_POPUP: ews->geometry.x = x; ews->geometry.y = y; ews->geometry.changed = EINA_TRUE; break; case E_WAYLAND_SHELL_SURFACE_TYPE_NONE: ews->geometry.x += x; ews->geometry.y += y; ews->geometry.changed = EINA_TRUE; break; default: break; } /* stacking */ /* activate */ switch (ews->shell_surface->type) { case E_WAYLAND_SHELL_SURFACE_TYPE_TOPLEVEL: _e_wl_shell_shell_surface_create_toplevel(ews); break; case E_WAYLAND_SHELL_SURFACE_TYPE_POPUP: _e_wl_shell_shell_surface_create_popup(ews); break; default: break; } } static void _e_wl_shell_shell_surface_unmap(E_Wayland_Surface *ews) { Eina_List *l = NULL; E_Wayland_Input *input; E_Border_Hook *hook; if (!ews) return; EINA_LIST_FREE(ews->bd_hooks, hook) e_border_hook_del(hook); EINA_LIST_FOREACH(_e_wl_comp->seats, l, input) { if ((input->wl.seat.keyboard) && (input->wl.seat.keyboard->focus == ews->wl.surface)) wl_keyboard_set_focus(input->wl.seat.keyboard, NULL); if ((input->wl.seat.pointer) && (input->wl.seat.pointer->focus == ews->wl.surface)) wl_pointer_set_focus(input->wl.seat.pointer, NULL, 0, 0); } if (ews->obj) { /* delete smart callbacks */ evas_object_smart_callback_del(ews->obj, "mouse_in", _e_wl_shell_shell_surface_cb_mouse_in); evas_object_smart_callback_del(ews->obj, "mouse_out", _e_wl_shell_shell_surface_cb_mouse_out); evas_object_smart_callback_del(ews->obj, "mouse_move", _e_wl_shell_shell_surface_cb_mouse_move); evas_object_smart_callback_del(ews->obj, "mouse_up", _e_wl_shell_shell_surface_cb_mouse_up); evas_object_smart_callback_del(ews->obj, "mouse_down", _e_wl_shell_shell_surface_cb_mouse_down); evas_object_smart_callback_del(ews->obj, "key_up", _e_wl_shell_shell_surface_cb_key_up); evas_object_smart_callback_del(ews->obj, "key_down", _e_wl_shell_shell_surface_cb_key_down); evas_object_smart_callback_del(ews->obj, "focus_in", _e_wl_shell_shell_surface_cb_focus_in); evas_object_smart_callback_del(ews->obj, "focus_out", _e_wl_shell_shell_surface_cb_focus_out); /* delete the object */ evas_object_del(ews->obj); } if (ews->ee) ecore_evas_free(ews->ee); if (ews->bd) e_object_del(E_OBJECT(ews->bd)); ews->mapped = EINA_FALSE; } static void _e_wl_shell_shell_surface_type_set(E_Wayland_Shell_Surface *ewss) { /* safety check */ if (!ewss) return; /* reset the shell surface type */ _e_wl_shell_shell_surface_type_reset(ewss); ewss->type = ewss->next_type; ewss->next_type = E_WAYLAND_SHELL_SURFACE_TYPE_NONE; switch (ewss->type) { case E_WAYLAND_SHELL_SURFACE_TYPE_TRANSIENT: ewss->surface->geometry.x = ewss->parent->geometry.x + ewss->transient.x; ewss->surface->geometry.y = ewss->parent->geometry.y + ewss->transient.y; break; /* record the current geometry so we can restore it */ case E_WAYLAND_SHELL_SURFACE_TYPE_FULLSCREEN: case E_WAYLAND_SHELL_SURFACE_TYPE_MAXIMIZED: ewss->saved.x = ewss->surface->geometry.x; ewss->saved.y = ewss->surface->geometry.y; ewss->saved.w = ewss->surface->geometry.w; ewss->saved.h = ewss->surface->geometry.h; ewss->saved.valid = EINA_TRUE; break; default: break; } } static void _e_wl_shell_shell_surface_type_reset(E_Wayland_Shell_Surface *ewss) { /* safety check */ if (!ewss) return; switch (ewss->type) { case E_WAYLAND_SHELL_SURFACE_TYPE_FULLSCREEN: /* ecore_evas_fullscreen_set(ewss->surface->ee, EINA_FALSE); */ /* unfullscreen the border */ if (ewss->surface->bd) e_border_unfullscreen(ewss->surface->bd); /* restore the saved geometry */ ewss->surface->geometry.x = ewss->saved.x; ewss->surface->geometry.y = ewss->saved.y; ewss->surface->geometry.w = ewss->saved.w; ewss->surface->geometry.h = ewss->saved.h; ewss->surface->geometry.changed = EINA_TRUE; break; case E_WAYLAND_SHELL_SURFACE_TYPE_MAXIMIZED: /* unmaximize the border */ if (ewss->surface->bd) e_border_unmaximize(ewss->surface->bd, (e_config->maximize_policy & E_MAXIMIZE_TYPE) | E_MAXIMIZE_BOTH); /* restore the saved geometry */ ewss->surface->geometry.x = ewss->saved.x; ewss->surface->geometry.y = ewss->saved.y; ewss->surface->geometry.w = ewss->saved.w; ewss->surface->geometry.h = ewss->saved.h; ewss->surface->geometry.changed = EINA_TRUE; break; default: break; } /* reset the current type to none */ ewss->type = E_WAYLAND_SHELL_SURFACE_TYPE_NONE; } static void _e_wl_shell_shell_surface_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED) { E_Wayland_Shell_Surface *ewss = NULL; /* try to get the shell surface from the listener */ ewss = container_of(listener, E_Wayland_Shell_Surface, wl.surface_destroy); if (!ewss) return; if (ewss->wl.resource) wl_resource_destroy(ewss->wl.resource); else wl_signal_emit(&ewss->wl.destroy_signal, ewss->wl.resource); } static int _e_wl_shell_shell_surface_cb_ping_timeout(void *data) { E_Wayland_Shell_Surface *ewss = NULL; E_Wayland_Shell_Grab *grab = NULL; /* try to cast to our shell surface structure */ if (!(ewss = data)) return 1; ewss->active = EINA_FALSE; /* TODO: this should loop inputs */ /* try to allocate space for our grab structure */ if (!(grab = E_NEW(E_Wayland_Shell_Grab, 1))) return 1; /* set grab properties */ grab->x = ewss->surface->input->wl.seat.pointer->grab_x; grab->y = ewss->surface->input->wl.seat.pointer->grab_y; /* set busy cursor */ _e_wl_shell_grab_start(grab, ewss, ewss->surface->input->wl.seat.pointer, &_e_busy_grab_interface, E_DESKTOP_SHELL_CURSOR_BUSY); return 1; } static void _e_wl_shell_shell_surface_cb_ee_resize(Ecore_Evas *ee) { E_Wayland_Surface *ews = NULL; /* try to get the surface structure of this ecore_evas */ if (!(ews = ecore_evas_data_get(ee, "surface"))) return; /* if we have the surface smart object */ if (ews->obj) { int w = 0, h = 0; /* grab the requested geometry */ ecore_evas_request_geometry_get(ee, NULL, NULL, &w, &h); /* resize the surface smart object */ evas_object_resize(ews->obj, w, h); } } static void _e_wl_shell_shell_surface_cb_render_post(void *data, Evas *evas EINA_UNUSED, void *event EINA_UNUSED) { E_Wayland_Surface *ews = NULL; unsigned int secs = 0; E_Wayland_Surface_Frame_Callback *cb = NULL, *ncb = NULL; /* try to cast data to our surface structure */ if (!(ews = data)) return; /* grab the current server time */ secs = e_comp_wl_time_get(); /* for each frame callback in the surface, signal it is done */ wl_list_for_each_safe(cb, ncb, &ews->wl.frames, wl.link) { wl_callback_send_done(cb->wl.resource, secs); wl_resource_destroy(cb->wl.resource); } } static void _e_wl_shell_shell_surface_cb_focus_in(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) { E_Wayland_Surface *ews = NULL; E_Wayland_Input *input = NULL; Eina_List *l = NULL; /* try to cast data to our surface structure */ if (!(ews = data)) return; /* if this surface is not visible, get out */ if (!ews->mapped) return; /* loop the list of inputs */ EINA_LIST_FOREACH(_e_wl_comp->seats, l, input) { /* set keyboard focus */ wl_keyboard_set_focus(input->wl.seat.keyboard, ews->wl.surface); /* update the keyboard focus in the data device */ wl_data_device_set_keyboard_focus(&input->wl.seat); } } static void _e_wl_shell_shell_surface_cb_focus_out(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) { E_Wayland_Surface *ews = NULL; E_Wayland_Input *input = NULL; Eina_List *l = NULL; /* try to cast data to our surface structure */ if (!(ews = data)) return; /* if this surface is not visible, get out */ if (!ews->mapped) return; /* loop the list of inputs */ EINA_LIST_FOREACH(_e_wl_comp->seats, l, input) { /* set keyboard focus */ wl_keyboard_set_focus(input->wl.seat.keyboard, NULL); /* end any keyboard grabs */ wl_keyboard_end_grab(input->wl.seat.keyboard); } } static void _e_wl_shell_shell_surface_cb_mouse_in(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) { E_Wayland_Surface *ews = NULL; struct wl_pointer *ptr = NULL; /* try to cast data to our surface structure */ if (!(ews = data)) return; /* if this surface is not visible, get out */ if (!ews->mapped) return; /* try to get the pointer from this input */ if ((ptr = _e_wl_comp->input->wl.seat.pointer)) { /* if the mouse entered this surface & it is not the current surface */ if (ews->wl.surface != ptr->current) { const struct wl_pointer_grab_interface *grab; /* set this surface as the current */ grab = ptr->grab->interface; ptr->current = ews->wl.surface; /* send a pointer focus event */ grab->focus(ptr->grab, ews->wl.surface, ptr->current_x, ptr->current_y); } } } static void _e_wl_shell_shell_surface_cb_mouse_out(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) { E_Wayland_Surface *ews = NULL; struct wl_pointer *ptr = NULL; /* try to cast data to our surface structure */ if (!(ews = data)) return; /* if (!ews->input) return; */ /* try to get the pointer from this input */ if ((ptr = _e_wl_comp->input->wl.seat.pointer)) { /* if we have a pointer grab and this is the currently focused surface */ /* if ((ptr->grab) && (ptr->focus == ptr->current)) */ /* return; */ /* unset the pointer image */ ecore_x_window_cursor_set(ecore_evas_window_get(ews->ee), 0); /* set pointer focus */ ptr->current = NULL; /* NB: Ideally, we should call this function to tell the * pointer that nothing has focus, HOWEVER, when we do * it breaks re-entrant focus of some wayland clients: * * NB: I sent a patch for this already to the wayland devs */ wl_pointer_set_focus(ptr, NULL, 0, 0); } } static void _e_wl_shell_shell_surface_cb_mouse_move(void *data, Evas_Object *obj EINA_UNUSED, void *event) { E_Wayland_Surface *ews = NULL; Evas_Event_Mouse_Move *ev; struct wl_pointer *ptr = NULL; ev = event; /* try to cast data to our surface structure */ if (!(ews = data)) return; /* try to get the pointer from this input */ if ((ptr = _e_wl_comp->input->wl.seat.pointer)) { ptr->x = wl_fixed_from_int(ev->cur.output.x); ptr->y = wl_fixed_from_int(ev->cur.output.y); ptr->current_x = ptr->x; ptr->current_y = ptr->y; ptr->grab->x = ptr->x; ptr->grab->y = ptr->y; /* send this mouse movement to wayland */ ptr->grab->interface->motion(ptr->grab, ev->timestamp, ptr->grab->x, ptr->grab->y); } } static void _e_wl_shell_shell_surface_cb_mouse_up(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) { E_Wayland_Surface *ews = NULL; struct wl_pointer *ptr = NULL; Evas_Event_Mouse_Up *ev; int btn = 0; ev = event; /* try to cast data to our surface structure */ if (!(ews = data)) return; /* try to get the pointer from this input */ if ((ptr = _e_wl_comp->input->wl.seat.pointer)) { if (ev->button == 1) btn = ev->button + BTN_LEFT - 1; // BTN_LEFT else if (ev->button == 2) btn = BTN_MIDDLE; else if (ev->button == 3) btn = BTN_RIGHT; ptr->button_count--; /* send this button press to the pointer */ ptr->grab->interface->button(ptr->grab, ev->timestamp, btn, WL_POINTER_BUTTON_STATE_RELEASED); if (ptr->button_count == 1) ptr->grab_serial = wl_display_get_serial(_e_wl_comp->wl.display); } } static void _e_wl_shell_shell_surface_cb_mouse_down(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) { E_Wayland_Surface *ews = NULL; struct wl_pointer *ptr = NULL; Evas_Event_Mouse_Down *ev; int btn = 0; ev = event; /* try to cast data to our surface structure */ if (!(ews = data)) return; /* try to get the pointer from this input */ if ((ptr = _e_wl_comp->input->wl.seat.pointer)) { unsigned int serial = 0; if (ev->button == 1) btn = ev->button + BTN_LEFT - 1; // BTN_LEFT else if (ev->button == 2) btn = BTN_MIDDLE; else if (ev->button == 3) btn = BTN_RIGHT; serial = wl_display_next_serial(_e_wl_comp->wl.display); /* if the compositor has a ping callback, call it on this surface */ if (_e_wl_comp->ping_cb) _e_wl_comp->ping_cb(ews, serial); /* update some pointer properties */ if (ptr->button_count == 0) { ptr->x = wl_fixed_from_int(ev->output.x); ptr->y = wl_fixed_from_int(ev->output.y); ptr->grab_x = ptr->x; ptr->grab_y = ptr->y; ptr->grab_button = btn; ptr->grab_time = ev->timestamp; } ptr->button_count++; /* send this button press to the pointer */ ptr->grab->interface->button(ptr->grab, ev->timestamp, btn, WL_POINTER_BUTTON_STATE_PRESSED); if (ptr->button_count == 1) ptr->grab_serial = wl_display_get_serial(_e_wl_comp->wl.display); } } static void _e_wl_shell_shell_surface_cb_key_up(void *data, Evas_Object *obj EINA_UNUSED, void *event) { Evas_Event_Key_Up *ev; E_Wayland_Surface *ews = NULL; struct wl_keyboard *kbd; struct wl_keyboard_grab *grab; unsigned int key = 0, *end, *k; unsigned int serial = 0; ev = event; /* try to cast data to our surface structure */ if (!(ews = data)) return; /* try to get a reference to the input's keyboard */ if (!(kbd = _e_wl_comp->input->wl.seat.keyboard)) return; /* does this keyboard have a focused surface ? */ if (!kbd->focus) return; /* is the focused surface actually This surface ? */ if (kbd->focus != ews->wl.surface) return; /* get the keycode for this key from X */ key = ecore_x_keysym_keycode_get(ev->keyname) - 8; end = (kbd->keys.data + kbd->keys.size); for (k = kbd->keys.data; k < end; k++) if ((*k == key)) *k = *--end; kbd->keys.size = (void *)end - kbd->keys.data; /* try to get the current keyboard's grab interface. * Fallback to the default grab */ if (!(grab = kbd->grab)) grab = &kbd->default_grab; /* if we have a grab, send this key to it */ if (grab) grab->interface->key(grab, ev->timestamp, key, WL_KEYBOARD_KEY_STATE_RELEASED); /* update xkb key state */ xkb_state_update_key(_e_wl_comp->input->xkb.state, key + 8, XKB_KEY_DOWN); /* update keyboard modifiers */ serial = wl_display_get_serial(_e_wl_comp->wl.display); e_comp_wl_input_modifiers_update(serial); } static void _e_wl_shell_shell_surface_cb_key_down(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) { Evas_Event_Key_Down *ev; E_Wayland_Surface *ews = NULL; struct wl_keyboard *kbd; struct wl_keyboard_grab *grab; unsigned int serial = 0, key = 0; unsigned int *end, *k; ev = event; /* try to cast data to our surface structure */ if (!(ews = data)) return; /* try to get a reference to the input's keyboard */ if (!(kbd = _e_wl_comp->input->wl.seat.keyboard)) return; /* does this keyboard have a focused surface ? */ if (!kbd->focus) return; /* is the focused surface actually This surface ? */ if (kbd->focus != ews->wl.surface) return; serial = wl_display_next_serial(_e_wl_comp->wl.display); /* if the compositor has a ping callback, call it on this surface */ if (_e_wl_comp->ping_cb) _e_wl_comp->ping_cb(ews, serial); /* get the keycode for this key from X */ key = ecore_x_keysym_keycode_get(ev->keyname) - 8; /* update the keyboards grab properties */ kbd->grab_key = key; kbd->grab_time = ev->timestamp; end = (kbd->keys.data + kbd->keys.size); for (k = kbd->keys.data; k < end; k++) { /* ignore server generated key repeats */ if ((*k == key)) return; } kbd->keys.size = (void *)end - kbd->keys.data; k = wl_array_add(&kbd->keys, sizeof(*k)); *k = key; /* try to get the current keyboard's grab interface. * Fallback to the default grab */ if (!(grab = kbd->grab)) grab = &kbd->default_grab; /* if we have a grab, send this key to it */ if (grab) grab->interface->key(grab, ev->timestamp, key, WL_KEYBOARD_KEY_STATE_PRESSED); /* update xkb key state */ xkb_state_update_key(_e_wl_comp->input->xkb.state, key + 8, XKB_KEY_DOWN); /* update keyboard modifiers */ serial = wl_display_get_serial(_e_wl_comp->wl.display); e_comp_wl_input_modifiers_update(serial); } static void _e_wl_shell_shell_surface_cb_bd_move_end(void *data, void *bd) { E_Wayland_Surface *ews = NULL; struct wl_pointer *ptr = NULL; E_Border *border = NULL; /* try to cast data to our surface structure */ if (!(ews = data)) return; border = bd; if (border != ews->bd) return; /* try to get the pointer from this input */ if ((ptr = _e_wl_comp->input->wl.seat.pointer)) { ptr->button_count--; /* send this button press to the pointer */ ptr->grab->interface->button(ptr->grab, ptr->grab_time, ptr->grab_button, WL_POINTER_BUTTON_STATE_RELEASED); if (ptr->button_count == 1) ptr->grab_serial = wl_display_get_serial(_e_wl_comp->wl.display); } } static void _e_wl_shell_shell_surface_cb_bd_resize_update(void *data, void *bd) { E_Wayland_Shell_Grab *grab = NULL; E_Wayland_Surface *ews = NULL; struct wl_pointer *ptr = NULL; E_Border *border = NULL; /* try to cast data to our surface structure */ if (!(ews = data)) return; border = bd; if (border != ews->bd) return; /* try to get the pointer from this input */ if ((ptr = _e_wl_comp->input->wl.seat.pointer)) { Evas_Coord w = 0, h = 0; if (!(grab = (E_Wayland_Shell_Grab *)ptr->grab)) return; w = border->w; h = border->h; wl_shell_surface_send_configure(ews->shell_surface->wl.resource, grab->edges, w, h); } } static void _e_wl_shell_shell_surface_cb_bd_resize_end(void *data, void *bd) { E_Wayland_Surface *ews = NULL; struct wl_pointer *ptr = NULL; E_Border *border = NULL; /* try to cast data to our surface structure */ if (!(ews = data)) return; border = bd; if (border != ews->bd) return; /* try to get the pointer from this input */ if ((ptr = _e_wl_comp->input->wl.seat.pointer)) { ptr->button_count--; /* send this button press to the pointer */ ptr->grab->interface->button(ptr->grab, ptr->grab_time, ptr->grab_button, WL_POINTER_BUTTON_STATE_RELEASED); if (ptr->button_count == 1) ptr->grab_serial = wl_display_get_serial(_e_wl_comp->wl.display); } } /* shell surface interface functions */ static void _e_wl_shell_shell_surface_cb_pong(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, unsigned int serial) { E_Wayland_Shell_Surface *ewss = NULL; E_Wayland_Ping_Timer *tmr = NULL; Eina_Bool responsive = EINA_FALSE; /* try to cast the resource to our shell surface */ if (!(ewss = wl_resource_get_user_data(resource))) return; /* try to cast the ping timer */ if (!(tmr = (E_Wayland_Ping_Timer *)ewss->ping_timer)) return; if (tmr->serial != serial) return; responsive = ewss->active; ewss->active = EINA_TRUE; if (!responsive) { E_Wayland_Surface *ews = NULL; E_Wayland_Shell_Grab *grab = NULL; ews = ewss->surface; grab = (E_Wayland_Shell_Grab *)ews->input->wl.seat.pointer->grab; if (grab->grab.interface == &_e_busy_grab_interface) { _e_wl_shell_grab_end(grab); free(ews->input->wl.seat.pointer->grab); } } if (tmr->source) wl_event_source_remove(tmr->source); /* free the allocated space for ping timer */ E_FREE(tmr); ewss->ping_timer = NULL; } static void _e_wl_shell_mouse_down_helper(E_Border *bd, int button, Evas_Point *output, E_Binding_Event_Mouse_Button *ev, Eina_Bool move) { if ((button >= 1) && (button <= 3)) { bd->mouse.last_down[button - 1].mx = output->x; bd->mouse.last_down[button - 1].my = output->y; bd->mouse.last_down[button - 1].x = bd->x; bd->mouse.last_down[button - 1].y = bd->y; bd->mouse.last_down[button - 1].w = bd->w; bd->mouse.last_down[button - 1].h = bd->h; } else { bd->moveinfo.down.x = bd->x; bd->moveinfo.down.y = bd->y; bd->moveinfo.down.w = bd->w; bd->moveinfo.down.h = bd->h; } bd->mouse.current.mx = output->x; bd->mouse.current.my = output->y; if (move) { /* tell E to start moving the border */ e_border_act_move_begin(bd, ev); /* we have to get a reference to the window_move action here, or else * when e_border stops the move we will never get notified */ bd->cur_mouse_action = e_action_find("window_move"); if (bd->cur_mouse_action) e_object_ref(E_OBJECT(bd->cur_mouse_action)); } else { /* tell E to start resizing the border */ e_border_act_resize_begin(bd, ev); /* we have to get a reference to the window_resize action here, * or else when e_border stops the resize we will never get notified */ bd->cur_mouse_action = e_action_find("window_resize"); if (bd->cur_mouse_action) e_object_ref(E_OBJECT(bd->cur_mouse_action)); } e_focus_event_mouse_down(bd); if ((button >= 1) && (button <= 3)) { bd->mouse.last_down[button - 1].mx = output->x; bd->mouse.last_down[button - 1].my = output->y; bd->mouse.last_down[button - 1].x = bd->x; bd->mouse.last_down[button - 1].y = bd->y; bd->mouse.last_down[button - 1].w = bd->w; bd->mouse.last_down[button - 1].h = bd->h; } else { bd->moveinfo.down.x = bd->x; bd->moveinfo.down.y = bd->y; bd->moveinfo.down.w = bd->w; bd->moveinfo.down.h = bd->h; } bd->mouse.current.mx = output->x; bd->mouse.current.my = output->y; } static void _e_wl_shell_mouse_up_helper(E_Border *bd, int button, Evas_Point *output, E_Binding_Event_Mouse_Button *ev EINA_UNUSED) { if ((button >= 1) && (button <= 3)) { bd->mouse.last_up[button - 1].mx = output->x; bd->mouse.last_up[button - 1].my = output->y; bd->mouse.last_up[button - 1].x = bd->x; bd->mouse.last_up[button - 1].y = bd->y; } bd->mouse.current.mx = output->x; bd->mouse.current.my = output->y; if ((button >= 1) && (button <= 3)) { bd->mouse.last_up[button - 1].mx = output->x; bd->mouse.last_up[button - 1].my = output->y; bd->mouse.last_up[button - 1].x = bd->x; bd->mouse.last_up[button - 1].y = bd->y; } bd->drag.start = 0; } static void _e_wl_shell_shell_surface_cb_move(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource, unsigned int serial) { E_Wayland_Input *input = NULL; E_Wayland_Shell_Surface *ewss = NULL; E_Wayland_Shell_Grab *grab = NULL; E_Binding_Event_Mouse_Button *ev; struct wl_pointer *ptr = NULL; /* try to cast the seat resource to our input structure */ if (!(input = wl_resource_get_user_data(seat_resource))) return; /* try to cast the resource to our shell surface */ if (!(ewss = wl_resource_get_user_data(resource))) return; /* if the shell surface is fullscreen, get out */ if (ewss->type == E_WAYLAND_SHELL_SURFACE_TYPE_FULLSCREEN) return; /* check for valid move setup */ if ((input->wl.seat.pointer->button_count == 0) || (input->wl.seat.pointer->grab_serial != serial) || (input->wl.seat.pointer->focus != ewss->surface->wl.surface)) return; /* try to allocate space for our grab structure */ if (!(grab = E_NEW(E_Wayland_Shell_Grab, 1))) return; /* try to get the pointer from this input */ ptr = _e_wl_comp->input->wl.seat.pointer; /* set grab properties */ grab->x = ptr->grab_x; grab->y = ptr->grab_y; /* start the movement grab */ _e_wl_shell_grab_start(grab, ewss, input->wl.seat.pointer, &_e_move_grab_interface, E_DESKTOP_SHELL_CURSOR_MOVE); /* create a fake binding event for mouse button */ ev = E_NEW(E_Binding_Event_Mouse_Button, 1); /* set button property of the binding event */ if (grab->pointer->grab_button == BTN_LEFT) ev->button = 1; else if (grab->pointer->grab_button == BTN_MIDDLE) ev->button = 2; else if (grab->pointer->grab_button == BTN_RIGHT) ev->button = 3; /* set the clicked location in the binding event */ ev->canvas.x = wl_fixed_to_int(ptr->x) + (ewss->surface->bd->x + ewss->surface->bd->client_inset.l); ev->canvas.y = wl_fixed_to_int(ptr->y) + (ewss->surface->bd->y + ewss->surface->bd->client_inset.t); /* call our helper function to initiate a move */ _e_wl_shell_mouse_down_helper(ewss->surface->bd, ev->button, &(Evas_Point){ev->canvas.x, ev->canvas.y}, ev, EINA_TRUE); } static void _e_wl_shell_shell_surface_cb_resize(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource, unsigned int serial, unsigned int edges) { E_Wayland_Input *input = NULL; E_Wayland_Shell_Surface *ewss = NULL; E_Wayland_Shell_Grab *grab = NULL; E_Binding_Event_Mouse_Button *ev; struct wl_pointer *ptr = NULL; /* try to cast the seat resource to our input structure */ if (!(input = wl_resource_get_user_data(seat_resource))) return; /* try to cast the resource to our shell surface */ if (!(ewss = wl_resource_get_user_data(resource))) return; /* if the shell surface is fullscreen or maximized, get out */ if ((ewss->type == E_WAYLAND_SHELL_SURFACE_TYPE_FULLSCREEN) || (ewss->type == E_WAYLAND_SHELL_SURFACE_TYPE_MAXIMIZED)) return; /* check for valid move setup */ if ((input->wl.seat.pointer->button_count == 0) || (input->wl.seat.pointer->grab_serial != serial) || (input->wl.seat.pointer->focus != ewss->surface->wl.surface)) return; if ((edges == 0) || (edges > 15) || ((edges & 3) == 3) || ((edges & 12) == 12)) return; /* try to allocate space for our grab structure */ if (!(grab = E_NEW(E_Wayland_Shell_Grab, 1))) return; /* try to get the pointer from this input */ ptr = _e_wl_comp->input->wl.seat.pointer; /* set grab properties */ grab->edges = edges; grab->w = ewss->surface->geometry.w; grab->h = ewss->surface->geometry.h; /* start the resize grab */ _e_wl_shell_grab_start(grab, ewss, input->wl.seat.pointer, &_e_resize_grab_interface, edges); /* create a fake binding event for mouse button */ ev = E_NEW(E_Binding_Event_Mouse_Button, 1); if (grab->pointer->grab_button == BTN_LEFT) ev->button = 1; else if (grab->pointer->grab_button == BTN_MIDDLE) ev->button = 2; else if (grab->pointer->grab_button == BTN_RIGHT) ev->button = 3; /* set the clicked location in the binding event */ ev->canvas.x = wl_fixed_to_int(ptr->x) + (ewss->surface->bd->x + ewss->surface->bd->client_inset.l); ev->canvas.y = wl_fixed_to_int(ptr->y) + (ewss->surface->bd->y + ewss->surface->bd->client_inset.t); /* call our helper function to initiate a resize */ _e_wl_shell_mouse_down_helper(ewss->surface->bd, ev->button, &(Evas_Point){ev->canvas.x, ev->canvas.y}, ev, EINA_FALSE); } static void _e_wl_shell_shell_surface_cb_toplevel_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) { E_Wayland_Shell_Surface *ewss = NULL; /* try to cast the resource to our shell surface */ if (!(ewss = wl_resource_get_user_data(resource))) return; /* set next surface type */ ewss->next_type = E_WAYLAND_SHELL_SURFACE_TYPE_TOPLEVEL; } static void _e_wl_shell_shell_surface_cb_transient_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *parent_resource, int x, int y, unsigned int flags) { E_Wayland_Shell_Surface *ewss = NULL; E_Wayland_Surface *ews = NULL; /* try to cast the resource to our shell surface */ if (!(ewss = wl_resource_get_user_data(resource))) return; ews = wl_resource_get_user_data(parent_resource); ewss->parent = ews; ewss->transient.x = x; ewss->transient.y = y; ewss->transient.flags = flags; /* set next surface type */ ewss->next_type = E_WAYLAND_SHELL_SURFACE_TYPE_TRANSIENT; } static void _e_wl_shell_shell_surface_cb_fullscreen_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, unsigned int method EINA_UNUSED, unsigned int framerate EINA_UNUSED, struct wl_resource *output_resource EINA_UNUSED) { E_Wayland_Shell_Surface *ewss = NULL; /* NB FIXME: Disable fullscreen support for right now. * * Appears to be some recent issue with e_border... * * Needs looking into */ return; /* try to cast the resource to our shell surface */ if (!(ewss = wl_resource_get_user_data(resource))) return; /* set next surface type */ ewss->next_type = E_WAYLAND_SHELL_SURFACE_TYPE_TOPLEVEL; /* check for valid border */ if (ewss->surface->bd) { /* ecore_evas_fullscreen_set(ewss->surface->ee, EINA_TRUE); */ /* tell E to fullscreen this window */ e_border_fullscreen(ewss->surface->bd, E_FULLSCREEN_RESIZE); /* send configure message to the shell surface to inform of new size */ wl_shell_surface_send_configure(ewss->wl.resource, 0, ewss->surface->bd->w, ewss->surface->bd->h); } } static void _e_wl_shell_shell_surface_cb_popup_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource, unsigned int serial, struct wl_resource *parent_resource, int x, int y, unsigned int flags EINA_UNUSED) { E_Wayland_Shell_Surface *ewss = NULL; E_Wayland_Input *input = NULL; /* try to cast the resource to our shell surface */ if (!(ewss = wl_resource_get_user_data(resource))) return; /* cast the seat resource to our input structure */ input = wl_resource_get_user_data(seat_resource); /* set surface type */ ewss->type = E_WAYLAND_SHELL_SURFACE_TYPE_POPUP; /* set surface popup properties */ ewss->parent = wl_resource_get_user_data(parent_resource); ewss->popup.seat = &input->wl.seat; ewss->popup.serial = serial; ewss->popup.x = x; ewss->popup.y = y; } static void _e_wl_shell_shell_surface_cb_maximized_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *output_resource EINA_UNUSED) { E_Wayland_Shell_Surface *ewss = NULL; /* try to cast the resource to our shell surface */ if (!(ewss = wl_resource_get_user_data(resource))) return; /* set next surface type */ ewss->next_type = E_WAYLAND_SHELL_SURFACE_TYPE_MAXIMIZED; /* check for valid border */ if (ewss->surface->bd) { unsigned int edges = 0; edges = (WL_SHELL_SURFACE_RESIZE_TOP | WL_SHELL_SURFACE_RESIZE_LEFT); /* tell E to maximize this window */ e_border_maximize(ewss->surface->bd, (e_config->maximize_policy & E_MAXIMIZE_TYPE) | E_MAXIMIZE_BOTH); /* send configure message to the shell surface to inform of new size */ wl_shell_surface_send_configure(ewss->wl.resource, edges, ewss->surface->bd->w, ewss->surface->bd->h); } } static void _e_wl_shell_shell_surface_cb_title_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *title) { E_Wayland_Shell_Surface *ewss = NULL; E_Wayland_Surface *ews = NULL; /* try to cast the resource to our shell surface */ if (!(ewss = wl_resource_get_user_data(resource))) return; /* free any previous title */ free(ewss->title); /* set new title */ ewss->title = strdup(title); /* update ecore_evas with new title */ if ((ews = ewss->surface)) { if (ews->ee) ecore_evas_title_set(ews->ee, ewss->title); } } static void _e_wl_shell_shell_surface_cb_class_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *clas) { E_Wayland_Shell_Surface *ewss = NULL; E_Wayland_Surface *ews = NULL; /* try to cast the resource to our shell surface */ if (!(ewss = wl_resource_get_user_data(resource))) return; /* free any previous class */ free(ewss->clas); /* set new class */ ewss->clas = strdup(clas); /* update ecore_evas with new class */ if ((ews = ewss->surface)) { if (ews->ee) ecore_evas_name_class_set(ews->ee, ewss->title, ewss->clas); } } /* shell move_grab interface functions */ static void _e_wl_shell_move_grab_cb_focus(struct wl_pointer_grab *grab, struct wl_resource *surface EINA_UNUSED, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED) { /* safety */ if (!grab) return; /* remove focus */ grab->focus = NULL; } static void _e_wl_shell_move_grab_cb_motion(struct wl_pointer_grab *grab EINA_UNUSED, unsigned int timestamp EINA_UNUSED, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED) { /* FIXME: This needs to become a no-op as the actual surface movement * is handled by e_border now */ } static void _e_wl_shell_move_grab_cb_button(struct wl_pointer_grab *grab, unsigned int timestamp EINA_UNUSED, unsigned int button EINA_UNUSED, unsigned int state) { E_Wayland_Shell_Grab *ewsg = NULL; struct wl_pointer *ptr; /* safety */ if (!grab) return; /* try to get the shell grab from the pointer grab */ if (!(ewsg = container_of(grab, E_Wayland_Shell_Grab, grab))) return; /* try to get the pointer */ if (!(ptr = grab->pointer)) return; /* test if we are done with the grab */ if ((ptr->button_count == 0) && (state == WL_POINTER_BUTTON_STATE_RELEASED)) { E_Wayland_Surface *ews = NULL; E_Binding_Event_Mouse_Button *ev; if (!(ews = ewsg->shell_surface->surface)) return; /* create a fake binding event for mouse button */ ev = E_NEW(E_Binding_Event_Mouse_Button, 1); /* set button property of the binding event */ if (ptr->grab_button == BTN_LEFT) ev->button = 1; else if (ptr->grab_button == BTN_MIDDLE) ev->button = 2; else if (ptr->grab_button == BTN_RIGHT) ev->button = 3; /* set the clicked location in the binding event */ ev->canvas.x = wl_fixed_to_int(ptr->x) + (ews->bd->x + ews->bd->client_inset.l); ev->canvas.y = wl_fixed_to_int(ptr->y) + (ews->bd->y + ews->bd->client_inset.t); /* call our helper function to end a move */ _e_wl_shell_mouse_up_helper(ews->bd, ev->button, &(Evas_Point){ev->canvas.x, ev->canvas.y}, ev); /* end the grab */ _e_wl_shell_grab_end(ewsg); free(grab); /* set surface geometry */ _e_wl_shell_shell_surface_configure(ews, ews->bd->x, ews->bd->y, ews->geometry.w, ews->geometry.h); } } /* shell resize_grab interface functions */ static void _e_wl_shell_resize_grab_cb_focus(struct wl_pointer_grab *grab, struct wl_resource *surface EINA_UNUSED, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED) { /* safety */ if (!grab) return; /* remove focus */ grab->focus = NULL; } static void _e_wl_shell_resize_grab_cb_motion(struct wl_pointer_grab *grab EINA_UNUSED, unsigned int timestamp EINA_UNUSED, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED) { /* FIXME: This needs to become a no-op as the actual surface resize * is handled by e_border now */ } static void _e_wl_shell_resize_grab_cb_button(struct wl_pointer_grab *grab, unsigned int timestamp EINA_UNUSED, unsigned int button EINA_UNUSED, unsigned int state) { E_Wayland_Shell_Grab *ewsg = NULL; struct wl_pointer *ptr; /* safety */ if (!grab) return; /* try to get the shell grab from the pointer grab */ if (!(ewsg = container_of(grab, E_Wayland_Shell_Grab, grab))) return; /* try to get the pointer */ if (!(ptr = grab->pointer)) return; /* test if we are done with the grab */ if ((ptr->button_count == 0) && (state == WL_POINTER_BUTTON_STATE_RELEASED)) { E_Wayland_Surface *ews = NULL; E_Binding_Event_Mouse_Button *ev; if (!(ews = ewsg->shell_surface->surface)) return; /* create a fake binding event for mouse button */ ev = E_NEW(E_Binding_Event_Mouse_Button, 1); /* set button property of the binding event */ if (ptr->grab_button == BTN_LEFT) ev->button = 1; else if (ptr->grab_button == BTN_MIDDLE) ev->button = 2; else if (ptr->grab_button == BTN_RIGHT) ev->button = 3; /* set the clicked location in the binding event */ ev->canvas.x = wl_fixed_to_int(ptr->x) + (ews->bd->x + ews->bd->client_inset.l); ev->canvas.y = wl_fixed_to_int(ptr->y) + (ews->bd->y + ews->bd->client_inset.t); /* call our helper function to end a move */ _e_wl_shell_mouse_up_helper(ews->bd, ev->button, &(Evas_Point){ev->canvas.x, ev->canvas.y}, ev); /* end the grab */ _e_wl_shell_grab_end(ewsg); free(grab); /* set surface geometry */ _e_wl_shell_shell_surface_configure(ews, ews->bd->x, ews->bd->y, ews->geometry.w, ews->geometry.h); } } /* shell popup_grab interface prototypes */ static void _e_wl_shell_popup_grab_cb_focus(struct wl_pointer_grab *grab, struct wl_resource *surface, wl_fixed_t x, wl_fixed_t y) { E_Wayland_Shell_Surface *ewss; struct wl_pointer *ptr; struct wl_client *client; /* try to get the pointer */ if (!(ptr = grab->pointer)) return; /* try to get the surface from the pointer grab */ ewss = container_of(grab, E_Wayland_Shell_Surface, popup.grab); if (!ewss) return; client = wl_resource_get_client(ewss->surface->wl.surface); if ((surface) && (surface->client == client)) { wl_pointer_set_focus(ptr, surface, x, y); grab->focus = surface; } else { wl_pointer_set_focus(ptr, NULL, 0, 0); grab->focus = NULL; } } static void _e_wl_shell_popup_grab_cb_motion(struct wl_pointer_grab *grab, unsigned int timestamp, wl_fixed_t x, wl_fixed_t y) { struct wl_resource *res; if ((res = grab->pointer->focus_resource)) wl_pointer_send_motion(res, timestamp, x, y); } static void _e_wl_shell_popup_grab_cb_button(struct wl_pointer_grab *grab, unsigned int timestamp, unsigned int button, unsigned int state) { E_Wayland_Shell_Surface *ewss = NULL; struct wl_resource *res; /* try to get the shell surface */ ewss = container_of(grab, E_Wayland_Shell_Surface, popup.grab); if (!ewss) return; if ((res = grab->pointer->focus_resource)) { struct wl_display *disp; unsigned int serial; disp = wl_client_get_display(res->client); serial = wl_display_get_serial(disp); wl_pointer_send_button(res, serial, timestamp, button, state); } else if ((state = WL_POINTER_BUTTON_STATE_RELEASED) && ((ewss->popup.up) || (timestamp - ewss->popup.seat->pointer->grab_time > 500))) { /* end the popup grab */ _e_wl_shell_popup_grab_end(grab->pointer); } if (state == WL_POINTER_BUTTON_STATE_RELEASED) ewss->popup.up = EINA_TRUE; } /* shell popup functions */ static void _e_wl_shell_popup_grab_end(struct wl_pointer *pointer) { E_Wayland_Shell_Surface *ewss = NULL; struct wl_pointer_grab *grab; grab = pointer->grab; /* try to get the shell surface from this grab */ ewss = container_of(grab, E_Wayland_Shell_Surface, popup.grab); if (!ewss) return; if (pointer->grab->interface == &_e_popup_grab_interface) { wl_shell_surface_send_popup_done(ewss->wl.resource); wl_pointer_end_grab(grab->pointer); ewss->popup.grab.pointer = NULL; } } /* shell busy_grab interface functions */ static void _e_wl_shell_busy_grab_cb_focus(struct wl_pointer_grab *grab, struct wl_resource *surface, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED) { E_Wayland_Shell_Grab *ewsg = NULL; /* try to cast the pointer grab to our structure */ if (!(ewsg = (E_Wayland_Shell_Grab *)grab)) return; /* if the grab's focus is not this surface, then end the grab */ if ((ewsg->grab.focus != surface)) { /* end the grab */ _e_wl_shell_grab_end(ewsg); free(grab); } } static void _e_wl_shell_busy_grab_cb_motion(struct wl_pointer_grab *grab EINA_UNUSED, unsigned int timestamp EINA_UNUSED, wl_fixed_t x EINA_UNUSED, wl_fixed_t y EINA_UNUSED) { /* no-op */ } static void _e_wl_shell_busy_grab_cb_button(struct wl_pointer_grab *grab, unsigned int timestamp EINA_UNUSED, unsigned int button, unsigned int state) { E_Wayland_Shell_Grab *ewsg = NULL; E_Wayland_Shell_Surface *ewss = NULL; E_Wayland_Input *input = NULL; /* try to cast the pointer grab to our structure */ if (!(ewsg = (E_Wayland_Shell_Grab *)grab)) return; /* try to get the current shell surface */ if (!(ewss = ewsg->shell_surface)) return; /* if (!(ews = (E_Wayland_Surface *)ewsg->grab.pointer->current)) return; */ /* try to cast the pointer seat to our input structure */ if (!(input = (E_Wayland_Input *)ewsg->grab.pointer->seat)) return; if ((ewss) && (button == 1) && (state)) { E_Wayland_Shell_Grab *mgrab = NULL; /* try to allocate space for our grab structure */ if (!(mgrab = E_NEW(E_Wayland_Shell_Grab, 1))) return; /* set grab properties */ mgrab->x = input->wl.seat.pointer->grab_x; mgrab->y = input->wl.seat.pointer->grab_y; /* start the movement grab */ _e_wl_shell_grab_start(mgrab, ewss, input->wl.seat.pointer, &_e_busy_grab_interface, E_DESKTOP_SHELL_CURSOR_MOVE); } }