efl/src/lib/efl_canvas_wl/efl_canvas_wl.c

5868 lines
176 KiB
C

#define EFL_INTERNAL_UNSTABLE
#define EFL_CANVAS_GROUP_PROTECTED
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if defined(__clang__)
# pragma clang diagnostic ignored "-Wunused-parameter"
#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4
# pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#include <unistd.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <dlfcn.h>
#include "Ecore_Evas.h"
#include "Ecore_Wl2.h"
#include "ecore_wl2_internal.h"
#include "Ecore_Input.h"
#include "Evas_GL.h"
/* We have to include the wayland server stuff after any wayland client stuff
* like Ecore_Wl2.h or we'll get complaints about struct wl_buffer being
* deprecated.
* That's because its deprecated in server code - it's still the name of
* the opaque struct client side.
*/
#include <wayland-server.h>
#include "xdg-shell-server-protocol.h"
#include "efl-hints-server-protocol.h"
#include "dmabuf.h"
# ifdef HAVE_ECORE_X
#include "Ecore_X.h"
#endif
#include "Evas_Internal.h"
#include "canvas/evas_canvas_eo.h"
#include "Efl_Canvas_Wl.h"
#undef COORDS_INSIDE
#define COORDS_INSIDE(x, y, xx, yy, ww, hh) \
(((x) < ((xx) + (ww))) && ((y) < ((yy) + (hh))) && ((x) >= (xx)) && ((y) >= (yy)))
#ifdef __linux__
# include <linux/input.h>
#else
# define BTN_LEFT 0x110
# define BTN_RIGHT 0x111
# define BTN_MIDDLE 0x112
# define BTN_SIDE 0x113
# define BTN_EXTRA 0x114
# define BTN_FORWARD 0x115
# define BTN_BACK 0x116
#endif
#undef container_of
# define container_of(ptr, type, member) \
({ \
const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
(type *)(void *)( (char *)__mptr - offsetof(type,member) ); \
})
#ifndef EGL_HEIGHT
# define EGL_HEIGHT 0x3056
#endif
#ifndef EGL_WIDTH
# define EGL_WIDTH 0x3057
#endif
#ifndef EGL_TEXTURE_FORMAT
# define EGL_TEXTURE_FORMAT 0x3080
#endif
#ifndef EGL_TEXTURE_RGBA
# define EGL_TEXTURE_RGBA 0x305E
#endif
#ifndef DRM_FORMAT_ARGB8888
# define DRM_FORMAT_ARGB8888 0x34325241
#endif
#ifndef DRM_FORMAT_XRGB8888
# define DRM_FORMAT_XRGB8888 0x34325258
#endif
#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \
WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \
WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
#define MY_CLASS EFL_CANVAS_WL_CLASS
typedef struct Input_Sequence
{
EINA_INLIST;
uint32_t id;
uint32_t down_serial;
uint32_t down_time;
uint32_t up_serial;
uint32_t up_time;
Eina_Bool pass : 1;
} Input_Sequence;
typedef struct Comp_Subsurface Comp_Subsurface;
typedef struct Comp_Surface Comp_Surface;
typedef struct Comp_Buffer
{
struct wl_resource *res;
Comp_Surface *cs;
Eina_List *renders;
Eina_List *post_renders;
int x, y, w, h;
struct wl_listener destroy_listener;
struct wl_shm_buffer *shm_buffer;
struct wl_shm_pool *pool;
struct linux_dmabuf_buffer *dmabuf_buffer;
Eina_Bool dbg : 1;
} Comp_Buffer;
typedef struct Comp
{
Efl_Canvas_Wl_Rotation rotation;
double scale;
char *env;
Efl_Exe_Flags flags;
Ecore_Wl2_Display *disp;
Ecore_Wl2_Display *parent_disp;
Ecore_Wl2_Display *client_disp;
struct wl_display *display;
double wayland_time_base;
Eo *obj;
Eo *clip;
Eo *events;
Eina_Hash *exes;
Eina_Inlist *surfaces;
unsigned int surfaces_count;
Eina_Hash *client_surfaces;
Comp_Surface *active_surface;
Eina_Inlist *shells;
Eina_List *render_queue;
Eina_List *post_render_queue;
Evas *evas;
Evas_GL *gl;
Evas_GL_Config *glcfg;
Evas_GL_Context *glctx;
Evas_GL_Surface *glsfc;
Evas_GL_API *glapi;
Eina_List *output_resources;
Eina_Inlist *seats;
Eina_Bool rendering : 1;
Eina_Bool data_device_proxy : 1;
Eina_Bool x11_selection : 1;
Eina_Bool rtl : 1;
Eina_Bool aspect : 1;
Eina_Bool minmax : 1;
} Comp;
typedef struct Comp_Data_Device_Source Comp_Data_Device_Source;
typedef struct Comp_Seat
{
EINA_INLIST;
Comp *c;
Eina_Stringshare *name;
struct wl_global *global;
Ecore_Wl2_Input *seat;
Ecore_Wl2_Input *client_seat;
Ecore_Wl2_Offer *client_offer;
uint32_t client_selection_serial;
Eo *dev;
Eina_List *resources;
Eina_Hash *data_devices;
struct
{
struct wl_resource *res;
Comp_Data_Device_Source *source;
Comp_Surface *surface;
Comp_Surface *enter;
uint32_t id;
Ecore_Evas *proxy_win;
Ecore_Window x11_owner;
Eina_List *x11_types;
Eina_Bool tch : 1;
} drag;
Comp_Data_Device_Source *selection_source;
uint32_t selection_serial;
Ecore_Window x11_selection_owner;
struct wl_client *active_client;
Comp_Surface *grab;
struct
{
struct wl_array keys;
struct wl_array *keys_external;
struct
{
xkb_mod_mask_t depressed;
xkb_mod_mask_t latched;
xkb_mod_mask_t locked;
xkb_layout_index_t group;
Eina_Bool changed : 1;
} mods;
struct xkb_context *context;
struct xkb_keymap *keymap;
struct xkb_state *state;
const char *keymap_str;
int keymap_str_size;
int repeat_rate;
int repeat_delay;
Eina_Hash *resources;
Comp_Surface *enter;
Eina_Bool external : 1;
} kbd;
struct
{
Eina_Hash *resources;
uint32_t button_mask;
uint32_t enter_serial;
Eina_Inlist *events;
Comp_Surface *enter;
struct
{
Comp_Surface *surface;
int x, y;
} cursor;
struct
{
Eo *obj;
int layer;
int x, y;
} efl;
Evas_Point pos;
Eina_Bool in : 1;
} ptr;
struct
{
Eina_Hash *resources;
Eina_Inlist *events;
Comp_Surface *enter;
Evas_Point pos;
} tch;
Eina_Bool pointer : 1;
Eina_Bool keyboard : 1;
Eina_Bool touch : 1;
Eina_Bool focused : 1;
Eina_Bool selection_changed : 1;
Eina_Bool selection_exists : 1;
Eina_Bool event_propagate : 1;
} Comp_Seat;
typedef struct Comp_Buffer_State
{
Comp_Buffer *buffer;
Eina_Tiler *opaque;
Eina_Tiler *damages;
Eina_Tiler *input;
Eina_List *frames;
Eina_Bool attach : 1;
Eina_Bool set_opaque : 1;
Eina_Bool set_input : 1;
} Comp_Buffer_State;
typedef struct Shell_Data
{
EINA_INLIST;
Comp *c;
struct wl_resource *res;
Eina_List *surfaces;
Eina_Inlist *positioners;
Eina_Bool ping : 1;
} Shell_Data;
typedef struct Shell_Positioner
{
EINA_INLIST;
Shell_Data *sd;
struct wl_resource *res;
Evas_Coord_Size size;
Eina_Rectangle anchor_rect;
enum xdg_positioner_anchor anchor;
enum xdg_positioner_gravity gravity;
enum xdg_positioner_constraint_adjustment constrain;
Evas_Coord_Point offset;
} Shell_Positioner;
struct Comp_Surface
{
EINA_INLIST;
Comp *c;
Eo *obj;
Eo *clip;
Eo *img;
Eina_Array *input_rects;
Eina_Array *opaque_rects;
Eina_List *proxies;
struct wl_resource *res;
struct wl_resource *role;
Comp_Seat *drag; //drag surface
Comp_Buffer *buffer[2]; // new, prev
/* subsurface stacking order */
Eina_List *subsurfaces;
Eina_List *pending_subsurfaces;
/* any child surface (xdg or subsurface */
Eina_Inlist *children;
Comp_Surface *parent;
Eina_Tiler *opaque;
Eina_Tiler *input;
Eina_List *frames;
Comp_Subsurface *subsurface;
Comp_Buffer_State pending;
struct
{
struct wl_resource *surface;
Eina_Rectangle geom;
Shell_Data *data;
Eina_Stringshare *title;
Eina_Stringshare *app_id;
Shell_Positioner *positioner;
Eina_List *grabs;
Eina_Bool popup : 1;
Eina_Bool new : 1;
Eina_Bool activated : 1;
} shell;
Eina_Bool mapped : 1;
Eina_Bool cursor : 1;
Eina_Bool render_queue : 1;
Eina_Bool post_render_queue : 1;
Eina_Bool dead : 1;
Eina_Bool commit : 1;
Eina_Bool extracted : 1;
Eina_Bool hint_set_weight : 1;
};
struct Comp_Subsurface
{
Comp_Surface *surface;
Comp_Buffer_State cache;
Evas_Point offset;
Evas_Point pending_offset;
Eina_Bool set_offset : 1;
Eina_Bool sync : 1;
Eina_Bool cached : 1;
};
typedef enum Comp_Data_Device_Offer_Type
{
COMP_DATA_DEVICE_OFFER_TYPE_DND,
COMP_DATA_DEVICE_OFFER_TYPE_CLIPBOARD,
} Comp_Data_Device_Offer_Type;
typedef struct Comp_Data_Device_Offer
{
Comp_Data_Device_Offer_Type type;
struct wl_resource *res;
Comp_Data_Device_Source *source;
Ecore_Wl2_Offer *proxy_offer;
uint32_t dnd_actions;
uint32_t preferred_dnd_action;
Eina_Bool in_ask : 1;
Eina_Bool proxy : 1;
} Comp_Data_Device_Offer;
typedef struct Comp_Data_Device_Source
{
struct wl_resource *res;
Comp_Seat *seat;
Comp_Data_Device_Offer *offer;
Ecore_Window x11_owner;
Eina_Inlist *transfers;
Eina_Binbuf *reader_data;
Ecore_Fd_Handler *reader;
Eina_List *mime_types;
uint32_t dnd_actions;
uint32_t current_dnd_action;
uint32_t compositor_action;
Ecore_Event_Handler *proxy_send_handler;
uint32_t proxy_serial;
Eina_Bool actions_set : 1;
Eina_Bool accepted : 1;
Eina_Bool proxy : 1;
} Comp_Data_Device_Source;
typedef struct Comp_Data_Device_Transfer
{
EINA_INLIST;
Comp_Data_Device_Offer_Type type;
Ecore_Fd_Handler *fdh;
size_t offset;
Eina_Stringshare *mime_type;
Comp_Data_Device_Source *source;
} Comp_Data_Device_Transfer;
static Eina_List *comps;
static Eina_List *handlers;
static inline Eina_Tiler *
tiler_new(void)
{
Eina_Tiler *t;
t = eina_tiler_new(65535, 65535);
eina_tiler_tile_size_set(t, 1, 1);
return t;
}
static inline void
fdh_del(Ecore_Fd_Handler *fdh)
{
int fd;
if (!fdh) return;
fd = ecore_main_fd_handler_fd_get(fdh);
if (fd >= 0)
close(fd);
ecore_main_fd_handler_del(fdh);
}
#define PTR_SWAP(A, B) ptr_swap((void**)A, (void**)B)
static inline void
ptr_swap(void **a, void **b)
{
void *c;
c = *a;
*a = *b;
*b = c;
}
static inline void
array_clear(Eina_Array **arr)
{
Eina_Array *a = *arr;
if (!a) return;
while (eina_array_count(a))
evas_object_del(eina_array_pop(a));
eina_array_free(a);
*arr = NULL;
}
static inline Eina_Bool
client_allowed_check(Comp *c, struct wl_client *client)
{
pid_t p;
int32_t pid;
Eina_Bool err;
wl_client_get_credentials(client, &p, NULL, NULL);
if (p == getpid()) return EINA_TRUE;
pid = p;
err = (!c->exes) || !eina_hash_find(c->exes, &pid);
if (err)
wl_client_post_no_memory(client);
return !err;
}
static inline void
comp_data_device_source_reader_clear(Comp_Data_Device_Source *ds)
{
fdh_del(ds->reader);
ds->reader = NULL;
eina_binbuf_free(ds->reader_data);
ds->reader_data = NULL;
}
static inline void
comp_surface_reparent(Comp_Surface *cs, Comp_Surface *pcs)
{
if (cs->parent == pcs) return;
if (!cs->extracted)
evas_object_smart_member_del(cs->obj);
if (cs->parent)
cs->parent->children = eina_inlist_remove(cs->parent->children, EINA_INLIST_GET(cs));
if (pcs)
{
cs->c->surfaces = eina_inlist_remove(cs->c->surfaces, EINA_INLIST_GET(cs));
cs->c->surfaces_count--;
if (!cs->extracted)
evas_object_smart_member_add(cs->obj, pcs->obj);
pcs->children = eina_inlist_append(pcs->children, EINA_INLIST_GET(cs));
}
else
{
if (!cs->extracted)
evas_object_smart_member_add(cs->obj, cs->c->obj);
cs->c->surfaces = eina_inlist_append(cs->c->surfaces, EINA_INLIST_GET(cs));
cs->c->surfaces_count++;
}
cs->parent = pcs;
}
static inline struct wl_resource *
data_device_find(Comp_Seat *s, struct wl_resource *resource)
{
struct wl_client *client = wl_resource_get_client(resource);
return eina_hash_find(s->data_devices, &client);
}
static inline void
seat_drag_end(Comp_Seat *s)
{
s->drag.tch = 0;
s->drag.id = 0;
s->drag.surface = NULL;
s->drag.res = NULL;
s->drag.enter = NULL;
}
static inline Comp_Seat *
seat_find(Comp_Surface *cs, const Eo *dev)
{
const Eo *seat = dev;
Comp_Seat *s;
if (!cs->c->parent_disp) return EINA_INLIST_CONTAINER_GET(cs->c->seats, Comp_Seat);
if (evas_device_class_get(seat) != EVAS_DEVICE_CLASS_SEAT)
seat = evas_device_parent_get(seat);
EINA_INLIST_FOREACH(cs->c->seats, s)
if (s->dev == seat) return s;
return NULL;
}
static inline Comp_Seat *
comp_seat_find(Comp *c, const Eo *dev)
{
const Eo *seat = dev;
Comp_Seat *s;
if (!c->parent_disp) return EINA_INLIST_CONTAINER_GET(c->seats, Comp_Seat);
if (evas_device_class_get(seat) != EVAS_DEVICE_CLASS_SEAT)
seat = evas_device_parent_get(seat);
EINA_INLIST_FOREACH(c->seats, s)
if (s->dev == seat) return s;
return NULL;
}
static inline Eina_List *
seat_kbd_active_resources_get(Comp_Seat *s)
{
Eina_List *l, *lcs, *llcs;
Comp_Surface *cs;
if (!s->active_client) return NULL;
if (!s->kbd.resources) return NULL;
l = eina_hash_find(s->kbd.resources, &s->active_client);
if (!l) return NULL;
lcs = eina_hash_find(s->c->client_surfaces, &s->active_client);
if (!lcs) return NULL;
EINA_LIST_REVERSE_FOREACH(lcs, llcs, cs)
if (cs->role && (!cs->shell.popup)) return l;
return NULL;
}
static inline Eina_List *
seat_ptr_resources_get(Comp_Seat *s, struct wl_client *client)
{
return s->ptr.resources ? eina_hash_find(s->ptr.resources, &client) : NULL;
}
static inline Eina_List *
seat_tch_resources_get(Comp_Seat *s, struct wl_client *client)
{
return s->tch.resources ? eina_hash_find(s->tch.resources, &client) : NULL;
}
static void comp_render_pre_proxied(Eo *o, Evas *e, void *event_info);
static void comp_render_post_proxied(Comp_Surface *cs, Evas *e, void *event_info);
static void comp_surface_commit_image_state(Comp_Surface *cs, Comp_Buffer *buffer, Eo *o);
static void
comp_surface_proxy_del(void *data, Evas *e, Eo *obj, void *event_info EINA_UNUSED)
{
Comp_Surface *cs = data;
int i;
cs->proxies = eina_list_remove(cs->proxies, obj);
for (i = 0; i < 2; i++)
{
Comp_Buffer *buffer = cs->buffer[i];
if (!buffer) continue;
if (buffer->renders) buffer->renders = eina_list_remove(buffer->renders, e);
if (buffer->post_renders) buffer->post_renders = eina_list_remove(buffer->post_renders, e);
}
evas_event_callback_del_full(e, EVAS_CALLBACK_RENDER_PRE, (Evas_Event_Cb)comp_render_pre_proxied, obj);
evas_event_callback_del_full(e, EVAS_CALLBACK_RENDER_POST, (Evas_Event_Cb)comp_render_post_proxied, cs);
#ifdef HAVE_ECORE_X
if (ecore_x_display_get())
ecore_x_dnd_callback_pos_update_set(NULL, NULL);
#endif
}
static void
comp_surface_proxy_resize(void *data, Evas *e EINA_UNUSED, Eo *obj, void *event_info EINA_UNUSED)
{
int w, h;
evas_object_geometry_get(obj, NULL, NULL, &w, &h);
ecore_evas_resize(data, w, h);
}
static Eina_Bool
comp_surface_is_alpha(Comp_Surface *cs, Comp_Buffer *buffer)
{
int format;
if (buffer->shm_buffer)
format = wl_shm_buffer_get_format(buffer->shm_buffer);
else if (buffer->dmabuf_buffer)
format = buffer->dmabuf_buffer->attributes.format;
else if (cs->c->gl)
cs->c->glapi->evasglQueryWaylandBuffer(cs->c->gl, buffer->res, EGL_TEXTURE_FORMAT, &format);
else return EINA_FALSE; //not a real case
switch (format)
{
case DRM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_ARGB8888:
case EGL_TEXTURE_RGBA:
return EINA_TRUE;
default:
break;
}
return EINA_FALSE;
}
static void
comp_surface_proxy_win_del(Ecore_Evas *ee)
{
Comp_Seat *s = ecore_evas_data_get(ee, "comp_seat");
if (!s) return;
s->drag.proxy_win = NULL;
//fprintf(stderr, "PROXY WIN DEL\n");
}
static void
seat_drag_proxy_win_add(Comp_Seat *s)
{
Eo *o;
if (s->drag.proxy_win) abort();
evas_object_hide(s->drag.surface->obj);
s->drag.proxy_win = ecore_evas_new(NULL, 0, 0, 1, 1, NULL);
ecore_evas_data_set(s->drag.proxy_win, "comp_seat", s);
ecore_evas_callback_destroy_set(s->drag.proxy_win, comp_surface_proxy_win_del);
ecore_evas_alpha_set(s->drag.proxy_win, 1);
ecore_evas_borderless_set(s->drag.proxy_win, 1);
ecore_evas_override_set(s->drag.proxy_win, 1);
ecore_evas_show(s->drag.proxy_win);
o = evas_object_image_filled_add(ecore_evas_get(s->drag.proxy_win));
evas_object_data_set(o, "comp_surface", s->drag.surface);
evas_event_callback_add(ecore_evas_get(s->drag.proxy_win),
EVAS_CALLBACK_RENDER_PRE, (Evas_Event_Cb)comp_render_pre_proxied, o);
evas_event_callback_add(ecore_evas_get(s->drag.proxy_win),
EVAS_CALLBACK_RENDER_POST, (Evas_Event_Cb)comp_render_post_proxied, s->drag.surface);
evas_object_image_border_center_fill_set(o, EVAS_BORDER_FILL_SOLID);
evas_object_image_colorspace_set(o, EVAS_COLORSPACE_ARGB8888);
if (!s->drag.surface->render_queue)
{
comp_surface_commit_image_state(s->drag.surface, s->drag.surface->buffer[1], o);
evas_object_image_alpha_set(o,
comp_surface_is_alpha(s->drag.surface, s->drag.surface->buffer[1]));
evas_object_image_data_update_add(o, 0, 0, 9999, 9999);
}
evas_object_show(o);
evas_object_event_callback_add(o, EVAS_CALLBACK_DEL,
comp_surface_proxy_del, s->drag.surface);
evas_object_event_callback_add(o, EVAS_CALLBACK_RESIZE,
comp_surface_proxy_resize, s->drag.proxy_win);
evas_object_resize(o,
s->drag.surface->buffer[!s->drag.surface->render_queue]->w,
s->drag.surface->buffer[!s->drag.surface->render_queue]->h);
s->drag.surface->proxies = eina_list_append(s->drag.surface->proxies, o);
}
static void comp_surface_send_data_device_leave(Comp_Surface *cs, Comp_Seat *s);
static void comp_surface_send_data_device_enter(Comp_Surface *cs, Comp_Seat *s);
static void
dnd_motion(Comp_Seat *s, int ex, int ey)
{
struct wl_resource *res;
Eina_List *l, *lcs;
Comp_Surface *cs;
s->ptr.pos.x = ex;
s->ptr.pos.y = ey;
if (!s->active_client) return;
l = eina_hash_find(s->c->client_surfaces, &s->active_client);
EINA_LIST_REVERSE_FOREACH(l, lcs, cs)
{
int x, y, w, h;
if ((!cs->mapped) || (!cs->role)) continue;
evas_object_geometry_get(cs->obj, &x, &y, &w, &h);
if (!COORDS_INSIDE(ex, ey, x, y, w, h)) continue;
if (s->drag.enter != cs)
{
if (s->drag.enter)
comp_surface_send_data_device_leave(s->drag.enter, s);
s->drag.enter = cs;
if (s->drag.source)
comp_surface_send_data_device_enter(cs, s);
}
if (!s->drag.source) break;
res = eina_hash_find(s->data_devices, &s->active_client);
wl_data_device_send_motion(res,
(unsigned int)((unsigned long long)(ecore_time_get() * 1000.0) & 0xffffffff),
wl_fixed_from_int(ex - x), wl_fixed_from_int(ey - y));
break;
}
}
#ifdef HAVE_ECORE_X
static void x11_send_send(Comp_Data_Device_Source *source, const char* mime_type, int32_t fd, Comp_Data_Device_Offer_Type type);
#endif
#include "copiedfromweston.x"
#ifdef HAVE_ECORE_X
# include "x11.x"
#endif
static void
resource_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
#define CONTAINS(x, y, w, h, xx, yy, ww, hh) \
(((xx) >= (x)) && (((x) + (w)) >= ((xx) + (ww))) && ((yy) >= (y)) && (((y) + (h)) >= ((yy) + (hh))))
#define CONSTRAINED(W, H, X, Y) \
!CONTAINS(zx, zy, zw, zh, (X), (Y), (W), (H))
static int
_apply_positioner_x(int x, Shell_Positioner *sp, Eina_Bool invert)
{
enum xdg_positioner_anchor an = XDG_POSITIONER_ANCHOR_NONE;
enum xdg_positioner_gravity grav = XDG_POSITIONER_GRAVITY_NONE;
if (invert)
{
if (sp->anchor == XDG_POSITIONER_ANCHOR_LEFT)
an = XDG_POSITIONER_ANCHOR_RIGHT;
else if (sp->anchor == XDG_POSITIONER_ANCHOR_RIGHT)
an = XDG_POSITIONER_ANCHOR_LEFT;
if (sp->gravity & XDG_POSITIONER_GRAVITY_LEFT)
grav |= XDG_POSITIONER_GRAVITY_RIGHT;
else if (sp->gravity & XDG_POSITIONER_GRAVITY_RIGHT)
grav |= XDG_POSITIONER_GRAVITY_LEFT;
}
else
{
an = sp->anchor;
grav = sp->gravity;
}
/* left edge */
if (an == XDG_POSITIONER_ANCHOR_LEFT)
x += sp->anchor_rect.x;
/* right edge */
else if (an == XDG_POSITIONER_ANCHOR_RIGHT)
x += sp->anchor_rect.x + sp->anchor_rect.w;
/* center */
else
x += sp->anchor_rect.x + (sp->anchor_rect.w / 2);
/* flip left over anchor */
if (grav & XDG_POSITIONER_GRAVITY_LEFT)
x -= sp->size.w;
/* center on anchor */
else if (!(grav & XDG_POSITIONER_GRAVITY_RIGHT))
x -= sp->size.w / 2;
return x;
}
static int
_apply_positioner_y(int y, Shell_Positioner *sp, Eina_Bool invert)
{
enum xdg_positioner_anchor an = XDG_POSITIONER_ANCHOR_NONE;
enum xdg_positioner_gravity grav = XDG_POSITIONER_GRAVITY_NONE;
if (invert)
{
if (sp->anchor == XDG_POSITIONER_ANCHOR_TOP)
an = XDG_POSITIONER_ANCHOR_BOTTOM;
else if (sp->anchor == XDG_POSITIONER_ANCHOR_BOTTOM)
an = XDG_POSITIONER_ANCHOR_TOP;
if (sp->gravity & XDG_POSITIONER_GRAVITY_TOP)
grav |= XDG_POSITIONER_GRAVITY_BOTTOM;
else if (sp->gravity & XDG_POSITIONER_GRAVITY_BOTTOM)
grav |= XDG_POSITIONER_GRAVITY_TOP;
}
else
{
an = sp->anchor;
grav = sp->gravity;
}
/* up edge */
if (an == XDG_POSITIONER_ANCHOR_TOP)
y += sp->anchor_rect.y;
/* bottom edge */
else if (an == XDG_POSITIONER_ANCHOR_BOTTOM)
y += sp->anchor_rect.y + sp->anchor_rect.h;
/* center */
else
y += sp->anchor_rect.y + (sp->anchor_rect.h / 2);
/* flip up over anchor */
if (grav & XDG_POSITIONER_GRAVITY_TOP)
y -= sp->size.h;
/* center on anchor */
else if (!(grav & XDG_POSITIONER_GRAVITY_BOTTOM))
y -= sp->size.h / 2;
return y;
}
static Eina_Bool
_apply_positioner_slide(Comp_Surface *cs, Shell_Positioner *sp, int *x, int *y, int *w, int *h, int zx, int zy, int zw, int zh)
{
int px, py, cx, cy;
evas_object_geometry_get(cs->parent->obj, &px, &py, NULL, NULL);
evas_object_geometry_get(cs->c->obj, &cx, &cy, NULL, NULL);
px -= cx, py -= cy;
if ((sp->constrain & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X) &&
(!CONTAINS(zx, zy, zw, zh, *x, zy, *w, 1)))
{
int sx = *x;
if (sp->gravity & XDG_POSITIONER_GRAVITY_LEFT)
{
if (*x + *w > zx + zw)
sx = MAX(zx + zw - *w, px + sp->anchor_rect.x - *w);
else if (*x < zx)
sx = MIN(zx, px + sp->anchor_rect.x + sp->anchor_rect.w);
}
else if (sp->gravity & XDG_POSITIONER_GRAVITY_RIGHT)
{
if (*x < zx)
sx = MIN(zx, *x + sp->anchor_rect.x + sp->anchor_rect.w);
else if (*x + *w > zx + zw)
sx = MAX(zx + zw - *w, px + sp->anchor_rect.x - *w);
}
if (CONTAINS(zx, zy, zw, zh, sx, zy, *w, 1))
*x = sx;
}
if (!CONSTRAINED(*w, *h, *x, *y)) return EINA_TRUE;
if ((sp->constrain & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) &&
(!CONTAINS(zx, zy, zw, zh, zx, *y, 1, *h)))
{
int sy = *y;
if (sp->gravity & XDG_POSITIONER_GRAVITY_TOP)
{
if (*y + *h > zy + zh)
sy = MAX(zy + zh - *h, py + sp->anchor_rect.y - *h);
else if (*y < zy)
sy = MIN(zy, py + sp->anchor_rect.y + sp->anchor_rect.h);
}
else if (sp->gravity & XDG_POSITIONER_GRAVITY_BOTTOM)
{
if (*y < zy)
sy = MIN(zy, py + sp->anchor_rect.y + sp->anchor_rect.h);
else if (*y + *h > zy + zh)
sy = MAX(zy + zh - *h, py + sp->anchor_rect.y - *h);
}
if (CONTAINS(zx, zy, zw, zh, zx, sy, 1, *h))
*y = sy;
}
return !CONSTRAINED(*w, *h, *x, *y);
}
static void
_apply_positioner(Comp_Surface *cs, Shell_Positioner *sp)
{
int ix, iy, x, y, w = -1, h = -1, px, py, cx, cy;
int zx, zy, zw, zh;
/* apply base geometry:
* coords are relative to parent
*/
evas_object_geometry_get(cs->parent->obj, &px, &py, NULL, NULL);
evas_object_geometry_get(cs->c->obj, &cx, &cy, NULL, NULL);
px -= cx, py -= cy;
ix = x = px + sp->offset.x;
iy = y = py + sp->offset.y;
if (sp->size.w && sp->size.h)
{
w = sp->size.w;
h = sp->size.h;
}
/* apply policies in order:
- anchor (add anchor_rect using anchor point)
- gravity (perform flips if gravity is not right|bottom)
- constrain (adjust if popup does not fit)
*/
x = _apply_positioner_x(x, sp, 0);
y = _apply_positioner_y(y, sp, 0);
evas_object_geometry_get(cs->c->obj, &zx, &zy, &zw, &zh);
if (!CONSTRAINED(w, h, x, y))
{
evas_object_move(cs->obj, zx + x, zy + y);
if (w > 0) evas_object_resize(cs->obj, w, h);
return;
}
/* assume smart placement:
- flip
- slide
- resize
*/
if ((sp->constrain & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X) &&
(!CONTAINS(zx, zy, zw, zh, x, zy, w, 1)))
{
int fx;
fx = _apply_positioner_x(ix, sp, 1);
if (CONTAINS(zx, zy, zw, zh, fx, zy, w, 1))
x = fx;
}
if (!CONSTRAINED(w, h, x, y))
{
evas_object_move(cs->obj, zx + x, zy + y);
if (w > 0) evas_object_resize(cs->obj, w, h);
return;
}
if ((sp->constrain & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y) &&
(!CONTAINS(zx, zy, zw, zh, zx, y, 1, h)))
{
int fy;
fy = _apply_positioner_y(iy, sp, 1);
if (CONTAINS(zx, zy, zw, zh, zx, fy, 1, h))
y = fy;
}
if (!CONSTRAINED(w, h, x, y))
{
evas_object_move(cs->obj, zx + x, zy + y);
if (w > 0) evas_object_resize(cs->obj, w, h);
return;
}
if (_apply_positioner_slide(cs, sp, &x, &y, &w, &h, zx, zy, zw, zh))
{
evas_object_move(cs->obj, zx + x, zy + y);
if (w > 0) evas_object_resize(cs->obj, w, h);
return;
}
if (!CONSTRAINED(w, h, x, y))
{
evas_object_move(cs->obj, zx + x, zy + y);
if (w > 0) evas_object_resize(cs->obj, w, h);
return;
}
if ((sp->constrain & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X) &&
(!CONTAINS(zx, zy, zw, zh, x, zy, w, 1)))
{
w = zx + zw - x;
if (!CONSTRAINED(w, h, x, y))
{
evas_object_move(cs->obj, zx + x, zy + y);
if (w > 0) evas_object_resize(cs->obj, w, h);
return;
}
}
if ((sp->constrain & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y) &&
(!CONTAINS(zx, zy, zw, zh, zx, y, 1, h)))
{
h = zy + zh - y;
}
evas_object_move(cs->obj, zx + x, zy + y);
if (w > 0) evas_object_resize(cs->obj, w, h);
}
static const struct wl_data_offer_interface data_device_offer_interface =
{
data_device_offer_accept,
data_device_offer_receive,
resource_destroy,
data_device_offer_finish,
data_device_offer_set_actions,
};
static void
data_device_offer_create(Comp_Data_Device_Source *source, struct wl_resource *resource)
{
Comp_Data_Device_Offer *off;
Eina_List *l;
Eina_Stringshare *type;
off = calloc(1, sizeof(Comp_Data_Device_Offer));
off->res = wl_resource_create(wl_resource_get_client(resource), &wl_data_offer_interface,
wl_resource_get_version(resource), 0);
wl_resource_set_implementation(off->res, &data_device_offer_interface, off, data_device_offer_impl_destroy);
off->source = source;
source->offer = off;
off->proxy = source->proxy;
wl_data_device_send_data_offer(resource, off->res);
EINA_LIST_FOREACH(source->mime_types, l, type)
wl_data_offer_send_offer(off->res, type);
}
static void
comp_buffer_state_alloc(Comp_Buffer_State *cbs)
{
cbs->damages = tiler_new();
cbs->opaque = tiler_new();
cbs->input = tiler_new();
}
static void
comp_buffer_state_clear(Comp_Buffer_State *cbs)
{
eina_tiler_free(cbs->damages);
eina_tiler_free(cbs->opaque);
eina_tiler_free(cbs->input);
while (cbs->frames)
wl_resource_destroy(eina_list_data_get(cbs->frames));
}
static void
comp_seat_send_modifiers(Comp_Seat *s, struct wl_resource *res, uint32_t serial)
{
wl_keyboard_send_modifiers(res, serial, s->kbd.mods.depressed,
s->kbd.mods.latched,
s->kbd.mods.locked,
s->kbd.mods.group);
s->kbd.mods.changed = 0;
}
static Eina_Bool
data_device_selection_read(void *d, Ecore_Fd_Handler *fdh)
{
Comp_Data_Device_Source *ds = d;
int len = -1;
do
{
unsigned char buf[2048];
int fd;
fd = ecore_main_fd_handler_fd_get(fdh);
if (fd < 0) break;
len = read(fd, buf, sizeof(buf));
if (len > 0)
{
if (!ds->reader_data)
ds->reader_data = eina_binbuf_new();
eina_binbuf_append_length(ds->reader_data, buf, len);
return ECORE_CALLBACK_RENEW;
}
} while (0);
fdh_del(fdh);
ds->reader = NULL;
if (len < 0)
{
eina_binbuf_free(ds->reader_data);
ds->reader_data = NULL;
}
return ECORE_CALLBACK_RENEW;
}
static void
comp_seat_kbd_data_device_enter(Comp_Seat *s)
{
struct wl_resource *res;
Comp_Data_Device_Source *ds = s->selection_source;
int fd[2];
if (!s->kbd.enter) return;
res = data_device_find(s, s->kbd.enter->res);
if (!res) return;
if (ds)
{
data_device_offer_create(ds, res);
ds->offer->type = COMP_DATA_DEVICE_OFFER_TYPE_CLIPBOARD;
}
if (ds)
wl_data_device_send_selection(res, ds->offer ? ds->offer->res : NULL);
if ((!ds) || (!ds->mime_types)) return;
EINA_SAFETY_ON_TRUE_RETURN(pipe2(fd, O_CLOEXEC) < 0);
wl_data_source_send_send(ds->res, eina_list_data_get(ds->mime_types), fd[1]);
close(fd[1]);
ds->reader = ecore_main_fd_handler_file_add(fd[0], ECORE_FD_READ | ECORE_FD_ERROR,
data_device_selection_read, ds, NULL, NULL);
}
static void
comp_seats_redo_enter(Comp *c, Comp_Surface *cs)
{
Comp_Seat *s;
uint32_t serial;
struct wl_client *client = NULL;
serial = wl_display_next_serial(c->display);
if (cs)
client = wl_resource_get_client(cs->res);
EINA_INLIST_FOREACH(c->seats, s)
{
Eina_List *l, *ll;
struct wl_resource *res;
Eina_Bool same = s->kbd.enter == cs;
if (c->active_surface && s->kbd.enter && (!same))
{
l = seat_kbd_active_resources_get(s);
EINA_LIST_FOREACH(l, ll, res)
wl_keyboard_send_leave(res, serial, s->kbd.enter->res);
}
s->active_client = client;
if (cs)
{
l = seat_kbd_active_resources_get(s);
EINA_LIST_FOREACH(l, ll, res)
{
if (!same)
wl_keyboard_send_enter(res, serial, cs->res, &s->kbd.keys);
comp_seat_send_modifiers(s, res, serial);
}
}
s->kbd.enter = cs;
if (s->kbd.enter && s->selection_source && (!same))
comp_seat_kbd_data_device_enter(s);
}
c->active_surface = cs;
}
static void shell_surface_send_configure(Comp_Surface *cs);
static void
shell_surface_deactivate_recurse(Comp_Surface *cs)
{
Comp_Surface *ccs;
EINA_INLIST_FOREACH(cs->children, ccs)
{
if (!ccs->shell.popup) continue;
shell_surface_deactivate_recurse(ccs);
if (ccs->shell.grabs) evas_object_hide(ccs->obj);
}
}
static void
shell_surface_activate_recurse(Comp_Surface *cs)
{
Comp_Surface *lcs, *parent = cs->parent;
Eina_List *parents = NULL;
Eina_Inlist *i;
if (parent)
{
/* remove focus from parents */
while (parent)
{
parents = eina_list_append(parents, parent);
if (!parent->shell.popup) break;
parent = parent->parent;
}
}
EINA_INLIST_FOREACH_SAFE(cs->c->surfaces, i, lcs)
if (lcs->shell.activated && (lcs != cs))
{
if ((!parents) || (!eina_list_data_find(parents, lcs)))
{
lcs->shell.activated = 0;
shell_surface_send_configure(lcs);
cs->c->surfaces = eina_inlist_promote(cs->c->surfaces, EINA_INLIST_GET(lcs));
}
}
eina_list_free(parents);
}
static void
shell_surface_minmax_update(Comp_Surface *cs)
{
int w, h;
if (!cs) return;
if (!cs->c->minmax) return;
if (cs->extracted) return;
if (cs->parent) return;
evas_object_size_hint_min_get(cs->obj, &w, &h);
evas_object_size_hint_min_set(cs->c->obj, w, h);
evas_object_size_hint_max_get(cs->obj, &w, &h);
if (!w) w = -1;
if (!h) h = -1;
evas_object_size_hint_max_set(cs->c->obj, w, h);
}
static void
shell_surface_aspect_update(Comp_Surface *cs)
{
Evas_Aspect_Control aspect;
int w, h;
if (!cs) return;
if (!cs->c->aspect) return;
if (cs->extracted) return;
if (cs->parent) return;
evas_object_size_hint_aspect_get(cs->obj, &aspect, &w, &h);
evas_object_size_hint_aspect_set(cs->c->obj, aspect, w, h);
}
static void
shell_surface_send_configure(Comp_Surface *cs)
{
uint32_t serial, *s;
struct wl_array states;
int w, h;
cs->shell.new = 0;
if (cs->shell.popup)
{
int x, y, px, py;
evas_object_geometry_get(cs->obj, &x, &y, &w, &h);
evas_object_geometry_get(cs->parent->obj, &px, &py, NULL, NULL);
serial = wl_display_next_serial(cs->c->display);
xdg_popup_send_configure(cs->role, x - px, y - py, w, h);
xdg_surface_send_configure(cs->shell.surface, serial);
return;
}
wl_array_init(&states);
s = wl_array_add(&states, sizeof(uint32_t));
*s = XDG_TOPLEVEL_STATE_FULLSCREEN;
if (cs->shell.activated)
{
s = wl_array_add(&states, sizeof(uint32_t));
*s = XDG_TOPLEVEL_STATE_ACTIVATED;
if (!cs->extracted)
evas_object_raise(cs->obj);
if (cs->parent)
cs->parent->children = eina_inlist_demote(cs->parent->children, EINA_INLIST_GET(cs));
else
cs->c->surfaces = eina_inlist_demote(cs->c->surfaces, EINA_INLIST_GET(cs));
shell_surface_activate_recurse(cs);
}
serial = wl_display_next_serial(cs->c->display);
if (cs->extracted)
evas_object_geometry_get(cs->obj, NULL, NULL, &w, &h);
else
evas_object_geometry_get(cs->c->clip, NULL, NULL, &w, &h);
xdg_toplevel_send_configure(cs->role, w, h, &states);
xdg_surface_send_configure(cs->shell.surface, serial);
wl_array_release(&states);
if (cs->shell.activated)
{
Comp_Surface *ccs;
/* apply activation to already-mapped surface */
if (cs->mapped)
{
comp_seats_redo_enter(cs->c, cs);
shell_surface_aspect_update(cs);
shell_surface_minmax_update(cs);
}
EINA_INLIST_FOREACH(cs->children, ccs)
if (ccs->shell.surface && ccs->role && ccs->shell.popup)
ccs->shell.activated = cs->shell.activated;
}
else
shell_surface_deactivate_recurse(cs);
}
static void
shell_surface_init(Comp_Surface *cs)
{
/* update activate status: newest window is always activated */
Comp_Surface *parent = cs->parent;
if (cs->c->active_surface && parent && (!parent->shell.activated))
{
/* child windows/popups cannot steal focus from toplevel */
shell_surface_send_configure(cs);
return;
}
cs->shell.activated = 1;
shell_surface_send_configure(cs);
}
static void
comp_surface_output_leave(Comp_Surface *cs)
{
Eina_List *l;
struct wl_resource *res;
EINA_LIST_FOREACH(cs->c->output_resources, l, res)
if (wl_resource_get_client(res) == wl_resource_get_client(cs->res))
wl_surface_send_leave(cs->res, res);
}
static void
comp_surface_output_enter(Comp_Surface *cs)
{
Eina_List *l;
struct wl_resource *res;
EINA_LIST_FOREACH(cs->c->output_resources, l, res)
if (wl_resource_get_client(res) == wl_resource_get_client(cs->res))
wl_surface_send_enter(cs->res, res);
}
static void
comp_surface_buffer_detach(Comp_Buffer **pbuffer)
{
Comp_Buffer *buffer;
buffer = *pbuffer;
if (!buffer) return;
if (buffer->post_renders)
fprintf(stderr, "CRASH %u\n", wl_resource_get_id(buffer->res));
eina_list_free(buffer->renders);
wl_list_remove(&buffer->destroy_listener.link);
//if (buffer->dbg) fprintf(stderr, "BUFFER(%d) RELEASE\n", wl_resource_get_id(buffer->res));
if (buffer->pool) wl_shm_pool_unref(buffer->pool);
wl_buffer_send_release(buffer->res);
free(buffer);
*pbuffer = NULL;
}
static void
comp_surface_buffer_post_render(Comp_Surface *cs)
{
double t = ecore_loop_time_get() - cs->c->wayland_time_base;
//if (cs->subsurface)
//fprintf(stderr, "FRAME(%d)\n", wl_resource_get_id(cs->res));
while (cs->frames)
{
struct wl_resource *frame = eina_list_data_get(cs->frames);
wl_callback_send_done(frame, t * 1000);
wl_resource_destroy(frame);
}
}
static void
comp_surface_pixels_get(void *data, Eo *obj)
{
Comp_Surface *cs = data;
Comp_Buffer *buffer;
evas_object_image_pixels_dirty_set(obj, 0);
evas_object_image_pixels_get_callback_set(obj, NULL, NULL);
buffer = cs->buffer[!cs->render_queue];
//if (cs->cursor || (buffer->w == 32)) fprintf(stderr, "RENDER(%d) %dx%d\n", wl_resource_get_id(buffer->res), buffer->w, buffer->h);
evas_object_image_size_set(obj, buffer->w, buffer->h);
evas_object_image_data_set(obj, wl_shm_buffer_get_data(buffer->shm_buffer));
}
static void
comp_surface_commit_image_state(Comp_Surface *cs, Comp_Buffer *buffer, Eo *o)
{
if ((!buffer->renders) || (!eina_list_data_find(buffer->renders, evas_object_evas_get(o))))
buffer->renders = eina_list_append(buffer->renders, evas_object_evas_get(o));
evas_object_image_pixels_dirty_set(o, 1);
if (buffer->shm_buffer)
{
//if (cs->subsurface)
//fprintf(stderr, "SET CB\n");
evas_object_image_pixels_get_callback_set(o, comp_surface_pixels_get, cs);
buffer->pool = wl_shm_buffer_ref_pool(buffer->shm_buffer);
}
else
{
Evas_Native_Surface ns;
evas_object_image_pixels_get_callback_set(o, NULL, NULL);
if (buffer->dmabuf_buffer)
{
ns.type = EVAS_NATIVE_SURFACE_WL_DMABUF;
ns.version = EVAS_NATIVE_SURFACE_VERSION;
ns.data.wl_dmabuf.attr = &buffer->dmabuf_buffer->attributes;
ns.data.wl_dmabuf.resource = buffer->res;
ns.data.wl_dmabuf.scanout.handler = NULL;
ns.data.wl_dmabuf.scanout.data = NULL;
}
else
{
ns.type = EVAS_NATIVE_SURFACE_WL;
ns.version = EVAS_NATIVE_SURFACE_VERSION;
ns.data.wl.legacy_buffer = buffer->res;
}
evas_object_image_native_surface_set(o, &ns);
}
}
static void
shell_surface_reset(Comp_Surface *cs)
{
EINA_RECTANGLE_SET(&cs->shell.geom, 0, 0, 0, 0);
eina_stringshare_replace(&cs->shell.title, NULL);
eina_stringshare_replace(&cs->shell.app_id, NULL);
cs->shell.activated = 0;
}
static void
comp_surface_commit_state(Comp_Surface *cs, Comp_Buffer_State *state)
{
int x, y;
Eina_List *l;
Eo *o;
Comp_Buffer *buffer = NULL;
Eina_Bool newly_new = EINA_FALSE;
if (state->attach)
{
comp_surface_buffer_detach(&cs->buffer[0]);
buffer = cs->buffer[0] = state->buffer;
if (buffer)
{
//if (cs->subsurface)
//fprintf(stderr, "BUFFER(%d) COMMIT %d\n", wl_resource_get_id(buffer->res), wl_resource_get_id(cs->res));
if ((!cs->c->rendering) && (!cs->post_render_queue) &&
((!cs->buffer[1]) || (!cs->buffer[1]->post_renders)))
comp_surface_buffer_detach(&cs->buffer[1]);
}
else
{
if (!cs->extracted)
evas_object_hide(cs->obj);
EINA_LIST_FOREACH(cs->proxies, l, o)
evas_object_hide(o);
if (cs->shell.surface)
{
cs->shell.new = 1;
newly_new = EINA_TRUE;
shell_surface_reset(cs);
}
}
}
evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
if (buffer && (!cs->mapped))
{
if (cs->role && (!cs->extracted))
evas_object_show(cs->obj);
/* apply activation to activated surface on map */
if (cs->role && cs->shell.surface && cs->shell.activated && (!cs->shell.popup))
{
comp_seats_redo_enter(cs->c, cs);
shell_surface_aspect_update(cs);
shell_surface_minmax_update(cs);
}
}
if (state->attach && state->buffer)
{
evas_object_move(cs->img, x + buffer->x, y + buffer->y);
evas_object_resize(cs->obj, buffer->w, buffer->h);
}
else if (cs->shell.new && (!newly_new))
shell_surface_init(cs);
state->attach = 0;
state->buffer = NULL;
cs->frames = eina_list_merge(cs->frames, state->frames);
state->frames = NULL;
if (eina_tiler_empty(state->damages))
{
comp_surface_buffer_detach(&buffer);
comp_surface_buffer_post_render(cs);
if (!cs->post_render_queue)
{
evas_object_image_pixels_dirty_set(cs->img, 0);
EINA_LIST_FOREACH(cs->proxies, l, o)
evas_object_image_pixels_dirty_set(o, 0);
}
}
else if (buffer)
{
Eina_Iterator *it;
Eina_Rectangle *rect;
comp_surface_commit_image_state(cs, buffer, cs->img);
EINA_LIST_FOREACH(cs->proxies, l, o)
comp_surface_commit_image_state(cs, buffer, o);
it = eina_tiler_iterator_new(state->damages);
EINA_ITERATOR_FOREACH(it, rect)
{
//if (cs->subsurface) fprintf(stderr, "BUFFER(%d) DAMAGE %d\n", wl_resource_get_id(buffer->res), wl_resource_get_id(cs->res));
evas_object_image_data_update_add(cs->img, rect->x, rect->y, rect->w, rect->h);
EINA_LIST_FOREACH(cs->proxies, l, o)
evas_object_image_data_update_add(o, rect->x, rect->y, rect->w, rect->h);
}
eina_iterator_free(it);
if (!cs->render_queue)
cs->c->render_queue = eina_list_append(cs->c->render_queue, cs);
cs->render_queue = 1;
}
eina_tiler_clear(state->damages);
if (state->set_opaque && (!eina_tiler_equal(cs->opaque, state->opaque)))
{
array_clear(&cs->opaque_rects);
if (!eina_tiler_empty(state->opaque))
/* FIXME: proxied opaque regions */
{
Eina_Iterator *it;
Eina_Rectangle *rect;
Eo *r;
it = eina_tiler_iterator_new(state->opaque);
cs->opaque_rects = eina_array_new(1);
EINA_ITERATOR_FOREACH(it, rect)
{
r = evas_object_rectangle_add(cs->c->evas);
evas_object_name_set(r, "opaque_rect");
evas_object_pass_events_set(r, 1);
evas_object_color_set(r, 0, 0, 0, 255);
evas_object_smart_member_add(r, cs->obj);
evas_object_geometry_set(r, x + rect->x, y + rect->y, rect->w, rect->h);
evas_object_stack_below(r, cs->img);
evas_object_show(r);
eina_array_push(cs->opaque_rects, r);
}
/* FIXME: maybe use image border here */
eina_iterator_free(it);
}
PTR_SWAP(&cs->opaque, &state->opaque);
}
eina_tiler_clear(state->opaque);
state->set_opaque = 0;
if (state->set_input)
{
if (eina_tiler_empty(state->input))
{
array_clear(&cs->input_rects);
evas_object_pass_events_set(cs->img, 0);
evas_object_pointer_mode_set(cs->img, EVAS_OBJECT_POINTER_MODE_NOGRAB);
}
else if (!eina_tiler_equal(cs->input, state->input))
{
Eina_Iterator *it;
Eina_Rectangle *rect;
Eo *r;
array_clear(&cs->input_rects);
it = eina_tiler_iterator_new(state->input);
cs->input_rects = eina_array_new(1);
EINA_ITERATOR_FOREACH(it, rect)
{
r = evas_object_rectangle_add(cs->c->evas);
evas_object_name_set(r, "input_rect");
evas_object_color_set(r, 0, 0, 0, 0);
evas_object_smart_member_add(r, cs->obj);
evas_object_geometry_set(r, x + rect->x, y + rect->y, rect->w, rect->h);
evas_object_stack_above(r, cs->img);
evas_object_pointer_mode_set(r, EVAS_OBJECT_POINTER_MODE_NOGRAB);
evas_object_show(r);
evas_object_show(r);
eina_array_push(cs->input_rects, r);
}
evas_object_pass_events_set(cs->img, 1);
eina_iterator_free(it);
}
PTR_SWAP(&cs->input, &state->input);
}
eina_tiler_clear(state->input);
state->set_input = 0;
if (cs->pending_subsurfaces)
{
cs->subsurfaces = eina_list_free(cs->subsurfaces);
PTR_SWAP(&cs->subsurfaces, &cs->pending_subsurfaces);
}
if (cs->subsurface)
{
if (cs->subsurface->set_offset)
{
cs->subsurface->offset.x = cs->subsurface->pending_offset.x;
cs->subsurface->offset.y = cs->subsurface->pending_offset.y;
cs->subsurface->pending_offset.x = cs->subsurface->pending_offset.y = 0;
cs->subsurface->set_offset = 0;
evas_object_geometry_get(cs->parent->obj, &x, &y, NULL, NULL);
evas_object_move(cs->obj, x + cs->subsurface->offset.x, y + cs->subsurface->offset.y);
}
}
}
static void
comp_surface_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
if (cs)
{
if (cs->post_render_queue)
{
cs->dead = 1;
evas_object_pass_events_set(cs->obj, 1);
return;
}
cs->res = NULL;
}
wl_resource_destroy(resource);
}
static void
comp_surface_buffer_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
Comp_Buffer *buffer;
Comp_Surface *cs;
buffer = container_of(listener, Comp_Buffer, destroy_listener);
cs = buffer->cs;
if (cs)
{
if (cs->buffer[0] == buffer) cs->buffer[0] = NULL;
else if (cs->buffer[1] == buffer) cs->buffer[1] = NULL;
else if (cs->pending.buffer == buffer) cs->pending.buffer = NULL;
else if (cs->subsurface)
{
if (cs->subsurface->cache.buffer == buffer) cs->subsurface->cache.buffer = NULL;
}
}
free(buffer);
}
static void
comp_surface_attach(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *buffer_resource, int32_t x, int32_t y)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
Comp_Buffer *buffer;
struct wl_shm_buffer *shmbuff;
struct linux_dmabuf_buffer *dmabuf;
if (cs->shell.new)
{
wl_resource_post_error(cs->shell.surface, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER,
"buffer attached/committed before configure");
return;
}
comp_surface_buffer_detach(&cs->pending.buffer);
cs->pending.attach = 1;
if (!buffer_resource) return;
buffer = calloc(1, sizeof(Comp_Buffer));
if (cs->subsurface)
{
//fprintf(stderr, "BUFFER(%d) HELD BY %d\n", wl_resource_get_id(buffer_resource), wl_resource_get_id(resource));
buffer->dbg = 1;
}
buffer->cs = cs;
buffer->x = x;
buffer->y = y;
shmbuff = wl_shm_buffer_get(buffer_resource);
dmabuf = linux_dmabuf_buffer_get(buffer_resource);
if (shmbuff)
{
buffer->w = wl_shm_buffer_get_width(shmbuff);
buffer->h = wl_shm_buffer_get_height(shmbuff);
}
else if (dmabuf)
{
buffer->w = dmabuf->attributes.width;
buffer->h = dmabuf->attributes.height;
}
else if (cs->c->gl)
{
cs->c->glapi->evasglQueryWaylandBuffer(cs->c->gl, buffer_resource, EGL_WIDTH, &buffer->w);
cs->c->glapi->evasglQueryWaylandBuffer(cs->c->gl, buffer_resource, EGL_HEIGHT, &buffer->h);
}
buffer->shm_buffer = shmbuff;
buffer->dmabuf_buffer = dmabuf;
buffer->res = buffer_resource;
buffer->destroy_listener.notify = comp_surface_buffer_destroy;
wl_resource_add_destroy_listener(buffer_resource, &buffer->destroy_listener);
cs->pending.buffer = buffer;
}
static void
comp_surface_damage_buffer(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
eina_tiler_rect_add(cs->pending.damages, &(Eina_Rectangle){x, y, w, h});
}
/*
* Currently damage and damage_buffer are the same because we don't support
* buffer_scale, transform, or viewport. Once we support those we'll have
* to make surface_cb_damage handle damage in surface co-ordinates.
*/
static void
comp_surface_damage(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
comp_surface_damage_buffer(client, resource, x, y, w, h);
}
static void
comp_surface_frame_impl_destroy(struct wl_resource *resource)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
if (!cs) return;
if (cs->frames)
cs->frames = eina_list_remove(cs->frames, resource);
if (cs->pending.frames)
cs->pending.frames = eina_list_remove(cs->pending.frames, resource);
if (cs->subsurface && cs->subsurface->cached)
cs->subsurface->cache.frames = eina_list_remove(cs->subsurface->cache.frames, resource);
}
static void
comp_surface_frame(struct wl_client *client, struct wl_resource *resource, uint32_t callback)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
struct wl_resource *res;
res = wl_resource_create(client, &wl_callback_interface, 1, callback);
wl_resource_set_implementation(res, NULL, cs, comp_surface_frame_impl_destroy);
cs->pending.frames = eina_list_append(cs->pending.frames, res);
}
static void
comp_surface_set_opaque_region(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
cs->pending.set_opaque = 1;
eina_tiler_clear(cs->pending.opaque);
if (region_resource)
eina_tiler_union(cs->pending.opaque, wl_resource_get_user_data(region_resource));
}
static void
comp_surface_set_input_region(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
if (cs->cursor) return;
cs->pending.set_input = 1;
eina_tiler_clear(cs->pending.input);
if (region_resource)
eina_tiler_union(cs->pending.input, wl_resource_get_user_data(region_resource));
}
static void
subcomp_subsurface_cache_merge(Comp_Subsurface *css)
{
//fprintf(stderr, "CACHE MERGE\n");
css->cached = 1;
if (css->cache.frames || css->surface->pending.frames)
css->cache.frames = eina_list_merge(css->cache.frames, css->surface->pending.frames);
css->surface->pending.frames = NULL;
eina_tiler_union(css->cache.damages, css->surface->pending.damages);
eina_tiler_clear(css->surface->pending.damages);
css->cache.set_input = css->surface->pending.set_input;
if (css->surface->pending.set_input)
{
eina_tiler_clear(css->cache.input);
PTR_SWAP(&css->cache.input, &css->surface->pending.input);
}
css->cache.set_opaque = css->surface->pending.set_opaque;
if (css->surface->pending.set_opaque)
{
eina_tiler_clear(css->cache.opaque);
PTR_SWAP(&css->cache.opaque, &css->surface->pending.opaque);
}
css->surface->pending.set_input = 0;
css->surface->pending.set_opaque = 0;
if (!css->surface->pending.attach) return;
css->cache.attach = 1;
comp_surface_buffer_detach(&css->cache.buffer);
PTR_SWAP(&css->cache.buffer, &css->surface->pending.buffer);
css->surface->pending.attach = 0;
}
static void
comp_surface_commit(struct wl_client *client, struct wl_resource *resource)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
Comp_Subsurface *css;
Eina_List *l;
Comp_Buffer_State *cbs = &cs->pending;
if (cs->shell.popup && (!cs->parent))
{
wl_resource_post_error(cs->shell.surface, XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT,
"popup surface has no parent");
return;
}
cs->commit = 1;
if (cs->subsurface)
{
Comp_Surface *parent;
css = cs->subsurface;
parent = cs->parent;
if ((!parent->commit) && css->sync)
{
subcomp_subsurface_cache_merge(css);
return;
}
while (parent && (!parent->commit) && parent->subsurface)
{
Comp_Subsurface *pss = parent->subsurface;
if (pss->sync)
{
subcomp_subsurface_cache_merge(css);
return;
}
parent = parent->parent;
}
subcomp_subsurface_cache_merge(css);
cbs = &css->cache;
}
comp_surface_commit_state(cs, cbs);
EINA_LIST_FOREACH(cs->subsurfaces, l, css)
if (css->sync) comp_surface_commit(client, css->surface->res);
cs->commit = 0;
}
static void
comp_surface_set_buffer_transform(struct wl_client *client, struct wl_resource *resource, int32_t transform)
{
//Comp_Surface *cs = wl_resource_get_user_data(resource);
}
static void
comp_surface_set_buffer_scale(struct wl_client *client, struct wl_resource *resource, int32_t scale)
{
//Comp_Surface *cs = wl_resource_get_user_data(resource);
}
static const struct wl_surface_interface comp_surface_interface =
{
comp_surface_destroy,
comp_surface_attach,
comp_surface_damage,
comp_surface_frame,
comp_surface_set_opaque_region,
comp_surface_set_input_region,
comp_surface_commit,
comp_surface_set_buffer_transform,
comp_surface_set_buffer_scale,
comp_surface_damage_buffer
};
static void
comp_surface_impl_destroy(struct wl_resource *resource)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
Eina_List *subsurfaces;
Comp_Subsurface *css;
Comp_Seat *s;
struct wl_client *client = wl_resource_get_client(resource);
EINA_INLIST_FOREACH(cs->c->seats, s)
{
if (s->kbd.enter == cs) s->kbd.enter = NULL;
if (s->ptr.enter == cs) s->ptr.enter = NULL;
if (s->ptr.cursor.surface == cs)
{
if (s->ptr.in)
{
const Eina_List *l;
Eo *dev;
Ecore_Evas *ee = ecore_evas_ecore_evas_get(s->c->evas);
EINA_LIST_FOREACH(evas_device_list(s->c->evas, s->dev), l, dev)
if (evas_device_class_get(dev) == EVAS_DEVICE_CLASS_MOUSE)
ecore_evas_cursor_device_unset(ee, dev);
}
s->ptr.cursor.surface = NULL;
}
if (s->drag.surface == cs) s->drag.surface = NULL;
}
eina_hash_list_remove(cs->c->client_surfaces, &client, cs);
if (cs->render_queue)
cs->c->render_queue = eina_list_remove(cs->c->render_queue, cs);
subsurfaces = cs->pending_subsurfaces ?: cs->subsurfaces;
EINA_LIST_FREE(subsurfaces, css)
{
evas_object_hide(css->surface->obj);
comp_surface_reparent(css->surface, NULL);
}
if (cs->pending_subsurfaces) eina_list_free(cs->subsurfaces);
cs->pending_subsurfaces = cs->subsurfaces = NULL;
comp_surface_buffer_detach(&cs->buffer[0]);
cs->res = NULL;
if (cs->post_render_queue && (!cs->dead))
{
Eina_List *l;
Eo *o;
cs->dead = 1;
evas_object_hide(cs->obj);
EINA_LIST_FOREACH(cs->proxies, l, o)
evas_object_hide(o);
}
else
{
comp_surface_buffer_detach(&cs->buffer[1]);
if (!cs->dead) evas_object_del(cs->obj);
}
}
static inline Eina_Bool
comp_surface_check_grab(Comp_Surface *cs, Comp_Seat *s)
{
Comp_Surface *parent;
if (!s->grab) return EINA_TRUE;
if (cs == s->grab) return EINA_TRUE;
parent = s->grab->parent;
while (parent)
{
if (cs == parent) return EINA_TRUE;
parent = parent->parent;
}
return EINA_FALSE;
}
static void
comp_surface_input_event(Eina_Inlist **list, uint32_t id, uint32_t serial, uint32_t time, Eina_Bool up)
{
Input_Sequence *ev;
if (up)
{
EINA_INLIST_FOREACH(*list, ev)
if (ev->id == id)
{
ev->up_serial = serial;
ev->up_time = time;
return;
}
return;
}
ev = calloc(1, sizeof(Input_Sequence));
ev->id = id;
ev->down_serial = serial;
ev->down_time = time;
*list = eina_inlist_append(*list, EINA_INLIST_GET(ev));
}
static void
comp_surface_send_data_device_enter(Comp_Surface *cs, Comp_Seat *s)
{
struct wl_resource *offer = NULL;
int x, y, cx, cy;
uint32_t serial;
struct wl_resource *res = data_device_find(s, cs->res);
if (!res) return;
evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
if (s->drag.tch)
cx = s->tch.pos.x, cy = s->tch.pos.y;
else
cx = s->ptr.pos.x, cy = s->ptr.pos.y;
if (s->drag.source)
{
data_device_offer_create(s->drag.source, res);
s->drag.source->offer->type = COMP_DATA_DEVICE_OFFER_TYPE_DND;
offer = s->drag.source->offer->res;
}
s->drag.enter = cs;
s->ptr.enter_serial = serial = wl_display_next_serial(cs->c->display);
wl_data_device_send_enter(res, serial, cs->res,
wl_fixed_from_int(cx - x), wl_fixed_from_int(cy - y), offer);
}
static Eina_Bool
comp_surface_send_pointer_enter(Comp_Surface *cs, Comp_Seat *s, int cx, int cy)
{
Eina_List *l, *ll;
struct wl_resource *res;
uint32_t serial;
int x, y;
if (s->ptr.enter && (cs != s->grab)) return EINA_FALSE;
if (!comp_surface_check_grab(cs, s)) return EINA_FALSE;
s->ptr.enter = cs;
if (cs->dead) return EINA_FALSE;
if (s->drag.res && (!s->drag.tch))
{
comp_surface_send_data_device_enter(cs, s);
return EINA_TRUE;
}
l = seat_ptr_resources_get(s, wl_resource_get_client(cs->res));
if (!l) return EINA_FALSE;
s->ptr.enter_serial = serial = wl_display_next_serial(cs->c->display);
//fprintf(stderr, "ENTER %s\n", cs->shell.popup ? "POPUP" : "TOPLEVEL");
evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
EINA_LIST_FOREACH(l, ll, res)
wl_pointer_send_enter(res, serial, cs->res,
wl_fixed_from_int(cx - x), wl_fixed_from_int(cy - y));
return EINA_TRUE;
}
static void
comp_surface_mouse_in(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info)
{
Evas_Event_Mouse_In *ev = event_info;
Comp_Seat *s;
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
s = seat_find(data, ev->dev);
if (comp_surface_send_pointer_enter(data, s, ev->canvas.x, ev->canvas.y))
{
s->event_propagate = 1;
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
}
}
static void
comp_surface_send_data_device_leave(Comp_Surface *cs, Comp_Seat *s)
{
struct wl_resource *res = data_device_find(s, cs->res);
if (!res) return;
if (s->drag.source)
{
s->drag.source->offer->source = NULL;
s->drag.source->offer = NULL;
}
s->drag.enter = NULL;
wl_data_device_send_leave(res);
}
static Eina_Bool
comp_surface_send_pointer_leave(Comp_Surface *cs, Comp_Seat *s)
{
Eina_List *l, *ll;
struct wl_resource *res;
uint32_t serial;
if (s->ptr.enter != cs) return EINA_FALSE;
if (!comp_surface_check_grab(cs, s)) return EINA_FALSE;
s->ptr.enter = NULL;
if (cs->dead) return EINA_FALSE;
if (s->drag.res)
{
comp_surface_send_data_device_leave(cs, s);
return EINA_TRUE;
}
l = seat_ptr_resources_get(s, wl_resource_get_client(cs->res));
if (!l) return EINA_FALSE;
serial = wl_display_next_serial(cs->c->display);
//fprintf(stderr, "LEAVE %s\n", cs->shell.popup ? "POPUP" : "TOPLEVEL");
EINA_LIST_FOREACH(l, ll, res)
wl_pointer_send_leave(res, serial, cs->res);
return EINA_TRUE;
}
static void
comp_surface_mouse_out(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info)
{
Evas_Event_Mouse_Out *ev = event_info;
Comp_Seat *s;
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
s = seat_find(data, ev->dev);
if (comp_surface_send_pointer_leave(data, s))
{
s->event_propagate = 1;
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
}
}
static Eina_Bool
comp_surface_mouse_button(Comp_Surface *cs, Comp_Seat *s, uint32_t timestamp, uint32_t button_id, uint32_t state)
{
uint32_t serial, btn;
Eina_List *l, *ll;
struct wl_resource *res;
switch (button_id)
{
case 1:
btn = BTN_LEFT;
break;
case 2:
btn = BTN_MIDDLE;
break;
case 3:
btn = BTN_RIGHT;
break;
case 4:
btn = BTN_SIDE;
break;
case 5:
btn = BTN_EXTRA;
break;
case 6:
btn = BTN_FORWARD;
break;
case 7:
btn = BTN_BACK;
break;
default:
btn = button_id + BTN_SIDE - 8;
break;
}
if (s->ptr.enter != cs) return EINA_FALSE;
if (!comp_surface_check_grab(cs, s)) return EINA_FALSE;
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
s->ptr.button_mask |= 1 << button_id;
else
{
if (!(s->ptr.button_mask & (1 << button_id))) return EINA_FALSE;
s->ptr.button_mask &= ~(1 << button_id);
if (s->drag.res && (!s->drag.tch))
{
drag_grab_button(s, timestamp, button_id, WL_POINTER_BUTTON_STATE_RELEASED);
comp_surface_input_event(&s->ptr.events, button_id, 0, timestamp, state == WL_POINTER_BUTTON_STATE_RELEASED);
s->ptr.enter = NULL;
comp_surface_send_pointer_enter(cs, s, s->ptr.pos.x, s->ptr.pos.y);
return EINA_TRUE;
}
}
if (cs->dead)
{
comp_surface_input_event(&s->ptr.events, button_id, 0, timestamp, state == WL_POINTER_BUTTON_STATE_RELEASED);
return EINA_TRUE;
}
l = seat_ptr_resources_get(s, wl_resource_get_client(cs->res));
if (!l) return EINA_FALSE;
serial = wl_display_next_serial(s->c->display);
comp_surface_input_event(&s->ptr.events, button_id, serial, timestamp, state == WL_POINTER_BUTTON_STATE_RELEASED);
EINA_LIST_FOREACH(l, ll, res)
wl_pointer_send_button(res, serial, timestamp, btn, state);
return EINA_TRUE;
}
static void
comp_surface_mouse_down(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info)
{
Evas_Event_Mouse_Down *ev = event_info;
Comp_Seat *s;
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
s = seat_find(data, ev->dev);
if (comp_surface_mouse_button(data, s, ev->timestamp, ev->button, WL_POINTER_BUTTON_STATE_PRESSED))
{
s->event_propagate = 1;
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
}
}
static void
comp_surface_mouse_up(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info)
{
Evas_Event_Mouse_Down *ev = event_info;
Comp_Seat *s;
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
s = seat_find(data, ev->dev);
if (comp_surface_mouse_button(data, s, ev->timestamp, ev->button, WL_POINTER_BUTTON_STATE_RELEASED))
{
s->event_propagate = 1;
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
}
}
static void
comp_surface_mouse_move(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info)
{
Evas_Event_Mouse_Move *ev = event_info;
Comp_Surface *cs = data;
Eina_List *l = NULL, *ll;
struct wl_resource *res;
int x, y;
Comp_Seat *s;
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
if (cs->dead) return;
s = seat_find(data, ev->dev);
if (s->ptr.enter != cs) return;
if (!comp_surface_check_grab(cs, s)) return;
if (s->drag.res)
{
if (s->drag.enter != cs) return;
res = data_device_find(s, cs->res);
if (!res) return;
}
else
{
l = seat_ptr_resources_get(s, wl_resource_get_client(cs->res));
if (!l) return;
}
evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
//fprintf(stderr, "MOTION %s\n", cs->shell.popup ? "POPUP" : "TOPLEVEL");
if (s->drag.res)
wl_data_device_send_motion(res, ev->timestamp,
wl_fixed_from_int(ev->cur.canvas.x - x), wl_fixed_from_int(ev->cur.canvas.y - y));
else
{
//int n = 0;
EINA_LIST_FOREACH(l, ll, res)
{
//fprintf(stderr, "motion %d\n", n++);
wl_pointer_send_motion(res, ev->timestamp,
wl_fixed_from_int(ev->cur.canvas.x - x), wl_fixed_from_int(ev->cur.canvas.y - y));
}
}
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
s->event_propagate = 1;
}
static void
comp_surface_mouse_wheel(void *data, Evas *evas EINA_UNUSED, Eo *obj EINA_UNUSED, void *event)
{
Evas_Event_Mouse_Wheel *ev = event;
Comp_Surface *cs = data;
Eina_List *l, *ll;
struct wl_resource *res;
uint32_t axis, dir;
Comp_Seat *s;
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
if (ev->direction == 0)
axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
else
axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
if (ev->z < 0)
dir = -wl_fixed_from_int(abs(10 * ev->z));
else
dir = wl_fixed_from_int(10 * ev->z);
if (cs->dead) return;
s = seat_find(data, ev->dev);
if (s->ptr.enter != cs) return;
if (!comp_surface_check_grab(cs, s)) return;
l = seat_ptr_resources_get(s, wl_resource_get_client(cs->res));
if (!l) return;
EINA_LIST_FOREACH(l, ll, res)
wl_pointer_send_axis(res, ev->timestamp, axis, dir);
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
s->event_propagate = 1;
}
static void
comp_surface_multi_down(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info)
{
Evas_Event_Multi_Down *ev = event_info;
Comp_Surface *cs = data;
Eina_List *l, *ll;
struct wl_resource *res;
uint32_t serial;
int x, y;
Comp_Seat *s;
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
if (cs->dead) return;
s = seat_find(data, ev->dev);
if (!comp_surface_check_grab(cs, s)) return;
s->tch.enter = cs;
l = seat_tch_resources_get(s, wl_resource_get_client(cs->res));
if (!l)
{
comp_surface_input_event(&s->tch.events, ev->device, 0, ev->timestamp, 0);
s->event_propagate = 1;
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
return;
}
evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
serial = wl_display_next_serial(cs->c->display);
comp_surface_input_event(&s->tch.events, ev->device, serial, ev->timestamp, 0);
EINA_LIST_FOREACH(l, ll, res)
wl_touch_send_down(res, serial, ev->timestamp, cs->res, ev->device,
wl_fixed_from_int(ev->canvas.x - x), wl_fixed_from_int(ev->canvas.y - y));
s->event_propagate = 1;
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
}
static void
comp_surface_multi_up(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info)
{
Evas_Event_Multi_Up *ev = event_info;
Comp_Surface *cs = data;
Eina_List *l, *ll;
struct wl_resource *res;
uint32_t serial;
Comp_Seat *s;
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
if (cs->dead) return;
s = seat_find(data, ev->dev);
if (!comp_surface_check_grab(cs, s)) return;
if (!ev->device)
s->tch.enter = NULL;
l = seat_tch_resources_get(s, wl_resource_get_client(cs->res));
if ((!l) || (s->drag.tch && ((uint32_t)ev->device == s->drag.id)))
{
if (s->drag.tch)
{
res = data_device_find(s, cs->res);
if (!res) return;
wl_data_device_send_drop(res);
}
comp_surface_input_event(&s->tch.events, ev->device, 0, ev->timestamp, 1);
s->event_propagate = 1;
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
return;
}
serial = wl_display_next_serial(cs->c->display);
comp_surface_input_event(&s->tch.events, ev->device, serial, ev->timestamp, 1);
EINA_LIST_FOREACH(l, ll, res)
wl_touch_send_up(res, serial, ev->timestamp, ev->device);
s->event_propagate = 1;
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
}
static void
comp_surface_multi_move(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info)
{
Evas_Event_Multi_Move *ev = event_info;
Comp_Surface *cs = data;
struct wl_resource *res;
int x, y;
Comp_Seat *s;
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
if (cs->dead) return;
s = seat_find(data, ev->dev);
if (!comp_surface_check_grab(cs, s)) return;
evas_object_geometry_get(cs->obj, &x, &y, NULL, NULL);
if (s->drag.tch)
{
if (s->drag.enter != cs) return;
res = data_device_find(s, cs->res);
if (res)
{
wl_data_device_send_motion(res, ev->timestamp,
wl_fixed_from_int(ev->cur.canvas.x - x), wl_fixed_from_int(ev->cur.canvas.y - y));
s->event_propagate = 1;
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
}
return;
}
else
{
Eina_List *l, *ll;
l = seat_tch_resources_get(s, wl_resource_get_client(cs->res));
if (!l) return;
EINA_LIST_FOREACH(l, ll, res)
wl_touch_send_motion(res, ev->timestamp, ev->device,
wl_fixed_from_int(ev->cur.canvas.x - x), wl_fixed_from_int(ev->cur.canvas.y - y));
s->event_propagate = 1;
ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
}
}
EOLIAN static Eo *
_efl_canvas_wl_surface_efl_object_constructor(Eo *obj, Comp_Surface *cs EINA_UNUSED)
{
efl_canvas_group_clipped_set(obj, EINA_TRUE);
return efl_constructor(efl_super(obj, EFL_CANVAS_WL_SURFACE_CLASS));
}
static void
_efl_canvas_wl_surface_efl_canvas_group_group_add(Eo *obj, Comp_Surface *cs)
{
Evas *e;
efl_canvas_group_add(efl_super(obj, EFL_CANVAS_WL_SURFACE_CLASS));
cs->obj = obj;
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_DOWN, comp_surface_mouse_down, cs);
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_UP, comp_surface_mouse_up, cs);
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_IN, comp_surface_mouse_in, cs);
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_OUT, comp_surface_mouse_out, cs);
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_MOVE, comp_surface_mouse_move, cs);
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MOUSE_WHEEL, comp_surface_mouse_wheel, cs);
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MULTI_DOWN, comp_surface_multi_down, cs);
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MULTI_UP, comp_surface_multi_up, cs);
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_MULTI_MOVE, comp_surface_multi_move, cs);
e = evas_object_evas_get(obj);
cs->img = evas_object_image_filled_add(e);
evas_object_show(cs->img);
cs->clip = (Eo*)efl_canvas_group_clipper_get(obj);
efl_gfx_entity_geometry_set(cs->clip, efl_gfx_entity_geometry_get(obj));
evas_object_smart_member_add(cs->img, cs->obj);
evas_object_image_border_center_fill_set(cs->img, EVAS_BORDER_FILL_SOLID);
evas_object_image_colorspace_set(cs->img, EVAS_COLORSPACE_ARGB8888);
}
static void
_efl_canvas_wl_surface_efl_canvas_group_group_del(Eo *obj, Comp_Surface *cs)
{
array_clear(&cs->input_rects);
array_clear(&cs->opaque_rects);
eina_tiler_free(cs->opaque);
eina_tiler_free(cs->input);
comp_buffer_state_clear(&cs->pending);
while (cs->proxies)
evas_object_del(eina_list_data_get(cs->proxies));
if (cs->res)
{
cs->dead = 1;
wl_resource_destroy(cs->res);
}
evas_object_del(cs->img);
if (cs->shell.surface)
{
if (cs->role)
wl_resource_destroy(cs->role);
wl_resource_destroy(cs->shell.surface);
}
cs->c->surfaces = eina_inlist_remove(cs->c->surfaces, EINA_INLIST_GET(cs));
cs->c->surfaces_count--;
efl_canvas_group_del(efl_super(obj, EFL_CANVAS_WL_SURFACE_CLASS));
}
static void
_efl_canvas_wl_surface_efl_gfx_entity_position_set(Eo *obj, Comp_Surface *cs, Eina_Position2D pos)
{
efl_gfx_entity_position_set(efl_super(obj, EFL_CANVAS_WL_SURFACE_CLASS), pos);
efl_gfx_entity_position_set(cs->clip, pos);
//{
//if (cs->cursor)
//fprintf(stderr, "COMP %sSURFACE(%p) %d,%d\n", cs->subsurface ? "SUB" : "", cs, x, y);
//}
}
static void
_efl_canvas_wl_surface_efl_gfx_entity_size_set(Eo *obj, Comp_Surface *cs, Eina_Size2D sz)
{
efl_gfx_entity_size_set(efl_super(obj, EFL_CANVAS_WL_SURFACE_CLASS), sz);
evas_object_resize(cs->clip, sz.w, sz.h);
//if (cs->cursor) fprintf(stderr, "COMP %sSURFACE(%p) %dx%d\n", cs->subsurface ? "SUB" : "", cs, w, h);
if (cs->drag)
evas_object_move(cs->obj, cs->drag->ptr.pos.x, cs->drag->ptr.pos.y);
}
static void
_efl_canvas_wl_surface_efl_gfx_entity_visible_set(Eo *obj, Comp_Surface *cs, Eina_Bool vis)
{
Comp_Surface *pcs = NULL, *lcs;
efl_gfx_entity_visible_set(efl_super(obj, EFL_CANVAS_WL_SURFACE_CLASS), vis);
if (vis)
{
evas_object_show(cs->clip);
cs->mapped = 1;
return;
}
evas_object_hide(cs->clip);
cs->mapped = 0;
if (!cs->shell.activated) return;
cs->shell.activated = 0;
if (cs->shell.popup && cs->role)
xdg_popup_send_popup_done(cs->role);
if (cs->parent && cs->shell.popup) return;
/* attempt to revert focus based on stacking order */
if (cs->parent)
{
EINA_INLIST_REVERSE_FOREACH(cs->parent->children, lcs)
{
if (lcs == cs) continue;
if (!evas_object_visible_get(lcs->obj)) continue;
if ((!lcs->shell.surface) || (!lcs->role)) continue;
lcs->shell.activated = 1;
if (lcs->shell.popup)
{
if (!lcs->extracted)
evas_object_raise(lcs->obj);
}
else
shell_surface_send_configure(lcs);
return;
}
if (!cs->parent->shell.popup)
{
pcs = cs->parent;
if (!pcs->mapped) pcs = NULL;
}
}
if (cs->c->seats)
comp_seats_redo_enter(cs->c, pcs);
}
static void
comp_surface_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
struct wl_resource *res;
Comp_Surface *cs;
Comp *c = wl_resource_get_user_data(resource);
Eo *obj;
int x, y;
res = wl_resource_create(client, &wl_surface_interface, wl_resource_get_version(resource), id);
obj = efl_add(EFL_CANVAS_WL_SURFACE_CLASS, c->obj);
efl_gfx_entity_visible_set(obj, EINA_FALSE);
cs = efl_data_scope_get(obj, EFL_CANVAS_WL_SURFACE_CLASS);
cs->res = res;
evas_object_smart_member_add(cs->obj, c->obj);
cs->c = c;
evas_object_geometry_get(c->obj, &x, &y, NULL, NULL);
evas_object_move(cs->obj, x, y);
c->surfaces = eina_inlist_prepend(c->surfaces, EINA_INLIST_GET(cs));
c->surfaces_count++;
eina_hash_list_append(c->client_surfaces, &client, cs);
if (evas_object_visible_get(cs->c->clip))
comp_surface_output_enter(cs);
else
comp_surface_output_leave(cs);
cs->opaque = tiler_new();
cs->input = tiler_new();
comp_buffer_state_alloc(&cs->pending);
cs->pending.set_input = 1;
eina_tiler_rect_add(cs->pending.input, &(Eina_Rectangle){0, 0, 65535, 65535});
wl_resource_set_implementation(res, &comp_surface_interface, cs, comp_surface_impl_destroy);
}
/////////////////////////////////////////////////////////////////
static void
comp_region_add(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
Eina_Tiler *tiler = wl_resource_get_user_data(resource);
eina_tiler_rect_add(tiler, &(Eina_Rectangle){x, y, w, h});
}
static void
comp_region_subtract(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
Eina_Tiler *tiler = wl_resource_get_user_data(resource);
eina_tiler_rect_del(tiler, &(Eina_Rectangle){x, y, w, h});
}
static const struct wl_region_interface comp_region_interface =
{
resource_destroy,
comp_region_add,
comp_region_subtract
};
static void
comp_region_impl_destroy(struct wl_resource *resource)
{
eina_tiler_free(wl_resource_get_user_data(resource));
}
static void
comp_region_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
Eina_Tiler *tiler;
struct wl_resource *res;
tiler = tiler_new();
res = wl_resource_create(client, &wl_region_interface, 1, id);
wl_resource_set_implementation(res, &comp_region_interface, tiler, comp_region_impl_destroy);
}
/////////////////////////////////////////////////////////////////
static const struct wl_compositor_interface comp_interface =
{
comp_surface_create,
comp_region_create,
};
static void
comp_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct wl_resource *res;
if (!client_allowed_check(data, client)) return;
res = wl_resource_create(client, &wl_compositor_interface, version, id);
wl_resource_set_implementation(res, &comp_interface, data, NULL);
}
/////////////////////////////////////////////////////////////////
static void
subcomp_subsurface_impl_destroy(struct wl_resource *resource)
{
Comp_Subsurface *css = wl_resource_get_user_data(resource);
evas_object_hide(css->surface->obj);
if (css->surface->parent)
{
css->surface->parent->subsurfaces = eina_list_remove(css->surface->parent->subsurfaces, css);
if (css->surface->parent->pending_subsurfaces)
css->surface->parent->pending_subsurfaces = eina_list_remove(css->surface->parent->pending_subsurfaces, css);
}
comp_surface_reparent(css->surface, NULL);
comp_buffer_state_clear(&css->cache);
comp_surface_buffer_detach(&css->cache.buffer);
css->surface->subsurface = NULL;
css->surface->role = NULL;
free(css);
}
static void
subcomp_subsurface_set_position(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y)
{
Comp_Subsurface *css = wl_resource_get_user_data(resource);
css->pending_offset.x = x;
css->pending_offset.y = y;
css->set_offset = 1;
}
static void
subcomp_subsurface_place_above(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *sibling_resource)
{
Comp_Subsurface *css = wl_resource_get_user_data(resource);
Comp_Subsurface *css2 = wl_resource_get_user_data(sibling_resource);
if ((!css) || (!css2))
{
wl_resource_post_error( (!css) ? resource : sibling_resource,
WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "surface is not a valid subsurface");
return;
}
if (!css->surface->parent->pending_subsurfaces)
css->surface->parent->pending_subsurfaces = eina_list_clone(css->surface->parent->subsurfaces);
css->surface->parent->pending_subsurfaces = eina_list_remove(css->surface->parent->pending_subsurfaces, css);
css->surface->parent->pending_subsurfaces = eina_list_append_relative(css->surface->parent->pending_subsurfaces, css, css2);
}
static void
subcomp_subsurface_place_below(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *sibling_resource)
{
Comp_Subsurface *css = wl_resource_get_user_data(resource);
Comp_Subsurface *css2 = wl_resource_get_user_data(sibling_resource);
if ((!css) || (!css2))
{
wl_resource_post_error( (!css) ? resource : sibling_resource,
WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "surface is not a valid subsurface");
return;
}
if (!css->surface->parent->pending_subsurfaces)
css->surface->parent->pending_subsurfaces = eina_list_clone(css->surface->parent->subsurfaces);
css->surface->parent->pending_subsurfaces = eina_list_remove(css->surface->parent->pending_subsurfaces, css);
css->surface->parent->pending_subsurfaces = eina_list_prepend_relative(css->surface->parent->pending_subsurfaces, css, css2);
}
static void
subcomp_subsurface_set_sync(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
Comp_Subsurface *css = wl_resource_get_user_data(resource);
css->sync = 1;
}
static void
subcomp_subsurface_set_desync(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
Comp_Subsurface *css = wl_resource_get_user_data(resource);
css->sync = 0;
}
static const struct wl_subsurface_interface subcomp_subsurface_interface =
{
resource_destroy,
subcomp_subsurface_set_position,
subcomp_subsurface_place_above,
subcomp_subsurface_place_below,
subcomp_subsurface_set_sync,
subcomp_subsurface_set_desync
};
static void
subcomp_subsurface_create(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *parent_resource)
{
Comp_Subsurface *css;
Comp_Surface *cs = wl_resource_get_user_data(surface_resource);
Comp_Surface *pcs = wl_resource_get_user_data(parent_resource);
if (surface_resource == parent_resource)
{
wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
"cannot create subsurface as its own child");
return;
}
if (cs->role)
{
wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
"surface already has a role");
return;
}
css = cs->subsurface = calloc(1, sizeof(Comp_Subsurface));
comp_buffer_state_alloc(&css->cache);
css->sync = 1;
evas_object_name_set(cs->img, "subsurface");
css->surface = cs;
if (!pcs->pending_subsurfaces)
pcs->pending_subsurfaces = eina_list_clone(pcs->subsurfaces);
pcs->pending_subsurfaces = eina_list_append(pcs->pending_subsurfaces, css);
comp_surface_reparent(cs, pcs);
cs->role = wl_resource_create(client, &wl_subsurface_interface, 1, id);
wl_resource_set_implementation(cs->role, &subcomp_subsurface_interface, css, subcomp_subsurface_impl_destroy);
}
static const struct wl_subcompositor_interface subcomp_interface =
{
resource_destroy,
subcomp_subsurface_create,
};
static void
subcomp_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct wl_resource *res;
if (!client_allowed_check(data, client)) return;
res = wl_resource_create(client, &wl_subcompositor_interface, version, id);
wl_resource_set_implementation(res, &subcomp_interface, data, NULL);
}
/////////////////////////////////////////////////////////////////
static void
data_device_source_offer(struct wl_client *client, struct wl_resource *resource, const char *type)
{
Comp_Data_Device_Source *ds = wl_resource_get_user_data(resource);
if ((!ds->proxy) || (!ds->x11_owner))
ds->mime_types = eina_list_append(ds->mime_types, eina_stringshare_add(type));
}
static void
data_device_source_set_actions(struct wl_client *client, struct wl_resource *resource, uint32_t dnd_actions)
{
Comp_Data_Device_Source *ds = wl_resource_get_user_data(resource);
if (ds->actions_set)
{
wl_resource_post_error(ds->res, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
"dnd_actions already set");
return;
}
if (dnd_actions & ~ALL_ACTIONS)
{
wl_resource_post_error(ds->res, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
"invalid dnd_actions");
return;
}
if (ds->seat && (!ds->proxy))
{
wl_resource_post_error(ds->res, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
"drag already begun");
return;
}
ds->dnd_actions = dnd_actions;
ds->actions_set = 1;
}
static void
data_device_source_impl_destroy(struct wl_resource *resource)
{
Comp_Data_Device_Source *ds = wl_resource_get_user_data(resource);
Eina_Stringshare *type;
EINA_LIST_FREE(ds->mime_types, type) eina_stringshare_del(type);
while (ds->transfers)
{
Comp_Data_Device_Transfer *dt = EINA_INLIST_CONTAINER_GET(ds->transfers, Comp_Data_Device_Transfer);
fdh_del(dt->fdh);
ds->transfers = eina_inlist_remove(ds->transfers, EINA_INLIST_GET(dt));
free(dt);
}
if (ds->seat && (ds->seat->selection_source == ds)) ds->seat->selection_source = NULL;
if (ds->seat && (ds->seat->drag.source == ds)) ds->seat->drag.source = NULL;
comp_data_device_source_reader_clear(ds);
ecore_event_handler_del(ds->proxy_send_handler);
if (ds->offer) ds->offer->source = NULL;
free(ds);
}
static const struct wl_data_source_interface data_device_source_interface =
{
data_device_source_offer,
resource_destroy,
data_device_source_set_actions,
};
static void
data_device_manager_source_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
Comp_Data_Device_Source *ds;
pid_t pid;
Comp_Seat *s;
Comp *c;
ds = calloc(1, sizeof(Comp_Data_Device_Source));
c = wl_resource_get_user_data(resource);
wl_client_get_credentials(client, &pid, NULL, NULL);
ds->proxy = c->client_disp && (pid == getpid());
ds->res = wl_resource_create(client, &wl_data_source_interface, MIN(wl_resource_get_version(resource), 3), id);
wl_resource_set_implementation(ds->res, &data_device_source_interface, ds, data_device_source_impl_destroy);
if (!ds->proxy) return;
EINA_INLIST_FOREACH(c->seats, s)
{
int x, y;
struct wl_resource *res;
Eina_Array *arr;
Eina_Array_Iterator it;
unsigned int i;
char *type;
if (((!s->client_offer) && (!s->drag.x11_owner)) || s->drag.res) continue;
//proxied drag
s->drag.res = resource;
s->drag.source = ds;
ds->seat = s;
if (!s->drag.enter) return;
evas_object_geometry_get(s->drag.enter->obj, &x, &y, NULL, NULL);
if (s->client_offer)
{
arr = ecore_wl2_offer_mimes_get(s->client_offer);
EINA_ARRAY_ITER_NEXT(arr, i, type, it)
ds->mime_types = eina_list_append(ds->mime_types, eina_stringshare_add(type));
}
else if (s->drag.x11_owner)
{
PTR_SWAP(&s->drag.x11_types, &ds->mime_types);
ds->x11_owner = s->drag.x11_owner;
s->drag.x11_owner = 0;
}
comp_surface_send_data_device_enter(s->drag.enter, s);
res = data_device_find(s, s->drag.enter->res);
if (!res) return;
wl_data_device_send_motion(res,
(unsigned int)((unsigned long long)(ecore_time_get() * 1000.0) & 0xffffffff),
wl_fixed_from_int(s->ptr.pos.x - x), wl_fixed_from_int(s->ptr.pos.y - y));
}
}
static void
data_device_manager_device_impl_destroy(struct wl_resource *resource)
{
Comp_Seat *s = wl_resource_get_user_data(resource);
struct wl_client *client = wl_resource_get_client(resource);
eina_hash_del_by_key(s->data_devices, &client);
}
static void
data_device_start_drag(struct wl_client *client, struct wl_resource *resource, struct wl_resource *source_resource, struct wl_resource *origin_resource, struct wl_resource *icon_resource, uint32_t serial)
{
Comp_Seat *s = wl_resource_get_user_data(resource);
Comp_Data_Device_Source *ds = NULL;
Comp_Surface *cs = wl_resource_get_user_data(origin_resource);
Comp_Surface *ics = wl_resource_get_user_data(icon_resource);
Comp_Surface *enter;
Input_Sequence *ev;
Eina_Bool found = EINA_FALSE, up = EINA_FALSE;
int cx, cy;
if (source_resource) ds = wl_resource_get_user_data(source_resource);
if ((cs != s->ptr.enter) && (cs != s->tch.enter)) return;
enter = s->ptr.enter;
if (enter) comp_surface_send_pointer_leave(enter, s);
EINA_INLIST_FOREACH(s->tch.events, ev)
{
if (ev->down_serial == serial)
{
s->drag.tch = 1;
up = !!ev->up_time;
found = 1;
s->drag.id = ev->id;
break;
}
}
if (!found)
{
EINA_INLIST_FOREACH(s->ptr.events, ev)
{
if (ev->down_serial == serial)
{
up = !!ev->up_time;
found = 1;
s->drag.id = ev->id;
break;
}
}
}
if (!found) return;
if (s->ptr.enter && (!s->drag.tch))
comp_surface_send_pointer_leave(s->ptr.enter, s);
s->drag.res = resource;
s->drag.source = ds;
if (ds) ds->seat = s;
s->drag.surface = ics;
if (ds && ds->proxy && s->drag.x11_owner)
ds->x11_owner = s->drag.x11_owner;
s->drag.x11_owner = 0;
if (s->drag.tch)
{
cx = s->tch.pos.x;
cy = s->tch.pos.y;
}
else
{
cx = s->ptr.pos.x;
cy = s->ptr.pos.y;
}
if (ics)
{
int w, h;
ics->cursor = 1;
ics->drag = s;
ics->pending.set_input = 1;
eina_tiler_clear(ics->pending.input);
evas_object_smart_member_del(ics->obj);
evas_object_pass_events_set(ics->obj, 1);
evas_object_layer_set(ics->obj, EVAS_LAYER_MAX - 1);
evas_object_geometry_get(ics->obj, NULL, NULL, &w, &h);
evas_object_move(ics->obj, cx, cy);
evas_object_show(ics->obj);
}
if (s->drag.tch)
{
if (s->tch.enter)
comp_surface_send_data_device_enter(s->tch.enter, s);
}
else if (enter)
comp_surface_send_pointer_enter(enter, s, cx, cy);
#ifdef HAVE_ECORE_X
if (ecore_x_display_get())
ecore_x_pointer_grab(ecore_evas_window_get(ecore_evas_ecore_evas_get(s->c->evas)));
#endif
if (up) drag_grab_button(s, ev->up_time, ev->id, WL_POINTER_BUTTON_STATE_RELEASED);
}
static Eina_Bool
data_device_proxy_send_send(void *d, int t EINA_UNUSED, void *event)
{
Comp_Data_Device_Source *ds = d;
Ecore_Wl2_Event_Data_Source_Send *ev = event;
if (ds->proxy_serial != ev->serial) return ECORE_CALLBACK_RENEW;
if (ev->display == ds->seat->c->parent_disp)
{
if (ecore_wl2_input_seat_id_get(ds->seat->seat) != ev->seat) return ECORE_CALLBACK_RENEW;
wl_data_source_send_send(ds->res, ev->type, ev->fd);
close(ev->fd);
}
return ECORE_CALLBACK_RENEW;
}
static void
data_device_set_selection(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *source_resource, uint32_t serial)
{
Comp_Seat *s = wl_resource_get_user_data(resource);
Comp_Data_Device_Source *ds = NULL;
if (source_resource)
ds = wl_resource_get_user_data(source_resource);
if (ds && ds->actions_set)
{
wl_resource_post_error(source_resource, WL_DATA_SOURCE_ERROR_INVALID_SOURCE, "invalid source");
return;
}
if (s->selection_source && (s->selection_serial - serial < UINT32_MAX / 2))
return;
if (s->selection_source)
{
if (!s->selection_source->transfers)
comp_data_device_source_reader_clear(s->selection_source);
ecore_event_handler_del(s->selection_source->proxy_send_handler);
s->selection_source->proxy_send_handler = NULL;
}
if (ds) ds->seat = s;
s->selection_source = ds;
s->selection_serial = serial;
comp_seat_kbd_data_device_enter(s);
if (ds && ds->proxy && s->x11_selection_owner)
ds->x11_owner = s->x11_selection_owner;
s->x11_selection_owner = 0;
if (1)
//if (s->c->data_device_proxy)
{
if (s->c->parent_disp) //wayland
{
if (ds && ds->mime_types)
{
char *t, *types[eina_list_count(ds->mime_types) + 1];
Eina_List *l;
int i = 0;
EINA_LIST_FOREACH(ds->mime_types, l, t)
types[i++] = t;
types[i] = NULL;
ds->proxy_serial = ecore_wl2_dnd_selection_set(s->seat, (const char**)types);
ds->proxy_send_handler =
ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_SEND,
data_device_proxy_send_send, ds);
}
else if (ds)
ds->proxy_serial = ecore_wl2_dnd_selection_clear(s->seat);
}
#ifdef HAVE_ECORE_X
else if (ds && (!ds->proxy) && ecore_x_display_get())
{
Eina_List *l;
Comp *c;
Ecore_X_Time t = ecore_x_current_time_get();
EINA_LIST_FOREACH(comps, l, c)
c->x11_selection = 0;
s->c->x11_selection = 1;
ecore_x_selection_owner_set(ecore_evas_window_get(ecore_evas_ecore_evas_get(s->c->evas)),
ECORE_X_ATOM_SELECTION_CLIPBOARD, t);
}
#endif
}
s->client_selection_serial = 0;
}
static const struct wl_data_device_interface data_device_interface =
{
data_device_start_drag,
data_device_set_selection,
resource_destroy,
};
static void
data_device_manager_device_create(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *seat_resource)
{
Comp_Seat *s = wl_resource_get_user_data(seat_resource);
struct wl_resource *res;
res = wl_resource_create(client, &wl_data_device_interface, wl_resource_get_version(manager_resource), id);
wl_resource_set_implementation(res, &data_device_interface, s, data_device_manager_device_impl_destroy);
eina_hash_add(s->data_devices, &client, res);
}
static const struct wl_data_device_manager_interface data_device_manager_interface =
{
data_device_manager_source_create,
data_device_manager_device_create
};
static void
data_device_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct wl_resource *res;
if (!client_allowed_check(data, client)) return;
res = wl_resource_create(client, &wl_data_device_manager_interface, MIN(3, version), id);
wl_resource_set_implementation(res, &data_device_manager_interface, data, NULL);
}
/////////////////////////////////////////////////////////////////
static void
output_resize(Comp *c, struct wl_resource *res)
{
int w, h;
int rot[][4] =
{
{
[EFL_CANVAS_WL_ROTATION_ROTATE_0] = WL_OUTPUT_TRANSFORM_NORMAL,
[EFL_CANVAS_WL_ROTATION_ROTATE_90] = WL_OUTPUT_TRANSFORM_90,
[EFL_CANVAS_WL_ROTATION_ROTATE_180] = WL_OUTPUT_TRANSFORM_180,
[EFL_CANVAS_WL_ROTATION_ROTATE_270] = WL_OUTPUT_TRANSFORM_270,
},
{
[EFL_CANVAS_WL_ROTATION_ROTATE_0] = WL_OUTPUT_TRANSFORM_FLIPPED,
[EFL_CANVAS_WL_ROTATION_ROTATE_90] = WL_OUTPUT_TRANSFORM_FLIPPED_90,
[EFL_CANVAS_WL_ROTATION_ROTATE_180] = WL_OUTPUT_TRANSFORM_FLIPPED_180,
[EFL_CANVAS_WL_ROTATION_ROTATE_270] = WL_OUTPUT_TRANSFORM_FLIPPED_270,
},
};
evas_object_geometry_get(c->clip, NULL, NULL, &w, &h);
/* FIXME: transform */
wl_output_send_geometry(res, 0, 0, w, h, 0, "", "", rot[c->rtl][c->rotation]);
wl_output_send_mode(res, WL_OUTPUT_MODE_CURRENT, w, h, 60 * 1000);
if (wl_resource_get_version(res) >= WL_OUTPUT_DONE_SINCE_VERSION)
wl_output_send_done(res);
}
static void
output_unbind(struct wl_resource *resource)
{
Comp *c = wl_resource_get_user_data(resource);
c->output_resources = eina_list_remove(c->output_resources, resource);
}
static void
output_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
Comp *c = data;
Comp_Surface *cs;
struct wl_resource *res;
if (!client_allowed_check(data, client)) return;
res = wl_resource_create(client, &wl_output_interface, version, id);
c->output_resources = eina_list_append(c->output_resources, res);
wl_resource_set_implementation(res, NULL, data, output_unbind);
if (wl_resource_get_version(res) >= WL_OUTPUT_SCALE_SINCE_VERSION)
wl_output_send_scale(res, lround(c->scale));
output_resize(c, res);
EINA_INLIST_FOREACH(c->surfaces, cs)
if (wl_resource_get_client(cs->res) == client)
{
if (evas_object_visible_get(c->clip))
comp_surface_output_enter(cs);
else
comp_surface_output_leave(cs);
}
}
/////////////////////////////////////////////////////////////////
static void
shell_surface_toplevel_impl_destroy(struct wl_resource *resource)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
cs->role = NULL;
evas_object_hide(cs->obj);
shell_surface_reset(cs);
if (!cs->parent) return;
comp_surface_reparent(cs, NULL);
}
static void
shell_surface_toplevel_set_parent(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *parent_resource)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
Comp_Surface *pcs = NULL;
if (parent_resource) pcs = wl_resource_get_user_data(parent_resource);
comp_surface_reparent(cs, pcs);
if (parent_resource)
efl_event_callback_call(cs->c->obj, EFL_CANVAS_WL_EVENT_CHILD_ADDED, cs->obj);
}
static void
shell_surface_toplevel_set_title(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *title)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
eina_stringshare_replace(&cs->shell.title, title);
}
static void
shell_surface_toplevel_set_app_id(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *id)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
eina_stringshare_replace(&cs->shell.app_id, id);
}
static void
shell_surface_toplevel_show_window_menu(){}
static void
shell_surface_toplevel_move(){}
static void
shell_surface_toplevel_resize(){}
static void
shell_surface_toplevel_set_max_size(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t w, int32_t h)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
evas_object_size_hint_max_set(cs->obj, w, h);
if (cs == cs->c->active_surface)
shell_surface_minmax_update(cs);
}
static void
shell_surface_toplevel_set_min_size(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t w, int32_t h)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
evas_object_size_hint_min_set(cs->obj, w, h);
if (cs == cs->c->active_surface)
shell_surface_minmax_update(cs);
}
static void
shell_surface_toplevel_set_maximized(){}
static void
shell_surface_toplevel_unset_maximized(){}
static void
shell_surface_toplevel_set_fullscreen(){}
static void
shell_surface_toplevel_unset_fullscreen(){}
static void
shell_surface_toplevel_set_minimized(){}
static const struct xdg_toplevel_interface shell_surface_toplevel_interface =
{
resource_destroy,
shell_surface_toplevel_set_parent,
shell_surface_toplevel_set_title,
shell_surface_toplevel_set_app_id,
shell_surface_toplevel_show_window_menu,
shell_surface_toplevel_move,
shell_surface_toplevel_resize,
shell_surface_toplevel_set_max_size,
shell_surface_toplevel_set_min_size,
shell_surface_toplevel_set_maximized,
shell_surface_toplevel_unset_maximized,
shell_surface_toplevel_set_fullscreen,
shell_surface_toplevel_unset_fullscreen,
shell_surface_toplevel_set_minimized,
};
static void
shell_surface_toplevel_create(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t id)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
if (cs->buffer[0] || cs->pending.buffer)
{
wl_resource_post_error(resource, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER,
"buffer attached/committed before configure");
return;
}
if (cs->role)
{
wl_resource_post_error(resource, XDG_WM_BASE_ERROR_ROLE,
"surface already has assigned role");
return;
}
cs->role = wl_resource_create(client, &xdg_toplevel_interface, 1, id);
wl_resource_set_implementation(cs->role, &shell_surface_toplevel_interface, cs, shell_surface_toplevel_impl_destroy);
cs->shell.new = 1;
efl_event_callback_call(cs->c->obj, EFL_CANVAS_WL_EVENT_TOPLEVEL_ADDED, cs->obj);
}
static void
shell_surface_popup_grab(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial EINA_UNUSED)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
Comp_Seat *s = wl_resource_get_user_data(seat);
if (cs->dead || (!cs->role) || (!cs->shell.surface))
{
wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "can't grab for this resource");
return;
}
if (cs->mapped)
{
wl_resource_post_error(resource, XDG_POPUP_ERROR_INVALID_GRAB,
"grab requested on mapped popup");
return;
}
if (cs->parent->shell.popup && (s->grab != cs->parent))
{
wl_resource_post_error(resource, XDG_POPUP_ERROR_INVALID_GRAB,
"grab requested on ungrabbed nested popup");
return;
}
s->grab = cs;
cs->shell.grabs = eina_list_append(cs->shell.grabs, s);
}
static const struct xdg_popup_interface shell_surface_popup_interface =
{
resource_destroy,
shell_surface_popup_grab,
};
static void
shell_surface_popup_impl_destroy(struct wl_resource *resource)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
Comp_Seat *s;
cs->role = NULL;
evas_object_hide(cs->obj);
cs->shell.popup = 0;
shell_surface_reset(cs);
EINA_LIST_FREE(cs->shell.grabs, s)
if (s->grab == cs)
{
if (cs->parent->shell.grabs &&
eina_list_data_find(cs->parent->shell.grabs, s))
s->grab = cs->parent;
else
s->grab = NULL;
}
if (cs->children)
wl_resource_post_error(cs->shell.surface, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES,
"popups dismissed out of order");
if (cs->parent)
comp_surface_reparent(cs, NULL);
}
static void
shell_surface_popup_create(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *parent_resource, struct wl_resource *positioner_resource)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
if (cs->buffer[0] || cs->pending.buffer)
{
wl_resource_post_error(resource, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER,
"buffer attached/committed before configure");
return;
}
if (cs->role)
{
wl_resource_post_error(resource, XDG_WM_BASE_ERROR_ROLE,
"surface already has assigned role");
return;
}
cs->role = wl_resource_create(client, &xdg_popup_interface, 1, id);
wl_resource_set_implementation(cs->role, &shell_surface_popup_interface, cs, shell_surface_popup_impl_destroy);
cs->shell.new = 1;
cs->shell.popup = 1;
if(parent_resource)
comp_surface_reparent(cs, wl_resource_get_user_data(parent_resource));
cs->shell.positioner = wl_resource_get_user_data(positioner_resource);
_apply_positioner(cs, cs->shell.positioner);
efl_event_callback_call(cs->c->obj, EFL_CANVAS_WL_EVENT_POPUP_ADDED, cs->obj);
}
static void
_validate_size(struct wl_resource *resource, int32_t value)
{
if (value <= 0)
wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid size passed");
}
static void
_validate_size_negative(struct wl_resource *resource, int32_t value)
{
if (value < 0)
wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid size passed");
}
static void
shell_positioner_set_size(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, int32_t w, int32_t h)
{
Shell_Positioner *p = wl_resource_get_user_data(resource);
_validate_size(resource, w);
_validate_size(resource, h);
p->size.w = w;
p->size.h = h;
}
static void
shell_positioner_set_anchor_rect(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
Shell_Positioner *p = wl_resource_get_user_data(resource);
_validate_size_negative(resource, w);
_validate_size_negative(resource, h);
EINA_RECTANGLE_SET(&p->anchor_rect, x, y, w, h);
}
static void
shell_positioner_set_anchor(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, enum xdg_positioner_anchor anchor)
{
Shell_Positioner *p = wl_resource_get_user_data(resource);
p->anchor = anchor;
}
static void
shell_positioner_set_gravity(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, enum xdg_positioner_gravity gravity)
{
Shell_Positioner *p = wl_resource_get_user_data(resource);
if ((gravity & (XDG_POSITIONER_GRAVITY_TOP | XDG_POSITIONER_GRAVITY_BOTTOM)) ==
(XDG_POSITIONER_GRAVITY_TOP | XDG_POSITIONER_GRAVITY_BOTTOM))
wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid gravity values passed");
else if ((gravity & (XDG_POSITIONER_GRAVITY_LEFT | XDG_POSITIONER_GRAVITY_RIGHT)) ==
(XDG_POSITIONER_GRAVITY_LEFT | XDG_POSITIONER_GRAVITY_RIGHT))
wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid gravity values passed");
else
p->gravity = gravity;
}
static void
shell_positioner_set_constraint_adjustment(struct wl_client *wl_client EINA_UNUSED, struct wl_resource *resource, enum xdg_positioner_constraint_adjustment constraint_adjustment)
{
Shell_Positioner *p = wl_resource_get_user_data(resource);
p->constrain = constraint_adjustment;
}
static void
shell_positioner_set_offset(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y)
{
Shell_Positioner *p = wl_resource_get_user_data(resource);
p->offset.x = x;
p->offset.y = y;
}
static const struct xdg_positioner_interface shell_positioner_interface =
{
resource_destroy,
shell_positioner_set_size,
shell_positioner_set_anchor_rect,
shell_positioner_set_anchor,
shell_positioner_set_gravity,
shell_positioner_set_constraint_adjustment,
shell_positioner_set_offset,
};
static void
shell_positioner_impl_destroy(struct wl_resource *resource)
{
Shell_Positioner *sp;
sp = wl_resource_get_user_data(resource);
if (!sp) return;
if (sp->sd) sp->sd->positioners = eina_inlist_remove(sp->sd->positioners, EINA_INLIST_GET(sp));
free(sp);
}
static void
shell_positioner_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
struct wl_resource *res;
Shell_Data *sd;
Shell_Positioner *sp;
sd = wl_resource_get_user_data(resource);
res = wl_resource_create(client, &xdg_positioner_interface, 1, id);
sp = calloc(1, sizeof(Shell_Positioner));
sp->anchor_rect.w = sp->anchor_rect.h = -1;
sp->sd = sd;
sp->res = res;
sd->positioners = eina_inlist_append(sd->positioners, EINA_INLIST_GET(sp));
wl_resource_set_implementation(res, &shell_positioner_interface, sp, shell_positioner_impl_destroy);
}
static void
shell_surface_set_window_geometry(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
Comp_Surface *cs = wl_resource_get_user_data(resource);
EINA_RECTANGLE_SET(&cs->shell.geom, x, y, w, h);
}
static void
shell_surface_ack_configure(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t serial)
{
}
static const struct xdg_surface_interface shell_surface_interface =
{
resource_destroy,
shell_surface_toplevel_create,
shell_surface_popup_create,
shell_surface_set_window_geometry,
shell_surface_ack_configure,
};
static void
shell_surface_impl_destroy(struct wl_resource *resource)
{
Comp_Surface *ccs, *cs = wl_resource_get_user_data(resource);
if (cs->role)
{
wl_resource_post_error(resource, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES, "shell surface destroyed before role surfaces");
wl_resource_destroy(cs->role);
}
cs->shell.surface = NULL;
cs->shell.data->surfaces = eina_list_remove(cs->shell.data->surfaces, cs);
cs->shell.data = NULL;
shell_surface_reset(cs);
while (cs->children)
{
ccs = EINA_INLIST_CONTAINER_GET(cs->children, Comp_Surface);
evas_object_hide(ccs->obj);
comp_surface_reparent(ccs, cs->parent);
}
}
static void
shell_surface_create(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource)
{
Shell_Data *sd = wl_resource_get_user_data(resource);
Comp_Surface *cs = wl_resource_get_user_data(surface_resource);
if (cs->role || cs->shell.surface)
{
wl_resource_post_error(surface_resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "surface already has role");
return;
}
cs->shell.surface = wl_resource_create(client, &xdg_surface_interface, 1, id);
cs->shell.data = sd;
wl_resource_set_implementation(cs->shell.surface, &shell_surface_interface, cs, shell_surface_impl_destroy);
sd->surfaces = eina_list_append(sd->surfaces, cs);
}
static void
shell_pong(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t serial EINA_UNUSED)
{
Shell_Data *sd = wl_resource_get_user_data(resource);
sd->ping = 0;
}
static const struct xdg_wm_base_interface shell_interface =
{
resource_destroy,
shell_positioner_create,
shell_surface_create,
shell_pong
};
static void
shell_unbind(struct wl_resource *resource)
{
Shell_Data *sd = wl_resource_get_user_data(resource);
sd->c->shells = eina_inlist_remove(sd->c->shells, EINA_INLIST_GET(sd));
while (sd->surfaces)
{
Comp_Surface *cs = eina_list_data_get(sd->surfaces);
if (cs->shell.surface)
{
if (cs->role)
wl_resource_destroy(cs->role);
wl_resource_destroy(cs->shell.surface);
}
}
free(sd);
}
static void
shell_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
Comp *c = data;
struct wl_resource *res;
Shell_Data *sd;
if (!client_allowed_check(data, client)) return;
sd = calloc(1, sizeof(Shell_Data));
sd->c = c;
c->shells = eina_inlist_append(c->shells, EINA_INLIST_GET(sd));
res = wl_resource_create(client, &xdg_wm_base_interface, version, id);
sd->res = res;
wl_resource_set_implementation(res, &shell_interface, sd, shell_unbind);
}
/////////////////////////////////////////////////////////////////
static void
seat_update_caps(Comp_Seat *s, struct wl_resource *res)
{
enum wl_seat_capability caps = 0;
Eina_List *l;
if (s->pointer)
caps |= WL_SEAT_CAPABILITY_POINTER;
if (s->keyboard)
caps |= WL_SEAT_CAPABILITY_KEYBOARD;
if (s->touch)
caps |= WL_SEAT_CAPABILITY_TOUCH;
if (!caps) return;
if (res)
wl_seat_send_capabilities(res, caps);
else
EINA_LIST_FOREACH(s->resources, l, res)
wl_seat_send_capabilities(res, caps);
}
static int
anonymous_fd_get(off_t size)
{
Eina_Tmpstr *file;
int fd;
fd = eina_file_mkstemp("comp-keymapXXXXXX", &file);
if (fd < 0)
{
EINA_LOG_ERR("mkstemp failed!");
return - 1;
}
if (!eina_file_close_on_exec(fd, 1))
{
EINA_LOG_ERR("Failed to set CLOEXEC on fd %d", fd);
close(fd);
return - 1;
}
if (ftruncate(fd, size) == -1)
{
EINA_LOG_ERR("ftruncate failed!");
}
eina_file_unlink(file);
eina_tmpstr_del(file);
return fd;
}
static void
_keymap_send(Comp_Seat *s, struct wl_resource *res)
{
char *mem;
int fd;
fd = anonymous_fd_get(s->kbd.keymap_str_size);
EINA_SAFETY_ON_TRUE_RETURN(fd == -1);
mem = mmap(NULL, s->kbd.keymap_str_size,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
memcpy(mem, s->kbd.keymap_str, s->kbd.keymap_str_size);
munmap(mem, s->kbd.keymap_str_size);
wl_keyboard_send_keymap(res, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, fd, s->kbd.keymap_str_size);
close(fd);
}
static void
seat_keymap_send(Comp_Seat *s)
{
Eina_List *l;
Eina_Iterator *it;
it = eina_hash_iterator_data_new(s->kbd.resources);
EINA_ITERATOR_FOREACH(it, l)
{
Eina_List *ll;
struct wl_resource *res;
EINA_LIST_FOREACH(l, ll, res)
_keymap_send(s, res);
}
eina_iterator_free(it);
}
static Eina_Bool
seat_kbd_mods_update(Comp_Seat *s)
{
xkb_mod_mask_t mod;
xkb_layout_index_t grp;
mod = xkb_state_serialize_mods(s->kbd.state, XKB_STATE_DEPRESSED);
s->kbd.mods.changed |= mod != s->kbd.mods.depressed;
s->kbd.mods.depressed = mod;
mod = xkb_state_serialize_mods(s->kbd.state, XKB_STATE_MODS_LATCHED);
s->kbd.mods.changed |= mod != s->kbd.mods.latched;
s->kbd.mods.latched = mod;
mod = xkb_state_serialize_mods(s->kbd.state, XKB_STATE_MODS_LOCKED);
s->kbd.mods.changed |= mod != s->kbd.mods.locked;
s->kbd.mods.locked = mod;
grp = xkb_state_serialize_layout(s->kbd.state, XKB_STATE_LAYOUT_EFFECTIVE);
s->kbd.mods.changed |= grp != s->kbd.mods.group;
s->kbd.mods.group = grp;
return s->kbd.mods.changed;
}
static void
seat_kbd_external_init(Comp_Seat *s)
{
Eina_List *l, *ll;
uint32_t serial;
struct wl_resource *res;
seat_keymap_send(s);
if (!seat_kbd_mods_update(s)) return;
l = seat_kbd_active_resources_get(s);
if (!l) return;
serial = wl_display_next_serial(s->c->display);
EINA_LIST_FOREACH(l, ll, res)
comp_seat_send_modifiers(s, res, serial);
}
static void
seat_keymap_update(Comp_Seat *s)
{
xkb_mod_mask_t latched = 0, locked = 0;
s->kbd.keymap_str = NULL;
s->kbd.keymap_str_size = 0;
#ifdef HAVE_ECORE_X
if (!x11_kbd_keymap)
{
#endif
if (s->kbd.state)
{
latched = xkb_state_serialize_mods(s->kbd.state, XKB_STATE_MODS_LATCHED);
locked = xkb_state_serialize_mods(s->kbd.state, XKB_STATE_MODS_LOCKED);
xkb_state_unref(s->kbd.state);
}
if (!s->kbd.keymap)
{
s->kbd.state = NULL;
s->kbd.keymap_str = NULL;
s->kbd.keymap_str_size = 0;
return;
}
s->kbd.state = xkb_state_new(s->kbd.keymap);
xkb_state_update_mask(s->kbd.state, 0, latched, locked, 0, 0, 0);
#ifdef HAVE_ECORE_X
}
#endif
s->kbd.keymap_str = xkb_map_get_as_string(s->kbd.keymap);
s->kbd.keymap_str_size = strlen(s->kbd.keymap_str) + 1;
seat_keymap_send(s);
}
static inline void
seat_kbd_repeat_rate_update(Comp_Seat *s)
{
double rate, delay;
if (s->seat)
{
if (ecore_wl2_input_keyboard_repeat_get(s->seat, &rate, &delay))
{
s->kbd.repeat_rate = lround(1 / rate);
s->kbd.repeat_delay = lround(delay * 1000);
}
else
s->kbd.repeat_rate = s->kbd.repeat_delay = 0;
}
else
{
s->kbd.repeat_rate = 40;
s->kbd.repeat_delay = 400;
}
}
static void
seat_keymap_create(Comp_Seat *s)
{
struct xkb_rule_names names;
memset(&names, 0, sizeof(names));
names.rules = "evdev";
names.model = "pc105";
names.layout = "us";
s->kbd.context = xkb_context_new(0);
s->kbd.keymap = xkb_map_new_from_names(s->kbd.context, &names, 0);
}
static const struct wl_keyboard_interface seat_kbd_interface =
{
resource_destroy
};
static void
seat_kbd_unbind(struct wl_resource *resource)
{
Comp_Seat *s = wl_resource_get_user_data(resource);
struct wl_client *client = wl_resource_get_client(resource);
eina_hash_list_remove(s->kbd.resources, &client, resource);
}
static void
seat_kbd_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
Comp_Seat *s = wl_resource_get_user_data(resource);
struct wl_resource *res;
Eina_List *l, *ll;
uint32_t serial;
res = wl_resource_create(client, &wl_keyboard_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(res, &seat_kbd_interface, s, seat_kbd_unbind);
if (!s->kbd.resources) s->kbd.resources = eina_hash_pointer_new(NULL);
eina_hash_list_append(s->kbd.resources, &client, res);
_keymap_send(s, res);
if (wl_resource_get_version(res) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION)
wl_keyboard_send_repeat_info(res, s->kbd.repeat_rate, s->kbd.repeat_delay);
if (s->active_client != client) return;
l = seat_kbd_active_resources_get(s);
if (!l) return;
serial = wl_display_next_serial(s->c->display);
EINA_LIST_FOREACH(l, ll, res)
{
if (s->c->active_surface)
wl_keyboard_send_enter(res, serial, s->c->active_surface->res, &s->kbd.keys);
comp_seat_send_modifiers(s, res, serial);
}
}
static void
seat_ptr_del(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
Comp_Seat *s = data;
evas_object_event_callback_del_full(s->ptr.efl.obj, EVAS_CALLBACK_DEL, seat_ptr_del, s);
s->ptr.efl.obj = NULL;
}
static void
seat_ptr_inherit(Comp_Seat *s, Eo *dev)
{
ecore_evas_cursor_device_get(ecore_evas_ecore_evas_get(s->c->evas), dev, &s->ptr.efl.obj,
&s->ptr.efl.layer, &s->ptr.efl.x, &s->ptr.efl.y);
if (s->ptr.efl.obj)
evas_object_event_callback_add(s->ptr.efl.obj, EVAS_CALLBACK_DEL, seat_ptr_del, s);
}
static void
seat_ptr_set_cursor(struct wl_client *client, struct wl_resource *resource, uint32_t serial, struct wl_resource *surface_resource, int32_t x, int32_t y)
{
Comp_Seat *s = wl_resource_get_user_data(resource);
Comp_Surface *cs = NULL;
if (!s->active_client) return;
if (surface_resource && (s->active_client != wl_resource_get_client(surface_resource))) return;
if (s->ptr.enter_serial - serial > UINT32_MAX / 2) return;
if (surface_resource)
cs = wl_resource_get_user_data(surface_resource);
if (cs && cs->role && (!cs->cursor))
{
wl_resource_post_error(surface_resource,
WL_POINTER_ERROR_ROLE, "surface already has role");
return;
}
if (s->ptr.cursor.surface == cs) return;
if (cs)
{
cs->cursor = 1;
cs->pending.set_input = 1;
eina_tiler_clear(cs->pending.input);
evas_object_pass_events_set(cs->obj, 1);
}
if (s->ptr.cursor.surface)
{
s->ptr.cursor.surface->cursor = 0;
s->ptr.cursor.surface->role = NULL;
}
if (s->ptr.in)
{
const Eina_List *l;
Eo *dev;
Ecore_Evas *ee = ecore_evas_ecore_evas_get(s->c->evas);
EINA_LIST_FOREACH(evas_device_list(s->c->evas, s->dev), l, dev)
if (evas_device_class_get(dev) == EVAS_DEVICE_CLASS_MOUSE)
{
if ((!s->ptr.efl.obj) && (!s->ptr.cursor.surface))
seat_ptr_inherit(s, dev);
ecore_evas_cursor_device_unset(ee, dev);
if (cs)
{
cs->role = cs->res;
ecore_evas_object_cursor_device_set(ee, dev, cs->obj, EVAS_LAYER_MAX, x, y);
}
}
}
if (cs)
evas_object_smart_member_del(cs->obj);
s->ptr.cursor.surface = cs;
s->ptr.cursor.x = x;
s->ptr.cursor.y = y;
}
static const struct wl_pointer_interface seat_ptr_interface =
{
seat_ptr_set_cursor,
resource_destroy
};
static void
seat_ptr_unbind(struct wl_resource *resource)
{
Comp_Seat *s = wl_resource_get_user_data(resource);
struct wl_client *client = wl_resource_get_client(resource);
eina_hash_list_remove(s->ptr.resources, &client, resource);
}
static void
seat_ptr_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
Comp_Seat *s = wl_resource_get_user_data(resource);
struct wl_resource *res;
Comp_Surface *cs;
int x, y;
res = wl_resource_create(client, &wl_pointer_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(res, &seat_ptr_interface, s, seat_ptr_unbind);
if (!s->ptr.resources) s->ptr.resources = eina_hash_pointer_new(NULL);
eina_hash_list_append(s->ptr.resources, &client, res);
if (!s->ptr.enter) return;
cs = s->ptr.enter;
if (wl_resource_get_client(cs->res) != client) return;
s->ptr.enter = NULL;
evas_pointer_canvas_xy_get(s->c->evas, &x, &y);
comp_surface_send_pointer_enter(cs, s, x, y);
}
static const struct wl_touch_interface seat_tch_interface =
{
resource_destroy
};
static void
seat_tch_unbind(struct wl_resource *resource)
{
Comp_Seat *s = wl_resource_get_user_data(resource);
struct wl_client *client = wl_resource_get_client(resource);
eina_hash_list_remove(s->tch.resources, &client, resource);
}
static void
seat_tch_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
Comp_Seat *s = wl_resource_get_user_data(resource);
struct wl_resource *res;
res = wl_resource_create(client, &wl_touch_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(res, &seat_tch_interface, s, seat_tch_unbind);
if (!s->tch.resources) s->tch.resources = eina_hash_pointer_new(NULL);
eina_hash_list_append(s->tch.resources, &client, res);
}
static const struct wl_seat_interface seat_interface =
{
seat_ptr_create,
seat_kbd_create,
seat_tch_create,
resource_destroy,
};
static void
seat_unbind(struct wl_resource *resource)
{
Comp_Seat *s = wl_resource_get_user_data(resource);
s->resources = eina_list_remove(s->resources, resource);
}
static void
seat_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct wl_resource *res;
Comp_Seat *s = data;
if (!client_allowed_check(s->c, client)) return;
res = wl_resource_create(client, &wl_seat_interface, version, id);
s->resources = eina_list_append(s->resources, res);
if (s->c->active_surface)
s->active_client = wl_resource_get_client(s->c->active_surface->res);
wl_resource_set_implementation(res, &seat_interface, s, seat_unbind);
seat_update_caps(s, res);
if (s->name && (version >= WL_SEAT_NAME_SINCE_VERSION))
wl_seat_send_name(res, s->name);
}
static void
seat_resource_hash_free(Eina_Hash *h)
{
Eina_Iterator *it;
Eina_List *l;
void **key;
if (!h) return;
while (eina_hash_population(h))
{
it = eina_hash_iterator_key_new(h);
EINA_ITERATOR_FOREACH(it, key)
{
struct wl_resource *res;
l = eina_hash_set(h, key, NULL);
EINA_LIST_FREE(l, res)
wl_resource_destroy(res);
break;
}
eina_iterator_free(it);
}
eina_hash_free(h);
}
static void
seat_kbd_destroy(Comp_Seat *s)
{
if (s->kbd.external) return;
#ifdef HAVE_ECORE_X
if (!x11_kbd_keymap)
{
#endif
if (s->kbd.state) xkb_state_unref(s->kbd.state);
if (s->kbd.keymap) xkb_keymap_unref(s->kbd.keymap);
if (s->kbd.context) xkb_context_unref(s->kbd.context);
#ifdef HAVE_ECORE_X
}
#endif
s->kbd.keymap_str = NULL;
s->kbd.keymap_str_size = 0;
wl_array_release(&s->kbd.keys);
}
static void
seat_destroy(Comp_Seat *s)
{
Eina_Stringshare *type;
seat_resource_hash_free(s->kbd.resources);
seat_resource_hash_free(s->ptr.resources);
seat_resource_hash_free(s->tch.resources);
while (s->resources)
wl_resource_destroy(eina_list_data_get(s->resources));
eina_stringshare_del(s->name);
seat_kbd_destroy(s);
efl_unref(s->dev);
s->c->seats = eina_inlist_remove(s->c->seats, EINA_INLIST_GET(s));
eina_hash_free(s->data_devices);
EINA_LIST_FREE(s->drag.x11_types, type) eina_stringshare_del(type);
while (s->ptr.events)
{
Input_Sequence *ev = EINA_INLIST_CONTAINER_GET(s->ptr.events, Input_Sequence);
s->ptr.events = eina_inlist_remove(s->ptr.events, s->ptr.events);
free(ev);
}
while (s->tch.events)
{
Input_Sequence *ev = EINA_INLIST_CONTAINER_GET(s->tch.events, Input_Sequence);
s->tch.events = eina_inlist_remove(s->tch.events, s->tch.events);
free(ev);
}
wl_global_destroy(s->global);
free(s);
}
/////////////////////////////////////////////////////////////////
static void
comp_gl_shutdown(Comp *c)
{
if (c->glapi && c->glapi->evasglUnbindWaylandDisplay)
c->glapi->evasglUnbindWaylandDisplay(c->gl, c->display);
if (c->glsfc)
evas_gl_surface_destroy(c->gl, c->glsfc);
if (c->glctx)
evas_gl_context_destroy(c->gl, c->glctx);
evas_gl_free(c->gl);
evas_gl_config_free(c->glcfg);
c->glsfc = NULL;
c->glctx = NULL;
c->glcfg = NULL;
c->gl = NULL;
}
static void
comp_gl_init(Comp *c)
{
c->glctx = evas_gl_context_create(c->gl, NULL);
if (!c->glctx) goto end;
c->glcfg = evas_gl_config_new();
if (!c->glcfg) goto end;
c->glsfc = evas_gl_surface_create(c->gl, c->glcfg, 1, 1);
if (!c->glsfc) goto end;
if (!evas_gl_make_current(c->gl, c->glsfc, c->glctx)) goto end;
c->glapi = evas_gl_context_api_get(c->gl, c->glctx);
if (c->glapi->evasglBindWaylandDisplay &&
c->glapi->evasglBindWaylandDisplay(c->gl, c->display))
return;
end:
comp_gl_shutdown(c);
}
static void
comp_render_pre(Comp *c, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED)
{
Comp_Surface *cs;
Eina_List *l, *ll;
c->rendering = 1;
EINA_LIST_FOREACH_SAFE(c->render_queue, l, ll, cs)
{
Comp_Buffer *buffer;
//if (cs->subsurface) fprintf(stderr, "RENDER PRE\n");
comp_surface_buffer_detach(&cs->buffer[1]);
cs->buffer[1] = cs->buffer[0];
cs->buffer[0] = NULL;
cs->render_queue = 0;
buffer = cs->buffer[1];
if (!buffer)
{
c->render_queue = eina_list_remove_list(c->render_queue, l);
evas_object_image_pixels_dirty_set(cs->img, 0);
continue;
}
//if (cs->proxies) fprintf(stderr, "RENDER %d\n", wl_resource_get_id(buffer->res));
cs->post_render_queue = 1;
if (cs->opaque_rects && (eina_array_count(cs->opaque_rects) == 1))
{
Eo *r = eina_array_data_get(cs->opaque_rects, 0);
int x, y, w, h, ox, oy, ow, oh;
evas_object_geometry_get(cs->img, &x, &y, &w, &h);
evas_object_geometry_get(r, &ox, &oy, &ow, &oh);
evas_object_image_border_set(cs->img, ox - x, ox + ow, oy - y, oy + oh);
evas_object_image_border_center_fill_set(cs->img, EVAS_BORDER_FILL_SOLID);
}
else
{
evas_object_image_border_set(cs->img, 0, 0, 0, 0);
evas_object_image_border_center_fill_set(cs->img, EVAS_BORDER_FILL_DEFAULT);
}
evas_object_image_alpha_set(cs->img, comp_surface_is_alpha(cs, buffer));
evas_object_resize(cs->img, buffer->w, buffer->h);
evas_object_image_size_set(cs->img, buffer->w, buffer->h);
}
c->post_render_queue = c->render_queue;
c->render_queue = NULL;
}
static void
comp_render_pre_proxied(Eo *o, Evas *e, void *event_info)
{
Comp_Surface *cs = evas_object_data_get(o, "comp_surface");
Comp_Buffer *buffer = cs->buffer[!cs->render_queue];
//fprintf(stderr, "PROXY RENDER_PRE %d\n", buffer ? wl_resource_get_id(buffer->res) : -1);
if (buffer)
{
buffer->renders = eina_list_remove(buffer->renders, e);
buffer->post_renders = eina_list_append(buffer->post_renders, e);
evas_object_image_size_set(o, buffer->w, buffer->h);
evas_object_image_alpha_set(o, comp_surface_is_alpha(cs, buffer));
evas_object_resize(o, buffer->w, buffer->h);
}
else
evas_object_image_pixels_dirty_set(o, 0);
}
static void
comp_render_post_proxied(Comp_Surface *cs, Evas *e, void *event_info)
{
Comp_Buffer *buffer;
buffer = cs->buffer[!cs->render_queue];
//fprintf(stderr, "PROXY RENDER_POST %d\n", buffer ? wl_resource_get_id(buffer->res) : -1);
buffer->post_renders = eina_list_remove(buffer->post_renders, e);
if (buffer->post_renders) return;
if (cs->buffer[0] || cs->dead) comp_surface_buffer_detach(&cs->buffer[1]);
comp_surface_buffer_post_render(cs);
}
static void
comp_render_post(Comp *c, Evas *e, void *event_info EINA_UNUSED)
{
Comp_Surface *cs;
Comp_Seat *s;
c->rendering = 0;
EINA_LIST_FREE(c->post_render_queue, cs)
{
//fprintf(stderr, "POST(%d)\n", wl_resource_get_id(cs->res));
cs->buffer[1]->renders = eina_list_remove(cs->buffer[1]->renders, e);
if ((!cs->buffer[1]->renders) && (!cs->buffer[1]->post_renders))
{
if (cs->buffer[0] || cs->dead) comp_surface_buffer_detach(&cs->buffer[1]);
comp_surface_buffer_post_render(cs);
}
cs->post_render_queue = 0;
if (!cs->dead) continue;
if (cs->res) wl_resource_destroy(cs->res);
else evas_object_del(cs->obj);
}
EINA_INLIST_FOREACH(c->seats, s)
{
Input_Sequence *ev;
Eina_Inlist *l;
EINA_INLIST_FOREACH_SAFE(s->ptr.events, l, ev)
{
if (!ev->up_serial) continue;
if (!ev->pass++) continue;
s->ptr.events = eina_inlist_remove(s->ptr.events, EINA_INLIST_GET(ev));
free(ev);
}
EINA_INLIST_FOREACH_SAFE(s->tch.events, l, ev)
{
if (!ev->up_serial) continue;
if (!ev->pass++) continue;
s->tch.events = eina_inlist_remove(s->tch.events, EINA_INLIST_GET(ev));
free(ev);
}
}
}
static void
comp_seat_selection_update(Comp_Seat *s)
{
Ecore_Wl2_Offer *offer;
Eina_Array *arr;
unsigned int i;
Eina_Array_Iterator it;
char *type;
if (!s->client_seat) return;
s->selection_changed = 0;
if (!s->selection_exists)
{
s->client_selection_serial = ecore_wl2_dnd_selection_clear(s->client_seat);
return;
}
if (!s->seat) return; /* x11_fixes_selection_notify() */
offer = ecore_wl2_dnd_selection_get(s->seat);
if (!offer) return;
arr = ecore_wl2_offer_mimes_get(offer);
{
char *types[eina_array_count(arr) + 1];
EINA_ARRAY_ITER_NEXT(arr, i, type, it)
types[i] = type;
types[i] = NULL;
s->client_selection_serial = ecore_wl2_dnd_selection_set(s->client_seat, (const char**)types);
}
}
static void
seat_drag_offer_update(Comp_Seat *s)
{
Eina_Array *arr;
char **types;
unsigned int i;
Eina_Array_Iterator it;
char *type;
// drag.id = probably double proxied
if (s->drag.id || (!s->client_offer)) return;
arr = ecore_wl2_offer_mimes_get(s->client_offer);
types = alloca(sizeof(void*) * (eina_array_count(arr) + 1));
EINA_ARRAY_ITER_NEXT(arr, i, type, it)
types[i] = type;
types[i] = NULL;
ecore_wl2_dnd_drag_types_set(s->client_seat, (const char**)types);
ecore_wl2_dnd_set_actions(s->client_seat);
}
static void
seat_proxy_update(Comp_Seat *s)
{
Eina_Iterator *it;
Ecore_Wl2_Input *input;
if (!s->name) return;
it = ecore_wl2_display_inputs_get(s->c->client_disp);
EINA_ITERATOR_FOREACH(it, input)
{
const char *name = ecore_wl2_input_name_get(input);
if (!name) continue;
if (!eina_streq(s->name, name)) continue;
s->client_seat = input;
if (s->selection_changed)
comp_seat_selection_update(s);
if (s->client_offer)
seat_drag_offer_update(s);
break;
}
eina_iterator_free(it);
}
static void
comp_seat_proxy_update(Comp_Seat *s)
{
Eina_Iterator *it;
Ecore_Wl2_Input *input;
Ecore_Wl2_Window *win;
int n = 0, i = 0;
if (s->seat) return;
win = ecore_evas_wayland2_window_get(ecore_evas_ecore_evas_get(s->c->evas));
if (win)
{
Ecore_Wl2_Display *ewd = ecore_wl2_window_display_get(win);
it = ecore_wl2_display_inputs_get(ewd);
EINA_ITERATOR_FOREACH(it, input)
{
if (ecore_wl2_input_seat_id_get(input) == evas_device_seat_id_get(s->dev))
break;
n++;
}
eina_iterator_free(it);
}
it = ecore_wl2_display_inputs_get(s->c->parent_disp);
EINA_ITERATOR_FOREACH(it, input)
{
if (!win)
{
if (eina_streq(ecore_wl2_input_name_get(input), evas_device_name_get(s->dev)))
{
s->seat = input;
break;
}
}
else if (i == n)
{
s->seat = input;
break;
}
i++;
}
eina_iterator_free(it);
}
static void
comp_device_caps_update(Comp_Seat *s)
{
const Eina_List *l;
Eo *dev;
Eina_Bool kbd;
if (!s) return;
kbd = s->keyboard;
s->keyboard = s->touch = s->pointer = 0;
EINA_LIST_FOREACH(evas_device_list(s->c->evas, s->dev), l, dev)
{
Evas_Device_Class clas = evas_device_class_get(dev);
s->keyboard |= clas == EVAS_DEVICE_CLASS_KEYBOARD;
s->touch |= clas == EVAS_DEVICE_CLASS_TOUCH;
s->pointer |= (clas == EVAS_DEVICE_CLASS_MOUSE ||
clas == EVAS_DEVICE_CLASS_TOUCH ||
clas == EVAS_DEVICE_CLASS_PEN ||
clas == EVAS_DEVICE_CLASS_POINTER ||
clas == EVAS_DEVICE_CLASS_WAND);
}
if (s->keyboard != kbd)
{
if (s->kbd.external)
seat_kbd_external_init(s);
else
{
if (s->keyboard)
{
#ifdef HAVE_ECORE_X
if ((!s->c->parent_disp) && ecore_x_display_get())
x11_kbd_apply(s);
else
#endif
seat_keymap_create(s);
seat_kbd_repeat_rate_update(s);
}
else
{
xkb_keymap_unref(s->kbd.keymap);
s->kbd.keymap = NULL;
}
seat_keymap_update(s);
s->keyboard = !!s->kbd.state;
}
}
seat_update_caps(s, NULL);
}
static void
comp_seats_proxy(Comp *c)
{
const Eina_List *l, *ll;
Eo *dev;
l = evas_device_list(c->evas, NULL);
EINA_LIST_FOREACH(l, ll, dev)
{
Comp_Seat *s;
Eina_Bool skip = EINA_FALSE;
if (evas_device_class_get(dev) != EVAS_DEVICE_CLASS_SEAT) continue;
EINA_INLIST_FOREACH(c->seats, s)
if (s->dev == dev)
{
skip = EINA_TRUE;
if (c->parent_disp)
comp_seat_proxy_update(s);
break;
}
if (skip) continue;
s = calloc(1, sizeof(Comp_Seat));
s->c = c;
s->dev = dev;
efl_ref(s->dev);
if (c->parent_disp)
comp_seat_proxy_update(s);
s->name = eina_stringshare_ref(evas_device_name_get(dev));
s->data_devices = eina_hash_pointer_new(NULL);
c->seats = eina_inlist_append(c->seats, EINA_INLIST_GET(s));
wl_array_init(&s->kbd.keys);
if (s->seat)
{
Ecore_Wl2_Seat_Capabilities caps = ecore_wl2_input_seat_capabilities_get(s->seat);
s->keyboard = !!(caps & ECORE_WL2_SEAT_CAPABILITIES_KEYBOARD);
s->pointer = !!(caps & ECORE_WL2_SEAT_CAPABILITIES_POINTER);
s->touch = !!(caps & ECORE_WL2_SEAT_CAPABILITIES_TOUCH);
if (s->keyboard)
{
if (s->kbd.external)
seat_kbd_external_init(s);
else
{
if (s->seat)
{
s->kbd.keymap = ecore_wl2_input_keymap_get(s->seat);
if (s->kbd.keymap) xkb_keymap_ref(s->kbd.keymap);
}
else
{
#ifdef HAVE_ECORE_X
if ((!s->c->parent_disp) && ecore_x_display_get())
x11_kbd_apply(s);
else
#endif
seat_keymap_create(s);
}
seat_keymap_update(s);
seat_kbd_repeat_rate_update(s);
s->keyboard = !!s->kbd.state;
}
}
}
else if (!c->parent_disp)
comp_device_caps_update(s);
s->global = wl_global_create(c->display, &wl_seat_interface, 4, s, seat_bind);
efl_event_callback_call(s->c->obj, EFL_CANVAS_WL_EVENT_SEAT_ADDED, dev);
if (ecore_wl2_display_sync_is_done(c->client_disp))
seat_proxy_update(s);
}
}
static void
comp_wayland_features_init(Comp *c)
{
if (ecore_wl2_display_dmabuf_get(c->parent_disp))
linux_dmabuf_setup(c->display, c);
}
static Eina_Bool
comp_sync_done(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Sync_Done *ev)
{
Eina_List *l;
Comp *c;
EINA_LIST_FOREACH(comps, l, c)
{
if (c->parent_disp == ev->display)
{
c->client_disp = ecore_wl2_display_connect(c->env);
if (!c->seats)
comp_seats_proxy(c);
comp_wayland_features_init(c);
}
else if (c->client_disp == ev->display)
{
Eina_Iterator *it;
Ecore_Wl2_Input *input;
it = ecore_wl2_display_inputs_get(c->client_disp);
EINA_ITERATOR_FOREACH(it, input)
{
Comp_Seat *s;
EINA_INLIST_FOREACH(c->seats, s)
{
if (!eina_streq(s->name, ecore_wl2_input_name_get(input)))
continue;
s->client_seat = input;
if (s->selection_changed)
comp_seat_selection_update(s);
if (s->client_offer)
seat_drag_offer_update(s);
break;
}
}
eina_iterator_free(it);
}
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
comp_seat_selection_changed(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Seat_Selection *ev)
{
Eina_List *l;
Comp *c;
Comp_Seat *s;
EINA_LIST_FOREACH(comps, l, c)
if (c->parent_disp == ev->display)
EINA_INLIST_FOREACH(c->seats, s)
{
if (s->seat && (ecore_wl2_input_seat_id_get(s->seat) != ev->seat)) continue;
s->selection_changed = 1;
s->selection_exists = !!ecore_wl2_dnd_selection_get(s->seat);
if (!s->focused) continue;
comp_seat_selection_update(s);
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
comp_seat_caps_handler(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Seat_Capabilities *ev)
{
Eina_List *l;
Comp *c;
Comp_Seat *s;
EINA_LIST_FOREACH(comps, l, c)
if (c->parent_disp == ev->display)
EINA_INLIST_FOREACH(c->seats, s)
{
if (ecore_wl2_input_seat_id_get(s->seat) != ev->id) continue;
s->pointer = ev->pointer_enabled;
if (s->keyboard != ev->keyboard_enabled)
{
if (s->kbd.external)
seat_kbd_external_init(s);
else
{
if (ev->keyboard_enabled)
{
s->kbd.keymap = ecore_wl2_input_keymap_get(s->seat);
if (s->kbd.keymap) xkb_keymap_ref(s->kbd.keymap);
seat_kbd_repeat_rate_update(s);
}
else
{
xkb_keymap_unref(s->kbd.keymap);
s->kbd.keymap = NULL;
}
seat_keymap_update(s);
}
}
s->keyboard = !!ev->keyboard_enabled && !!s->kbd.state;
s->touch = ev->touch_enabled;
seat_update_caps(s, NULL);
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
comp_seat_name_changed(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Seat_Name *ev)
{
Eina_List *l;
Comp *c;
Comp_Seat *s;
EINA_LIST_FOREACH(comps, l, c)
if (c->parent_disp == ev->display)
EINA_INLIST_FOREACH(c->seats, s)
{
Eina_List *lll;
struct wl_resource *res;
if (ecore_wl2_input_seat_id_get(s->seat) != ev->id) continue;
eina_stringshare_replace(&s->name, ev->name);
EINA_LIST_FOREACH(s->resources, lll, res)
if (wl_resource_get_version(res) >= WL_SEAT_NAME_SINCE_VERSION)
wl_seat_send_name(res, s->name);
seat_proxy_update(s);
break;
}
else if (c->client_disp == ev->display)
EINA_INLIST_FOREACH(c->seats, s)
if (!s->client_seat) seat_proxy_update(s);
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
comp_seat_keymap_changed(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Seat_Keymap_Changed *ev)
{
Eina_List *l;
Comp *c;
Comp_Seat *s;
EINA_LIST_FOREACH(comps, l, c)
if (c->parent_disp == ev->display)
EINA_INLIST_FOREACH(c->seats, s)
{
struct xkb_keymap *keymap;
if (s->kbd.external) continue;
if (ecore_wl2_input_seat_id_get(s->seat) != ev->id) continue;
if (s->kbd.keymap) xkb_map_unref(s->kbd.keymap);
s->kbd.keymap = NULL;
keymap = ecore_wl2_input_keymap_get(s->seat);
if (keymap) s->kbd.keymap = xkb_keymap_ref(keymap);
seat_keymap_update(s);
if (s->keyboard != (!!s->kbd.state))
{
s->keyboard = !!s->kbd.state;
seat_update_caps(s, NULL);
}
}
return ECORE_CALLBACK_RENEW;
}
static void
seat_kbd_repeat_rate_send(Comp_Seat *s)
{
Eina_List *ll, *lll;
struct wl_resource *res;
Eina_Iterator *it;
it = eina_hash_iterator_data_new(s->kbd.resources);
EINA_ITERATOR_FOREACH(it, ll)
EINA_LIST_FOREACH(ll, lll, res)
if (wl_resource_get_version(res) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION)
wl_keyboard_send_repeat_info(res, s->kbd.repeat_rate, s->kbd.repeat_delay);
eina_iterator_free(it);
}
static Eina_Bool
comp_seat_keyboard_repeat_changed(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Seat_Keyboard_Repeat_Changed *ev)
{
Eina_List *l;
Comp *c;
Comp_Seat *s;
EINA_LIST_FOREACH(comps, l, c)
if (c->parent_disp == ev->display)
EINA_INLIST_FOREACH(c->seats, s)
{
if (ecore_wl2_input_seat_id_get(s->seat) != ev->id) continue;
if (s->kbd.external) continue;
seat_kbd_repeat_rate_update(s);
seat_kbd_repeat_rate_send(s);
}
return ECORE_CALLBACK_RENEW;
}
static void
comp_seat_key_send(Comp_Seat *s, xkb_keycode_t key, int dir, unsigned int timestamp, Eina_Bool mods)
{
Eina_List *l, *ll;
struct wl_resource *res;
uint32_t serial = wl_display_next_serial(s->c->display);
uint32_t wl[] = { WL_KEYBOARD_KEY_STATE_PRESSED, WL_KEYBOARD_KEY_STATE_RELEASED };
l = seat_kbd_active_resources_get(s);
EINA_LIST_FOREACH(l, ll, res)
{
wl_keyboard_send_key(res, serial, timestamp, key, wl[dir]);
if (mods) comp_seat_send_modifiers(s, res, serial);
}
}
static void
comp_seat_key_update(Comp_Seat *s, xkb_keycode_t key, int dir, unsigned int timestamp)
{
uint32_t xkb[] = { XKB_KEY_DOWN, XKB_KEY_UP };
Eina_Bool mods = EINA_FALSE;
if (s->kbd.external || xkb_state_update_key(s->kbd.state, key + 8, xkb[dir]))
mods = seat_kbd_mods_update(s);
if (s->focused)
comp_seat_key_send(s, key, dir, timestamp, mods);
}
static Eina_Bool
comp_key_down(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Key *ev)
{
uint32_t keycode;
Comp *c;
Eina_List *l;
Comp_Seat *s;
const Eo *dev = evas_device_parent_get(ev->dev);
keycode = (ev->keycode - 8);
EINA_LIST_FOREACH(comps, l, c)
EINA_INLIST_FOREACH(c->seats, s)
{
if (c->parent_disp && (dev != s->dev)) continue;
if (s->kbd.external)
{
/* only doing passthrough in external mode */
if (!s->focused) return ECORE_CALLBACK_RENEW;
}
else
{
uint32_t *end, *k;
end = (uint32_t*)s->kbd.keys.data + (s->kbd.keys.size / sizeof(uint32_t));
for (k = s->kbd.keys.data; k < end; k++)
if (*k == keycode) return ECORE_CALLBACK_RENEW;
s->kbd.keys.size = (char*)end - (char*)s->kbd.keys.data;
k = wl_array_add(&s->kbd.keys, sizeof(uint32_t));
*k = keycode;
}
comp_seat_key_update(s, keycode, 0, ev->timestamp);
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
comp_key_up(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Key *ev)
{
uint32_t keycode;
Comp *c;
Eina_List *l;
Comp_Seat *s;
const Eo *dev = evas_device_parent_get(ev->dev);
keycode = (ev->keycode - 8);
EINA_LIST_FOREACH(comps, l, c)
EINA_INLIST_FOREACH(c->seats, s)
{
if (c->parent_disp && (dev != s->dev)) continue;
if (s->kbd.external)
{
/* only doing passthrough in external mode */
if (!s->focused) return ECORE_CALLBACK_RENEW;
}
else
{
uint32_t *end, *k;
end = (uint32_t*)s->kbd.keys.data + (s->kbd.keys.size / sizeof(uint32_t));
for (k = s->kbd.keys.data; k < end; k++)
if (*k == keycode)
{
*k = end[-1];
s->kbd.keys.size = (char*)end - (char*)s->kbd.keys.data - 1;
break;
}
}
comp_seat_key_update(s, keycode, 1, ev->timestamp);
}
return ECORE_CALLBACK_RENEW;
}
static void
comp_device_add(void *d, const Efl_Event *ev)
{
Comp *c = d;
if (efl_input_device_type_get(ev->info) == EFL_INPUT_DEVICE_TYPE_SEAT)
comp_seats_proxy(c);
else if (!c->parent_disp)
comp_device_caps_update(comp_seat_find(c, ev->info));
}
static void
comp_device_del(void *d, const Efl_Event *ev)
{
Comp *c = d;
Eo *seat;
Eina_Inlist *l;
Comp_Seat *s;
if (efl_input_device_type_get(ev->info) == EFL_INPUT_DEVICE_TYPE_SEAT)
seat = ev->info;
else
seat = efl_input_device_seat_get(ev->info);
EINA_INLIST_FOREACH_SAFE(c->seats, l, s)
if (seat == s->dev)
{
if (ev->info == seat)
seat_destroy(s);
else if (!c->parent_disp)
comp_device_caps_update(s);
}
}
static Eina_Bool
comp_data_device_dnd_leave(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Dnd_Leave *ev)
{
Comp *c;
Comp_Seat *s;
Eina_List *l;
EINA_LIST_FOREACH(comps, l, c)
{
if (ev->display != c->parent_disp) continue;
EINA_INLIST_FOREACH(c->seats, s)
{
if (ecore_wl2_input_seat_id_get(s->seat) != ev->seat) continue;
if (s->drag.id) continue; // probably double proxied
if (s->drag.source)
wl_data_source_send_cancelled(s->drag.source->res);
if (s->client_seat && s->drag.res)
{
ecore_wl2_dnd_drag_end(s->client_seat);
s->client_offer = NULL;
}
if (s->drag.proxy_win)
{
if (s->seat)
ecore_wl2_dnd_drag_end(s->seat);
ecore_evas_free(s->drag.proxy_win);
}
seat_drag_end(s);
s->drag.source = NULL;
}
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
comp_data_device_dnd_end(void *d, int t, Ecore_Wl2_Event_Data_Source_End *ev)
{
Comp *c;
Comp_Seat *s;
Eina_List *l;
EINA_LIST_FOREACH(comps, l, c)
{
if (ev->display != c->parent_disp) continue;
EINA_INLIST_FOREACH(c->seats, s)
{
if (ecore_wl2_input_seat_id_get(s->seat) != ev->seat) continue;
if (!s->drag.source) continue;
if (s->drag.source->proxy_serial != ev->serial) continue;
if (ev->cancelled)
wl_data_source_send_cancelled(s->drag.source->res);
else
data_source_notify_finish(s->drag.source);
if (s->drag.proxy_win)
{
if (s->seat)
ecore_wl2_dnd_drag_end(s->seat);
ecore_evas_free(s->drag.proxy_win);
}
seat_drag_end(s);
s->drag.source = NULL;
s->client_offer = NULL;
}
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
comp_data_device_dnd_enter(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Dnd_Enter *ev)
{
Comp *c;
Comp_Seat *s;
Eina_List *l;
EINA_LIST_FOREACH(comps, l, c)
{
if (ev->display != c->parent_disp) continue;
EINA_INLIST_FOREACH(c->seats, s)
{
if (ecore_wl2_input_seat_id_get(s->seat) != ev->seat) continue;
s->client_offer = ev->offer;
seat_drag_offer_update(s);
}
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
comp_data_device_dnd_motion(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Dnd_Motion *ev)
{
Comp *c;
Comp_Seat *s;
Eina_List *l;
int x, y, w, h, ex, ey;
Eina_Bool found = EINA_FALSE;
EINA_LIST_FOREACH(comps, l, c)
{
int fx, fy;
if (ev->display != c->parent_disp) continue;
evas_output_framespace_get(c->evas, &fx, &fy, NULL, NULL);
ex = ev->x - fx;
ey = ev->y - fy;
evas_object_geometry_get(c->obj, &x, &y, &w, &h);
if (!COORDS_INSIDE(ex, ey, x, y, w, h)) continue;
found = EINA_TRUE;
break;
}
if (!found) return ECORE_CALLBACK_RENEW;
EINA_INLIST_FOREACH(c->seats, s)
if (ecore_wl2_input_seat_id_get(s->seat) == ev->seat)
{
dnd_motion(s, ex, ey);
break;
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
comp_data_device_dnd_drop(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Dnd_Drop *ev)
{
Comp *c;
Comp_Seat *s;
Eina_List *l;
EINA_LIST_FOREACH(comps, l, c)
{
if (c->parent_disp != ev->display) continue;
EINA_INLIST_FOREACH(c->seats, s)
if (ecore_wl2_input_seat_id_get(s->seat) == ev->seat)
{
if (s->drag.enter)
{
drag_grab_button(s, 0, s->drag.id, WL_POINTER_BUTTON_STATE_RELEASED);
if (s->drag.proxy_win)
{
ecore_wl2_dnd_drag_end(s->seat);
ecore_evas_free(s->drag.proxy_win);
}
return ECORE_CALLBACK_RENEW;
}
}
}
return ECORE_CALLBACK_RENEW;
}
static void
comp_handlers_init(void)
{
Ecore_Event_Handler *h;
h = ecore_event_handler_add(ECORE_WL2_EVENT_SEAT_CAPABILITIES_CHANGED,
(Ecore_Event_Handler_Cb)comp_seat_caps_handler, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_WL2_EVENT_SEAT_KEYMAP_CHANGED,
(Ecore_Event_Handler_Cb)comp_seat_keymap_changed, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_WL2_EVENT_SEAT_NAME_CHANGED,
(Ecore_Event_Handler_Cb)comp_seat_name_changed, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_WL2_EVENT_SYNC_DONE,
(Ecore_Event_Handler_Cb)comp_sync_done, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_WL2_EVENT_SEAT_KEYBOARD_REPEAT_CHANGED,
(Ecore_Event_Handler_Cb)comp_seat_keyboard_repeat_changed, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_WL2_EVENT_SEAT_SELECTION,
(Ecore_Event_Handler_Cb)comp_seat_selection_changed, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_WL2_EVENT_DND_MOTION,
(Ecore_Event_Handler_Cb)comp_data_device_dnd_motion, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_WL2_EVENT_DND_ENTER,
(Ecore_Event_Handler_Cb)comp_data_device_dnd_enter, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_WL2_EVENT_DND_LEAVE,
(Ecore_Event_Handler_Cb)comp_data_device_dnd_leave, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_END,
(Ecore_Event_Handler_Cb)comp_data_device_dnd_end, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_WL2_EVENT_DND_DROP,
(Ecore_Event_Handler_Cb)comp_data_device_dnd_drop, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN,
(Ecore_Event_Handler_Cb)comp_key_down, NULL);
handlers = eina_list_append(handlers, h);
h = ecore_event_handler_add(ECORE_EVENT_KEY_UP,
(Ecore_Event_Handler_Cb)comp_key_up, NULL);
handlers = eina_list_append(handlers, h);
}
static void
comp_focus_in(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
Comp *c = data;
//Efl_Input_Focus_Data *ev = event_info;
Comp_Seat *s;
EINA_INLIST_FOREACH(c->seats, s)
{
Eina_List *l, *ll;
struct wl_resource *res;
uint32_t serial;
//if (ev->device != s->dev) continue;
if (evas_seat_focus_get(c->evas, s->dev) != c->obj) continue;
s->focused = 1;
if (s->selection_changed)
comp_seat_selection_update(s);
if ((!s->kbd.keymap) && (!s->kbd.state)) continue;
if (s->kbd.external)
{
Eina_Bool mods = seat_kbd_mods_update(s);
if (!s->focused) return;
l = seat_kbd_active_resources_get(s);
EINA_LIST_FOREACH(l, ll, res)
{
uint32_t *end, *k;
serial = wl_display_next_serial(s->c->display);
end = (uint32_t*)s->kbd.keys_external->data + (s->kbd.keys_external->size / sizeof(uint32_t));
if (mods) comp_seat_send_modifiers(s, res, serial);
for (k = s->kbd.keys_external->data; k < end; k++)
comp_seat_key_send(s, *k, 0, 0, mods);
}
return;
}
if (!seat_kbd_mods_update(s)) continue;
l = seat_kbd_active_resources_get(s);
if (!l) continue;
serial = wl_display_next_serial(s->c->display);
EINA_LIST_FOREACH(l, ll, res)
comp_seat_send_modifiers(s, res, serial);
}
}
static void
comp_focus_out(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
Comp *c = data;
//Efl_Input_Focus_Data *ev = event_info;
Comp_Seat *s;
EINA_INLIST_FOREACH(c->seats, s)
{
//if (ev->device != s->dev) continue;
if (evas_seat_focus_get(c->evas, s->dev) != c->obj)
s->focused = 0;
}
}
static void
comp_mouse_in(void *data, Evas *e, Eo *obj, void *event_info)
{
Comp *c = data;
Evas_Event_Mouse_In *ev = event_info;
Comp_Seat *s;
s = comp_seat_find(c, ev->dev);
if (!s->event_propagate)
{
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
}
s->event_propagate = 0;
if (s->drag.proxy_win)
{
ecore_evas_free(s->drag.proxy_win);
#ifdef HAVE_ECORE_X
if (ecore_x_display_get())
{
ecore_x_dnd_abort(ecore_evas_window_get(ecore_evas_ecore_evas_get(s->c->evas)));
s->drag.x11_owner = 0;
}
#endif
if (s->ptr.cursor.surface)
evas_object_show(s->ptr.cursor.surface->obj);
}
if (s->drag.surface)
evas_object_show(s->drag.surface->obj);
s->ptr.in = 1;
seat_ptr_inherit(s, ev->dev);
ecore_evas_cursor_device_unset(ecore_evas_ecore_evas_get(e), ev->dev);
if (s->ptr.efl.obj) evas_object_hide(s->ptr.efl.obj);
if (!s->ptr.cursor.surface) return;
s->ptr.cursor.surface->role = s->ptr.cursor.surface->res;
ecore_evas_object_cursor_device_set(ecore_evas_ecore_evas_get(e), ev->dev,
s->ptr.cursor.surface->obj, EVAS_LAYER_MAX, s->ptr.cursor.x, s->ptr.cursor.y);
}
static void
comp_multi_move(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info)
{
Comp *c = data;
Evas_Event_Multi_Move *ev = event_info;
Comp_Seat *s;
int w, h;
s = comp_seat_find(c, ev->dev);
if (!s->event_propagate)
{
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
}
s->event_propagate = 0;
s->tch.pos.x = ev->cur.canvas.x;
s->tch.pos.y = ev->cur.canvas.y;
if ((!s->drag.tch) || (!s->drag.surface)) return;
evas_object_geometry_get(s->drag.surface->obj, NULL, NULL, &w, &h);
evas_object_move(s->drag.surface->obj, ev->cur.canvas.x, ev->cur.canvas.y);
}
static void
comp_mouse_move(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info)
{
Comp *c = data;
Evas_Event_Mouse_Move *ev = event_info;
Comp_Seat *s;
int w, h;
s = comp_seat_find(c, ev->dev);
if (!s->event_propagate)
{
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
}
s->event_propagate = 0;
s->ptr.pos.x = ev->cur.canvas.x;
s->ptr.pos.y = ev->cur.canvas.y;
if (s->drag.tch || (!s->drag.surface)) return;
evas_object_geometry_get(s->drag.surface->obj, NULL, NULL, &w, &h);
evas_object_move(s->drag.surface->obj, ev->cur.canvas.x, ev->cur.canvas.y);
}
static void
comp_mouse_out(void *data, Evas *e EINA_UNUSED, Eo *obj, void *event_info)
{
Comp *c = data;
Evas_Event_Mouse_Out *ev = event_info;
Comp_Seat *s;
Eina_List *l;
const char **types, *type;
unsigned int i = 0;
s = comp_seat_find(c, ev->dev);
if (!s->event_propagate)
{
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
}
s->event_propagate = 0;
if (!s->ptr.in) return;
s->ptr.in = 0;
ecore_evas_cursor_device_unset(ecore_evas_ecore_evas_get(e), ev->dev);
if (s->ptr.efl.obj)
{
ecore_evas_object_cursor_device_set(ecore_evas_ecore_evas_get(e), ev->dev,
s->ptr.efl.obj, s->ptr.efl.layer, s->ptr.efl.x, s->ptr.efl.y);
seat_ptr_del(s, NULL, NULL, NULL);
}
if (s->ptr.cursor.surface)
{
s->ptr.cursor.surface->role = NULL;
evas_object_hide(s->ptr.cursor.surface->obj);
}
if ((!s->drag.res) || (!s->drag.source) || s->drag.source->proxy) return;
if (s->drag.enter) comp_surface_send_data_device_leave(s->drag.enter, s);
if (s->drag.surface)
seat_drag_proxy_win_add(s);
types = alloca(sizeof(void*) * (eina_list_count(s->drag.source->mime_types) + 1));
EINA_LIST_FOREACH(s->drag.source->mime_types, l, type)
types[i++] = type;
types[i] = NULL;
if (s->c->parent_disp)
{
Ecore_Wl2_Window *win = NULL;
if (s->drag.proxy_win)
win = ecore_evas_wayland2_window_get(s->drag.proxy_win);
ecore_wl2_dnd_drag_types_set(s->seat, types);
s->drag.source->proxy_serial = ecore_wl2_dnd_drag_start(s->seat,
ecore_evas_wayland2_window_get(ecore_evas_ecore_evas_get(s->c->evas)),
win);
s->drag.source->proxy_send_handler =
ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_SEND,
data_device_proxy_send_send, s->drag.source);
}
#ifdef HAVE_ECORE_X
else if (ecore_x_display_get())
{
Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(s->c->evas));
Ecore_Window xwin = ecore_evas_window_get(s->drag.proxy_win);
Ecore_X_Atom actions[] = { ECORE_X_DND_ACTION_MOVE, ECORE_X_DND_ACTION_COPY };
if (s->drag.proxy_win && s->drag.surface)
{
int x, y, ex, ey;
ecore_evas_geometry_get(ecore_evas_ecore_evas_get(s->c->evas), &ex, &ey, NULL, NULL);
evas_object_geometry_get(s->drag.surface->obj, &x, &y, NULL, NULL);
x += ex, y += ey;
ecore_x_window_ignore_set(xwin, 1);
ecore_evas_move(s->drag.proxy_win, x, y);
}
s->drag.x11_owner = win;
ecore_x_dnd_types_set(win, types, i);
ecore_x_dnd_actions_set(win, actions, EINA_C_ARRAY_LENGTH(actions));
ecore_x_dnd_self_begin(win, NULL, 0);
ecore_x_dnd_callback_pos_update_set(x11_dnd_move, s->drag.proxy_win);
}
#endif
}
EFL_CALLBACKS_ARRAY_DEFINE(comp_device_cbs,
{ EFL_CANVAS_SCENE_EVENT_DEVICE_ADDED, comp_device_add },
{ EFL_CANVAS_SCENE_EVENT_DEVICE_REMOVED, comp_device_del });
static void
hints_set_aspect(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface, uint32_t width, uint32_t height, uint32_t aspect)
{
Comp_Surface *cs = wl_resource_get_user_data(surface);
evas_object_size_hint_aspect_set(cs->obj, aspect, width, height);
if (cs == cs->c->active_surface)
shell_surface_aspect_update(cs);
}
static void
hints_set_weight(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface, int w, int h)
{
Comp_Surface *cs = wl_resource_get_user_data(surface);
cs->hint_set_weight = 1;
evas_object_size_hint_weight_set(cs->obj, w / 100., h / 100.);
}
static const struct efl_hints_interface hints_interface =
{
hints_set_aspect,
hints_set_weight,
};
static void
efl_hints_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct wl_resource *res;
if (!client_allowed_check(data, client)) return;
res = wl_resource_create(client, &efl_hints_interface, version, id);
wl_resource_set_implementation(res, &hints_interface, data, NULL);
}
EOLIAN static Eo *
_efl_canvas_wl_efl_object_constructor(Eo *obj, Comp *c)
{
efl_canvas_group_clipped_set(obj, EINA_TRUE);
EINA_SAFETY_ON_TRUE_RETURN_VAL(!ecore_wl2_init(), NULL);
return efl_constructor(efl_super(obj, MY_CLASS));
}
EOLIAN static void
_efl_canvas_wl_efl_canvas_group_group_add(Eo *obj, Comp *c)
{
char *env, *dbg = NULL;
efl_canvas_group_add(efl_super(obj, MY_CLASS));
c->wayland_time_base = ecore_loop_time_get();
c->obj = obj;
c->flags = EFL_EXE_FLAGS_TERM_WITH_PARENT;
env = getenv("WAYLAND_DISPLAY");
if (env) env = strdup(env);
if (getenv("EFL_CANVAS_WL_DEBUG"))
{
dbg = eina_strdup(getenv("WAYLAND_DEBUG"));
setenv("WAYLAND_DEBUG", "1", 1);
}
c->disp = ecore_wl2_display_create(NULL);
if (getenv("EFL_CANVAS_WL_DEBUG"))
{
if (dbg) setenv("WAYLAND_DEBUG", dbg, 1);
else unsetenv("WAYLAND_DEBUG");
free(dbg);
}
c->env = eina_strdup(getenv("WAYLAND_DISPLAY"));
if (env) setenv("WAYLAND_DISPLAY", env, 1);
else unsetenv("WAYLAND_DISPLAY");
c->display = ecore_wl2_display_get(c->disp);
c->client_surfaces = eina_hash_pointer_new(NULL);
c->evas = efl_provider_find(obj, EVAS_CANVAS_CLASS);
c->clip = (Eo*)efl_canvas_group_clipper_get(obj);
efl_gfx_entity_geometry_set(c->clip, efl_gfx_entity_geometry_get(obj));
c->events = evas_object_rectangle_add(c->evas);
evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_IN, comp_mouse_in, c);
evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_MOVE, comp_mouse_move, c);
evas_object_event_callback_add(obj, EVAS_CALLBACK_MULTI_MOVE, comp_multi_move, c);
evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_OUT, comp_mouse_out, c);
evas_object_color_set(c->events, 0, 0, 0, 0);
evas_object_smart_member_add(c->events, obj);
evas_object_show(c->events);
evas_object_lower(c->events);
wl_global_create(c->display, &wl_compositor_interface, 4, c, comp_bind);
wl_global_create(c->display, &wl_subcompositor_interface, 1, c, subcomp_bind);
wl_global_create(c->display, &wl_output_interface, 2, c, output_bind);
wl_global_create(c->display, &xdg_wm_base_interface, 1, c, shell_bind);
wl_global_create(c->display, &wl_data_device_manager_interface, 3, c, data_device_manager_bind);
wl_global_create(c->display, &efl_hints_interface, 1, c, efl_hints_bind);
wl_display_init_shm(c->display);
if (env)
{
c->parent_disp = ecore_wl2_display_connect(env);
if (ecore_wl2_display_sync_is_done(c->parent_disp))
{
c->client_disp = ecore_wl2_display_connect(c->env);
comp_seats_proxy(c);
comp_wayland_features_init(c);
}
}
else
{
c->client_disp = ecore_wl2_display_connect(c->env);
comp_seats_proxy(c);
#ifdef HAVE_ECORE_X
if (ecore_x_display_get())
{
ecore_x_xkb_track_state();
// if proxiedallowed
ecore_x_dnd_aware_set(ecore_evas_window_get(ecore_evas_ecore_evas_get(c->evas)), EINA_TRUE);
if (!comps) x11_init();
}
#endif
}
c->gl = evas_gl_new(c->evas);
if (c->gl) comp_gl_init(c);
evas_event_callback_add(c->evas, EVAS_CALLBACK_RENDER_PRE, (Evas_Event_Cb)comp_render_pre, c);
evas_event_callback_add(c->evas, EVAS_CALLBACK_RENDER_POST, (Evas_Event_Cb)comp_render_post, c);
efl_event_callback_array_add(c->evas, comp_device_cbs(), c);
evas_object_event_callback_add(c->obj, EVAS_CALLBACK_FOCUS_IN, comp_focus_in, c);
evas_object_event_callback_add(c->obj, EVAS_CALLBACK_FOCUS_OUT, comp_focus_out, c);
if (!comps) comp_handlers_init();
comps = eina_list_append(comps, c);
free(env);
}
EOLIAN static void
_efl_canvas_wl_efl_canvas_group_group_del(Eo *obj, Comp *c)
{
evas_object_del(c->clip);
evas_object_del(c->events);
free(c->env);
if (c->gl) comp_gl_shutdown(c);
while (c->shells)
{
Shell_Data *sd = EINA_INLIST_CONTAINER_GET(c->shells, Shell_Data);
wl_resource_destroy(sd->res);
}
while (c->seats)
{
Comp_Seat *s = EINA_INLIST_CONTAINER_GET(c->seats, Comp_Seat);
seat_destroy(s);
}
while (c->surfaces)
{
Comp_Surface *cs = EINA_INLIST_CONTAINER_GET(c->surfaces, Comp_Surface);
evas_object_del(cs->obj);
}
eina_hash_free(c->client_surfaces);
if (c->parent_disp) ecore_wl2_display_disconnect(c->parent_disp);
ecore_wl2_display_destroy(c->disp);
ecore_wl2_display_disconnect(c->client_disp);
evas_event_callback_del_full(c->evas, EVAS_CALLBACK_RENDER_PRE, (Evas_Event_Cb)comp_render_pre, c);
evas_event_callback_del_full(c->evas, EVAS_CALLBACK_RENDER_POST, (Evas_Event_Cb)comp_render_post, c);
efl_event_callback_array_del(c->evas, comp_device_cbs(), c);
eina_hash_free(c->exes);
comps = eina_list_remove(comps, c);
if (!comps)
{
void *h;
EINA_LIST_FREE(handlers, h)
ecore_event_handler_del(h);
#ifdef HAVE_ECORE_X
x11_shutdown();
#endif
}
ecore_wl2_shutdown();
efl_canvas_group_del(efl_super(obj, MY_CLASS));
}
static void
_efl_canvas_wl_efl_gfx_entity_position_set(Eo *obj, Comp *c, Eina_Position2D pos)
{
efl_gfx_entity_position_set(efl_super(obj, MY_CLASS), pos);
efl_gfx_entity_position_set(c->clip, pos);
}
static void
_efl_canvas_wl_efl_gfx_entity_size_set(Eo *obj, Comp *c, Eina_Size2D sz)
{
Eina_List *l;
Comp_Surface *cs;
struct wl_resource *res;
int w = sz.w, h = sz.h;
efl_gfx_entity_size_set(efl_super(obj, MY_CLASS), sz);
evas_object_resize(c->clip, w, h);
evas_object_resize(c->events, w, h);
EINA_LIST_FOREACH(c->output_resources, l, res)
output_resize(c, res);
//fprintf(stderr, "COMP %dx%d\n", w, h);
EINA_INLIST_FOREACH(c->surfaces, cs)
if (cs->shell.surface && cs->role && (!cs->extracted))
shell_surface_send_configure(cs);
}
static void
_efl_canvas_wl_efl_gfx_entity_visible_set(Eo *obj, Comp *c, Eina_Bool vis)
{
Comp_Surface *cs;
efl_gfx_entity_visible_set(efl_super(obj, MY_CLASS), vis);
EINA_INLIST_FOREACH(c->surfaces, cs)
if (vis)
comp_surface_output_enter(cs);
else
comp_surface_output_leave(cs);
}
static void
exe_event_del(void *data, const Efl_Event *ev)
{
Comp *c = data;
int32_t pid = efl_exe_pid_get(ev->object);
eina_hash_del_by_key(c->exes, &pid);
}
# ifdef __GNUC__
# if __GNUC__ >= 4
__attribute__ ((visibility("hidden")))
# endif
#endif
Eina_Bool
comp_dmabuf_test(struct linux_dmabuf_buffer *dmabuf)
{
Evas_Native_Surface ns;
Eo *test;
int size;
void *data;
Comp *c = dmabuf->compositor;
if (c->gl)
{
Eina_Bool ret;
ns.type = EVAS_NATIVE_SURFACE_WL_DMABUF;
ns.version = EVAS_NATIVE_SURFACE_VERSION;
ns.data.wl_dmabuf.attr = &dmabuf->attributes;
ns.data.wl_dmabuf.resource = NULL;
ns.data.wl_dmabuf.scanout.handler = NULL;
ns.data.wl_dmabuf.scanout.data = NULL;
test = evas_object_image_add(c->evas);
evas_object_image_native_surface_set(test, &ns);
ret = evas_object_image_load_error_get(test) == EVAS_LOAD_ERROR_NONE;
evas_object_del(test);
if (!ns.data.wl_dmabuf.attr) return EINA_FALSE;
return ret;
}
/* TODO: Software rendering for multi-plane formats */
if (dmabuf->attributes.n_planes != 1) return EINA_FALSE;
if (dmabuf->attributes.format != DRM_FORMAT_ARGB8888 &&
dmabuf->attributes.format != DRM_FORMAT_XRGB8888) return EINA_FALSE;
/* This is only legit for ARGB8888 */
size = dmabuf->attributes.height * dmabuf->attributes.stride[0];
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf->attributes.fd[0], 0);
if (data == MAP_FAILED) return EINA_FALSE;
munmap(data, size);
return EINA_TRUE;
}
void
comp_dmabuf_formats_query(void *compositor EINA_UNUSED, int **formats EINA_UNUSED, int *num_formats)
{
*num_formats = 0;
}
void
comp_dmabuf_modifiers_query(void *compositor EINA_UNUSED, int format EINA_UNUSED, uint64_t **modifiers EINA_UNUSED, int *num_modifiers)
{
*num_modifiers = 0;
}
Eo *
comp_run(Eo *obj, Comp *c, const char *cmd, Efl_Exe_Flags flags)
{
char *env, *disp, *gl = NULL;
Efl_Exe *exe;
if (!c->exes)
c->exes = eina_hash_int32_new(NULL);
disp = getenv("DISPLAY");
if (disp) disp = strdup(disp);
unsetenv("DISPLAY");
env = getenv("WAYLAND_DISPLAY");
if (env) env = strdup(env);
setenv("WAYLAND_DISPLAY", c->env, 1);
if (c->gl)
{
gl = getenv("ELM_ACCEL");
if (gl) gl = strdup(gl);
setenv("ELM_ACCEL", "gl", 1);
}
exe = efl_add(EFL_EXE_CLASS, obj,
efl_core_command_line_command_string_set(efl_added, cmd),
efl_exe_flags_set(efl_added, flags),
efl_task_run(efl_added));
if (disp) setenv("DISPLAY", disp, 1);
if (env) setenv("WAYLAND_DISPLAY", env, 1);
else unsetenv("WAYLAND_DISPLAY");
if (c->gl)
{
if (gl) setenv("ELM_ACCEL", gl, 1);
else unsetenv("ELM_ACCEL");
free(gl);
}
free(env);
free(disp);
if (exe)
{
int32_t pid = efl_exe_pid_get(exe);
eina_hash_add(c->exes, &pid, (void*)1);
efl_event_callback_add(exe, EFL_TASK_EVENT_EXIT, exe_event_del, c);
}
return exe;
}
EOLIAN static Efl_Exe *
_efl_canvas_wl_run(Eo *obj, Comp *c, const char *cmd)
{
return comp_run(obj, c, cmd, EFL_EXE_FLAGS_TERM_WITH_PARENT);
}
EOLIAN static Efl_Exe_Flags
_efl_canvas_wl_exec_flags_get(const Eo *obj, Comp *c)
{
return c->flags;
}
EOLIAN static void
_efl_canvas_wl_exec_flags_set(Eo *obj, Comp *c, Efl_Exe_Flags flags)
{
c->flags = flags;
}
EOLIAN static void
_efl_canvas_wl_allowed_pid_add(Eo *obj, Comp *c, int32_t pid)
{
if (!c->exes)
c->exes = eina_hash_int32_new(NULL);
eina_hash_add(c->exes, &pid, (void*)1);
}
EOLIAN static void
_efl_canvas_wl_allowed_pid_del(Eo *obj, Comp *c, int32_t pid)
{
if (!c->exes) return;
eina_hash_del_by_key(c->exes, &pid);
}
EOLIAN static Eo *
_efl_canvas_wl_surface_next(Eo *obj, Comp *c)
{
Comp_Surface *cs;
if (c->surfaces_count < 2) return NULL;
EINA_INLIST_REVERSE_FOREACH(c->surfaces, cs)
{
if (cs->shell.activated) continue;
if ((!cs->role) || (!cs->shell.surface) || cs->dead) continue;
cs->shell.activated = 1;
shell_surface_send_configure(cs);
return cs->obj;
}
return NULL;
}
EOLIAN static Eo *
_efl_canvas_wl_surface_prev(Eo *obj, Comp *c)
{
Comp_Surface *cs;
if (c->surfaces_count < 2) return NULL;
EINA_INLIST_FOREACH(c->surfaces, cs)
{
if (cs->shell.activated) continue;
if ((!cs->role) || (!cs->shell.surface) || cs->dead) continue;
cs->shell.activated = 1;
shell_surface_send_configure(cs);
return cs->obj;
}
return NULL;
}
EOLIAN static Eo *
_efl_canvas_wl_active_surface_get(const Eo *obj, Comp *c)
{
if (c->active_surface && (!c->active_surface->dead))
return c->active_surface->obj;
return NULL;
}
EOLIAN static Eina_Bool
_efl_canvas_wl_active_surface_set(Eo *obj, Comp *c, Eo *surface)
{
Comp_Surface *cs = efl_data_scope_get(surface, EFL_CANVAS_WL_SURFACE_CLASS);
EINA_SAFETY_ON_NULL_RETURN_VAL(cs, EINA_FALSE);
if (cs->dead) return EINA_FALSE;
if (c->active_surface == cs) return EINA_TRUE;
/* can't activate a popup */
if (cs->shell.popup) return EINA_FALSE;
cs->shell.activated = 1;
shell_surface_send_configure(cs);
return EINA_TRUE;
}
EOLIAN static void
_efl_canvas_wl_rotation_get(const Eo *obj EINA_UNUSED, Comp *c, Efl_Canvas_Wl_Rotation *rotation, Eina_Bool *rtl)
{
if (rotation) *rotation = c->rotation;
if (rtl) *rtl = c->rtl;
}
EOLIAN static void
_efl_canvas_wl_rotation_set(Eo *obj EINA_UNUSED, Comp *c, Efl_Canvas_Wl_Rotation rot, Eina_Bool rtl)
{
Eina_List *l;
struct wl_resource *res;
c->rtl = !!rtl;
c->rotation = rot;
EINA_LIST_FOREACH(c->output_resources, l, res)
output_resize(c, res);
}
EOLIAN static double
_efl_canvas_wl_efl_gfx_entity_scale_get(const Eo *obj EINA_UNUSED, Comp *c)
{
return c->scale;
}
EOLIAN static void
_efl_canvas_wl_efl_gfx_entity_scale_set(Eo *obj EINA_UNUSED, Comp *c, double scale)
{
Eina_List *l;
struct wl_resource *res;
c->scale = scale;
EINA_LIST_FOREACH(c->output_resources, l, res)
if (wl_resource_get_version(res) >= WL_OUTPUT_SCALE_SINCE_VERSION)
wl_output_send_scale(res, lround(c->scale));
}
EOLIAN static Eina_Bool
_efl_canvas_wl_aspect_get(const Eo *obj EINA_UNUSED, Comp *c)
{
return c->aspect;
}
EOLIAN static void
_efl_canvas_wl_aspect_set(Eo *obj, Comp *c, Eina_Bool set)
{
if (c->aspect == (!!set)) return;
c->aspect = !!set;
if (c->aspect)
shell_surface_aspect_update(c->active_surface);
else
evas_object_size_hint_aspect_set(obj, EVAS_ASPECT_CONTROL_NONE, 0, 0);
}
EOLIAN static Eina_Bool
_efl_canvas_wl_minmax_get(const Eo *obj EINA_UNUSED, Comp *c)
{
return c->minmax;
}
EOLIAN static void
_efl_canvas_wl_minmax_set(Eo *obj, Comp *c, Eina_Bool set)
{
if (c->minmax == (!!set)) return;
c->minmax = !!set;
if (c->minmax)
shell_surface_minmax_update(c->active_surface);
else
{
evas_object_size_hint_min_set(obj, 0, 0);
evas_object_size_hint_max_set(obj, -1, -1);
}
}
EOLIAN static Efl_Canvas_Wl_Wl_Global *
_efl_canvas_wl_global_add(Eo *obj, Comp *c, const Efl_Canvas_Wl_Wl_Interface *interface, uint32_t version, Efl_Canvas_Wl_Wl_Interface_Data *data, Efl_Canvas_Wl_Wl_Interface_Bind_Cb *bind_cb)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(interface, NULL);
return (void*)wl_global_create(c->display, (void*)interface, version, (void*)data, (void*)bind_cb);
}
static void
extracted_focus(void *data, Evas *e EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
Comp_Surface *cs = data;
if (cs->dead) return;
if (!cs->shell.popup)
{
cs->shell.activated = 1;
shell_surface_send_configure(data);
}
evas_object_focus_set(cs->c->obj, 1);
}
static void
extracted_unfocus(void *data, Evas *e EINA_UNUSED, Eo *obj, void *event_info EINA_UNUSED)
{
Comp_Surface *cs = data;
Eo *focus;
if (cs->dead) return;
focus = evas_focus_get(cs->c->evas);
if ((!focus) || (focus == cs->c->obj)) return;
cs->shell.activated = 0;
shell_surface_send_configure(data);
}
static void
extracted_changed(void *data, Evas *e EINA_UNUSED, Eo *obj, void *event_info EINA_UNUSED)
{
Comp_Surface *cs = data;
if (cs->dead) return;
shell_surface_send_configure(data);
}
static EOLIAN int32_t
_efl_canvas_wl_surface_pid_get(const Eo *surface, Comp_Surface *cs)
{
int32_t pid;
EINA_SAFETY_ON_TRUE_RETURN_VAL(cs->dead, -1);
wl_client_get_credentials(wl_resource_get_client(cs->res), &pid, NULL, NULL);
return pid;
}
static EOLIAN Eina_Bool
_efl_canvas_wl_surface_extract(Eo *surface, Comp_Surface *cs)
{
EINA_SAFETY_ON_TRUE_RETURN_VAL(cs->extracted, EINA_FALSE);
EINA_SAFETY_ON_TRUE_RETURN_VAL(cs->dead, EINA_FALSE);
cs->extracted = 1;
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_RESIZE, extracted_changed, cs);
if (!cs->shell.popup)
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_FOCUS_OUT, extracted_unfocus, cs);
evas_object_event_callback_add(cs->obj, EVAS_CALLBACK_FOCUS_IN, extracted_focus, cs);
evas_object_smart_member_del(surface);
return EINA_TRUE;
}
Eo *
efl_canvas_wl_extracted_surface_object_find(void *surface_resource)
{
Comp_Surface *cs = wl_resource_get_user_data(surface_resource);
EINA_SAFETY_ON_NULL_RETURN_VAL(cs, NULL);
EINA_SAFETY_ON_TRUE_RETURN_VAL(!cs->extracted, NULL);
EINA_SAFETY_ON_TRUE_RETURN_VAL(cs->dead, NULL);
return cs->obj;
}
static EOLIAN Eo *
_efl_canvas_wl_surface_parent_surface_get(const Eo *surface, Comp_Surface *cs)
{
EINA_SAFETY_ON_TRUE_RETURN_VAL(cs->dead, NULL);
if (cs->parent && cs->role) return cs->parent->obj;
return NULL;
}
static EOLIAN void
_efl_canvas_wl_seat_keymap_set(Eo *obj, Comp *c, Eo *seat, Efl_Canvas_Wl_Xkb_State *state, const char *str, Efl_Canvas_Wl_Wl_Array *key_array)
{
Comp_Seat *s;
EINA_INLIST_FOREACH(c->seats, s)
{
if (!seat) efl_canvas_wl_seat_keymap_set(obj, s->dev, state, str, key_array);
else if (s->dev == seat) break;
}
if (!seat) return;
EINA_SAFETY_ON_NULL_RETURN(s);
seat_kbd_destroy(s);
s->kbd.external = 1;
s->kbd.keys_external = (void*)key_array;
s->kbd.state = (void*)state;
s->kbd.keymap_str = str;
if (str)
s->kbd.keymap_str_size = strlen(str) + 1;
else
s->kbd.keymap_str_size = 0;
s->kbd.context = NULL;
s->kbd.keymap = NULL;
if (s->keyboard)
seat_kbd_external_init(s);
}
static EOLIAN void
_efl_canvas_wl_seat_key_repeat_set(Eo *obj, Comp *c, Eo *seat, int repeat_rate, int repeat_delay)
{
Comp_Seat *s;
EINA_INLIST_FOREACH(c->seats, s)
{
if (!seat) efl_canvas_wl_seat_key_repeat_set(obj, s->dev, repeat_rate, repeat_delay);
else if (s->dev == seat) break;
}
if (!seat) return;
EINA_SAFETY_ON_NULL_RETURN(s);
s->kbd.repeat_rate = repeat_rate;
s->kbd.repeat_delay = repeat_delay;
seat_kbd_repeat_rate_send(s);
}
#define EFL_CANVAS_WL_EXTRA_OPS \
EFL_CANVAS_GROUP_ADD_DEL_OPS(efl_canvas_wl), \
#define EFL_CANVAS_WL_SURFACE_EXTRA_OPS \
EFL_CANVAS_GROUP_ADD_DEL_OPS(efl_canvas_wl_surface), \
#include "efl_canvas_wl.eo.c"
#include "efl_canvas_wl_surface.eo.c"