You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

3811 lines
118 KiB

#define E_COMP_WL
#include "e.h"
/* handle include for printing uint64_t */
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#define COMPOSITOR_VERSION 4
E_API int E_EVENT_WAYLAND_GLOBAL_ADD = -1;
#ifndef EGL_HEIGHT
# define EGL_HEIGHT 0x3056
#endif
#ifndef EGL_WIDTH
# define EGL_WIDTH 0x3057
#endif
/* Resource Data Mapping: (wl_resource_get_user_data)
*
* wl_surface == e_pixmap
* wl_region == eina_tiler
* wl_subsurface == e_client
*
*/
static void _e_comp_wl_subsurface_parent_commit(E_Client *ec, Eina_Bool parent_synchronized);
EINTERN Eina_Bool e_comp_wl_extensions_init(void);
/* local variables */
/* static Eina_Hash *clients_win_hash = NULL; */
static Eina_List *handlers = NULL;
static double _last_event_time = 0.0;
static int64_t surface_id = 0;
static Eina_List *grab_clients;
static Eina_List *grab_cbs;
/* local functions */
static Eina_Bool
_parent_client_contains_pointer(E_Client *ec)
{
Eina_List *l;
E_Client *c, *top = ec;
while (top->parent) top = top->parent;
if (top == ec) return EINA_FALSE;
if (top->mouse.in) return EINA_TRUE;
EINA_LIST_FOREACH(top->comp_data->sub.list, l, c)
if (c->mouse.in) return EINA_TRUE;
EINA_LIST_FOREACH(top->transients, l, c)
if ((ec != c) && c->mouse.in) return EINA_TRUE;
return EINA_FALSE;
}
static struct wl_resource *
_output_resource_find(Eina_List *reslist, struct wl_resource *surface)
{
Eina_List *l;
struct wl_client *client;
struct wl_resource *res;
client = wl_resource_get_client(surface);
EINA_LIST_FOREACH(reslist, l, res)
if (wl_resource_get_client(res) == client) return res;
return NULL;
}
static void
_e_comp_wl_surface_outputs_update(E_Client *ec)
{
Eina_List *l;
E_Zone *zone;
int32_t obits = 0;
if (ec->visible)
EINA_LIST_FOREACH(e_comp->zones, l, zone)
if (E_INTERSECTS(zone->x, zone->y, zone->w, zone->h,
ec->x, ec->y, ec->w, ec->h)) obits |= 1 << zone->id;
if (obits != ec->comp_data->on_outputs)
{
int32_t leave = (obits ^ ec->comp_data->on_outputs) & ec->comp_data->on_outputs;
int32_t enter = (obits ^ ec->comp_data->on_outputs) & obits;
ec->comp_data->on_outputs = obits;
EINA_LIST_FOREACH(e_comp->zones, l, zone)
{
struct wl_resource *s, *res;
E_Comp_Wl_Output *wlo;
s = ec->comp_data->surface;
wlo = zone->output;
if (!wlo) continue;
res = _output_resource_find(wlo->resources, s);
if (!res) continue;
if (leave & (1 << zone->id)) wl_surface_send_leave(s, res);
if (enter & (1 << zone->id)) wl_surface_send_enter(s, res);
}
}
}
static void
_e_comp_wl_configure_send(E_Client *ec, Eina_Bool edges)
{
int w = 0, h = 0;
if (e_object_is_del(E_OBJECT(ec))) return;
if (ec->changes.size || e_pixmap_usable_get(ec->pixmap))
{
if (e_comp_object_frame_exists(ec->frame))
w = ec->client.w, h = ec->client.h;
else
w = ec->w, h = ec->h;
}
ec->comp_data->shell.configure_send(ec->comp_data->shell.surface,
edges * e_comp_wl->resize.edges,
w, h);
}
/* static void */
/* _e_comp_wl_log_cb_print(const char *format, va_list args) */
/* { */
/* EINA_LOG_DOM_INFO(e_log_dom, format, args); */
/* } */
static void
_e_comp_wl_modules_load(void)
{
const char **m, *mods[] =
{
#ifdef USE_MODULE_WL_DESKTOP_SHELL
"wl_desktop_shell",
#endif
#ifdef USE_MODULE_XWAYLAND
"xwayland",
#endif
NULL
};
for (m = mods; *m; m++)
{
E_Module *mod = e_module_find(*m);
if (!mod)
mod = e_module_new(*m);
if (mod)
e_module_enable(mod);
}
}
static void
_e_comp_wl_evas_cb_show(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Client *ec = data;
if (e_object_is_del(E_OBJECT(ec))) return;
if (!ec->override) e_hints_window_visible_set(ec);
}
static void
_e_comp_wl_evas_cb_hide(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Client *ec = data;
if (!e_object_is_del(E_OBJECT(ec))) return;
if (!e_object_ref_get(E_OBJECT(ec))) return;
e_comp_object_damage(ec->frame, 0, 0, ec->w, ec->h);
e_comp_object_dirty(ec->frame);
if (e_comp_object_render(ec->frame))
e_comp_client_post_update_add(ec);
e_object_unref(E_OBJECT(ec));
}
static void
_e_comp_wl_mouse_in(E_Client *ec, Evas_Event_Mouse_In *ev)
{
struct wl_resource *res;
struct wl_client *wc;
Eina_List *l;
uint32_t serial;
if (e_object_is_del(E_OBJECT(ec))) return;
if (!ec->comp_data->surface) return;
e_comp_wl->ptr.ec = ec;
if (e_comp_wl->drag)
{
e_comp_wl_data_device_send_enter(ec);
return;
}
if (!eina_list_count(e_comp_wl->ptr.resources)) return;
wc = wl_resource_get_client(ec->comp_data->surface);
serial = wl_display_next_serial(e_comp_wl->wl.disp);
EINA_LIST_FOREACH(e_comp_wl->ptr.resources, l, res)
{
E_Comp_Wl_Pointer *ptr = wl_resource_get_user_data(res);
if (!e_comp_wl_input_pointer_check(res)) continue;
if (wl_resource_get_client(res) != wc) continue;
ptr->entered = 1;
wl_pointer_send_enter(res, serial, ec->comp_data->surface,
wl_fixed_from_int(ev->canvas.x - ec->client.x),
wl_fixed_from_int(ev->canvas.y - ec->client.y));
e_comp_wl_input_pointer_cursor_update(ptr);
}
}
static void
_e_comp_wl_evas_cb_mouse_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info)
{
if (e_comp_object_frame_exists(obj)) return;
_e_comp_wl_mouse_in(data, event_info);
}
static void
_e_comp_wl_cb_ssd_mouse_in(void *data, Evas_Object *obj, void *event_info)
{
E_Client *ec = data;
if (!e_comp_object_frame_exists(obj)) return;
if (!e_object_is_del(data))
ec->comp_data->ssd_mouse_in = 1;
_e_comp_wl_mouse_in(data, event_info);
}
static void
_e_comp_wl_mouse_out(E_Client *ec)
{
struct wl_resource *res;
struct wl_client *wc;
Eina_List *l;
uint32_t serial;
if ((ec == e_client_action_get()) && e_grabinput_mouse_win_get()) return;
/* FIXME? this is a hack to just reset the cursor whenever we mouse out. not sure if accurate */
{
e_pointer_object_set(e_comp->pointer, NULL, 0, 0);
evas_object_show(e_comp->pointer->o_ptr);
}
if (e_comp_wl->ptr.ec == ec)
e_comp_wl->ptr.ec = NULL;
if (!ec->comp_data) return;
if (!ec->comp_data->surface) return;
if (e_comp_wl->drag)
{
e_comp_wl_data_device_send_leave(ec);
return;
}
if (!eina_list_count(e_comp_wl->ptr.resources)) return;
wc = wl_resource_get_client(ec->comp_data->surface);
serial = wl_display_next_serial(e_comp_wl->wl.disp);
EINA_LIST_FOREACH(e_comp_wl->ptr.resources, l, res)
{
E_Comp_Wl_Pointer *ptr = wl_resource_get_user_data(res);
if (!e_comp_wl_input_pointer_check(res)) continue;
if (wl_resource_get_client(res) != wc) continue;
ptr->entered = 0;
wl_pointer_send_leave(res, serial, ec->comp_data->surface);
}
}
static void
_e_comp_wl_evas_cb_mouse_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
if (e_comp_object_frame_exists(obj)) return;
_e_comp_wl_mouse_out(data);
}
static void
_e_comp_wl_cb_ssd_mouse_out(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
E_Client *ec = data;
if (!e_comp_object_frame_exists(obj)) return;
if (!e_object_is_del(data))
ec->comp_data->ssd_mouse_in = 0;
_e_comp_wl_mouse_out(data);
}
static void
_e_comp_wl_send_mouse_move(E_Client *ec, int x, int y, unsigned int timestamp)
{
struct wl_resource *res;
struct wl_client *wc;
Eina_List *l;
E_Client *tec, *tec_in = NULL;
Eina_Bool found = EINA_FALSE;
Evas_Event_Mouse_In ev;
/* begin hacks:
*
* this should be handled by setting NOGRAB pointer mode on comp object internals,
* ie. 03a4ecbdb0972f8267c07ea14d752fcee69bd9fa
* but doing so breaks dnd eventing.
*
* instead, walk the transients list, assuming (correctly) that they are listed from
* bottom to top, and send mouse events to whichever surface the pointer is in
*/
ev.canvas.x = x;
ev.canvas.y = y;
EINA_LIST_FOREACH(ec->transients, l, tec)
{
if (e_client_util_ignored_get(tec) || (!evas_object_visible_get(tec->frame)))
continue;
if (tec->mouse.in) tec_in = tec;
if (E_INSIDE(x, y, tec->x, tec->y, tec->w, tec->h))
{
if (tec_in && (tec != tec_in))
{
e_client_mouse_out(tec_in, x, y);
_e_comp_wl_mouse_out(tec_in);
tec_in = NULL;
}
e_client_mouse_in(tec, x, y);
_e_comp_wl_mouse_in(tec, &ev);
e_client_mouse_move(tec, &(Evas_Point){x, y});
_e_comp_wl_send_mouse_move(tec, x, y, timestamp);
found = EINA_TRUE;
}
else if (tec->mouse.in)
{
e_client_mouse_out(tec, x, y);
_e_comp_wl_mouse_out(tec);
}
}
if (found) return;
/* if a transient previously had mouse.in, re-set mouse.in to the parent */
if (tec_in)
{
e_client_mouse_in(ec, x, y);
_e_comp_wl_mouse_in(ec, &ev);
e_client_mouse_move(ec, &(Evas_Point){x, y});
}
/* end hacks */
wc = wl_resource_get_client(ec->comp_data->surface);
EINA_LIST_FOREACH(e_comp_wl->ptr.resources, l, res)
{
if (!e_comp_wl_input_pointer_check(res)) continue;
if (wl_resource_get_client(res) != wc) continue;
wl_pointer_send_motion(res, timestamp,
wl_fixed_from_int(x - ec->client.x),
wl_fixed_from_int(y - ec->client.y));
}
}
static Eina_Bool
_e_comp_wl_evas_cb_mouse_internal_check(E_Client *ec, int x, int y)
{
if (!e_comp_object_frame_exists(ec->frame)) return EINA_TRUE;
if (E_INSIDE(x, y, ec->client.x, ec->client.y, ec->client.w, ec->client.h)) return EINA_TRUE;
return EINA_FALSE;
}
static void
_e_comp_wl_evas_cb_mouse_move(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
E_Client *ec = data;
Evas_Event_Mouse_Move *ev = event;
if (ec == e_client_action_get()) return;
if (!ec->mouse.in) return;
if (!_e_comp_wl_evas_cb_mouse_internal_check(ec, ev->cur.canvas.x, ev->cur.canvas.y))
{
e_client_mouse_move(ec, &(Evas_Point){ev->cur.canvas.x, ev->cur.canvas.y});
return;
}
if (e_object_is_del(E_OBJECT(ec))) return;
if (ec->ignored) return;
if (!ec->comp_data->surface) return;
if ((!e_comp_wl->drag_client) ||
(!e_client_has_xwindow(e_comp_wl->drag_client)) ||
e_comp_wl->drag_client->override)
_e_comp_wl_send_mouse_move(ec, ev->cur.canvas.x, ev->cur.canvas.y, ev->timestamp);
}
static void
_e_comp_wl_evas_cb_mouse_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
E_Client *ec = data;
Evas_Event_Mouse_Down *ev = event;
if (!_e_comp_wl_evas_cb_mouse_internal_check(ec, ev->output.x, ev->output.y)) return;
e_comp_wl_evas_handle_mouse_button(ec, ev->timestamp, ev->button,
WL_POINTER_BUTTON_STATE_PRESSED);
}
static void
_e_comp_wl_evas_cb_mouse_up(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
E_Client *ec = data;
Evas_Event_Mouse_Up *ev = event;
if (!_e_comp_wl_evas_cb_mouse_internal_check(ec, ev->output.x, ev->output.y)) return;
e_comp_wl_evas_handle_mouse_button(ec, ev->timestamp, ev->button,
WL_POINTER_BUTTON_STATE_RELEASED);
}
static void
_e_comp_wl_evas_cb_mouse_wheel(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
E_Client *ec;
Evas_Event_Mouse_Wheel *ev;
struct wl_resource *res;
struct wl_client *wc;
Eina_List *l;
uint32_t axis, dir;
ev = event;
if (!(ec = data)) return;
if (ec == e_client_action_get()) return;
if (e_object_is_del(E_OBJECT(ec))) return;
if (ec->ignored) return;
if (!ec->mouse.in) 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 (!ec->comp_data->surface) return;
wc = wl_resource_get_client(ec->comp_data->surface);
EINA_LIST_FOREACH(e_comp_wl->ptr.resources, l, res)
{
if (!e_comp_wl_input_pointer_check(res)) continue;
if (wl_resource_get_client(res) != wc) continue;
wl_pointer_send_axis(res, ev->timestamp, axis, dir);
}
}
static void
_e_comp_wl_evas_cb_multi_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
Eina_List *l;
struct wl_client *wc;
uint32_t serial;
struct wl_resource *res;
E_Client *ec = data;
Evas_Event_Multi_Down *ev = event;
wl_fixed_t x, y;
if (e_object_is_del(E_OBJECT(ec))) return;
if (!ec->comp_data->surface) return;
if (!ec->mouse.in) return;
wc = wl_resource_get_client(ec->comp_data->surface);
serial = wl_display_next_serial(e_comp_wl->wl.disp);
x = wl_fixed_from_int(ev->canvas.x - ec->client.x);
y = wl_fixed_from_int(ev->canvas.y - ec->client.y);
EINA_LIST_FOREACH(e_comp_wl->touch.resources, l, res)
{
if (wl_resource_get_client(res) != wc) continue;
if (!e_comp_wl_input_touch_check(res)) continue;
wl_touch_send_down(res, serial, ev->timestamp,
ec->comp_data->surface, ev->device, x, y);
}
}
static void
_e_comp_wl_evas_cb_multi_up(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
Eina_List *l;
struct wl_client *wc;
uint32_t serial;
struct wl_resource *res;
E_Client *ec = data;
Evas_Event_Multi_Up *ev = event;
if (e_object_is_del(E_OBJECT(ec))) return;
if (!ec->comp_data->surface) return;
if (!ec->mouse.in) return;
wc = wl_resource_get_client(ec->comp_data->surface);
serial = wl_display_next_serial(e_comp_wl->wl.disp);
EINA_LIST_FOREACH(e_comp_wl->touch.resources, l, res)
{
if (wl_resource_get_client(res) != wc) continue;
if (!e_comp_wl_input_touch_check(res)) continue;
wl_touch_send_up(res, serial, ev->timestamp, ev->device);
}
}
static void
_e_comp_wl_evas_cb_multi_move(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
Eina_List *l;
struct wl_client *wc;
struct wl_resource *res;
E_Client *ec = data;
Evas_Event_Multi_Move *ev = event;
wl_fixed_t x, y;
if (e_object_is_del(E_OBJECT(ec))) return;
if (!ec->comp_data->surface) return;
if (!ec->mouse.in) return;
wc = wl_resource_get_client(ec->comp_data->surface);
x = wl_fixed_from_int(ev->cur.canvas.x - ec->client.x);
y = wl_fixed_from_int(ev->cur.canvas.y - ec->client.y);
EINA_LIST_FOREACH(e_comp_wl->touch.resources, l, res)
{
if (wl_resource_get_client(res) != wc) continue;
if (!e_comp_wl_input_touch_check(res)) continue;
wl_touch_send_motion(res, ev->timestamp, ev->device, x, y);
}
}
static void
_e_comp_wl_client_priority_raise(E_Client *ec)
{
if (ec->netwm.pid <= 0) return;
if (ec->netwm.pid == getpid()) return;
e_pid_nice_priority_fg(ec->netwm.pid);
}
static void
_e_comp_wl_client_priority_normal(E_Client *ec)
{
if (ec->netwm.pid <= 0) return;
if (ec->netwm.pid == getpid()) return;
e_pid_nice_priority_bg(ec->netwm.pid);
}
static Eina_Bool
_e_comp_wl_evas_cb_focus_in_timer(E_Client *ec)
{
if (e_object_is_del(E_OBJECT(ec))) return EINA_FALSE;
ec->comp_data->on_focus_timer = NULL;
if (!e_comp_wl->kbd.focused) return EINA_FALSE;
e_comp_wl_input_keyboard_modifiers_update();
return EINA_FALSE;
}
static Eina_Bool
_keyboard_resource_add(struct wl_resource *newres)
{
Eina_List *l;
struct wl_resource *res;
EINA_LIST_FOREACH(e_comp_wl->kbd.focused, l, res)
if (res == newres) return EINA_FALSE;
e_comp_wl->kbd.focused = eina_list_append(e_comp_wl->kbd.focused, newres);
return EINA_TRUE;
}
static void
_e_comp_wl_evas_cb_focus_in(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Client *ec, *focused;
struct wl_resource *res;
struct wl_client *wc;
Eina_List *l;
int added = 0;
if (!(ec = data)) return;
if (e_object_is_del(E_OBJECT(ec))) return;
if (ec->iconic) return;
/* block spurious focus events */
focused = e_client_focused_get();
if ((focused) && (ec != focused)) return;
/* raise client priority */
_e_comp_wl_client_priority_raise(ec);
wc = wl_resource_get_client(ec->comp_data->surface);
if (ec->comp_data->is_xdg_surface)
{
/* We only send kbd focus to xdg top levels */
while (ec->parent)
{
ec = ec->parent;
/* If an xdg shell popup's parent already has focus we don't
* need to do anything more.
*/
if (ec->focused) return;
}
}
EINA_LIST_FOREACH(e_comp_wl->kbd.resources, l, res)
if (wl_resource_get_client(res) == wc)
added |= _keyboard_resource_add(res);
if (!e_comp_wl->kbd.focused) return;
if (!added) return;
e_comp_wl_input_keyboard_enter_send(ec);
if (e_comp_util_kbd_grabbed()) return;
e_comp_wl_data_device_keyboard_focus_set();
ec->comp_data->on_focus_timer =
ecore_timer_loop_add(0.8, (Ecore_Task_Cb)_e_comp_wl_evas_cb_focus_in_timer, ec);
}
static void
_e_comp_wl_keyboard_leave(E_Client *ec)
{
struct wl_resource *res;
Eina_List *l, *ll;
uint32_t serial;
if (!eina_list_count(e_comp_wl->kbd.resources)) return;
if (!ec->comp_data) return;
if (ec->comp_data->is_xdg_surface)
{
/* If we left an xdg popup to enter some other (sub)surface
* of the same top level, we don't need to do anything.
* We also test e_client_focused_get() because it will be NULL
* on moves which trip up _parent_client_contains_pointer() */
if (e_client_focused_get() &&
_parent_client_contains_pointer(ec)) return;
/* We only kbd focus top level xdg */
while (ec->parent) ec = ec->parent;
}
serial = wl_display_next_serial(e_comp_wl->wl.disp);
EINA_LIST_FOREACH_SAFE(e_comp_wl->kbd.focused, l, ll, res)
{
if (ec->comp_data->surface)
wl_keyboard_send_leave(res, serial, ec->comp_data->surface);
e_comp_wl->kbd.focused = eina_list_remove_list(e_comp_wl->kbd.focused, l);
}
if ((!e_comp_wl->wl.client_ec) || e_object_is_del(E_OBJECT(e_comp_wl->wl.client_ec))) return;
if (e_client_focused_get()) return;
e_comp_wl->kbd.focus = e_comp_wl->wl.client_ec->comp_data->surface;
e_comp_wl_data_device_keyboard_focus_set();
}
static void
_e_comp_wl_evas_cb_focus_out(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Client *ec = data;
/* lower client priority */
if (!e_object_is_del(data))
{
_e_comp_wl_client_priority_normal(ec);
E_FREE_FUNC(ec->comp_data->on_focus_timer, ecore_timer_del);
}
_e_comp_wl_keyboard_leave(ec);
}
static void
_e_comp_wl_evas_cb_restack(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
E_Client *sec, *ec = data;
Eina_List *l, *ll;
if (e_object_is_del(E_OBJECT(ec))) return;
if (e_client_has_xwindow(ec)) return;
/* only restack to enforce shell spec if config would otherwise not force restack */
if (!e_config->transient.raise)
e_client_transients_restack(ec);
if (!ec->comp_data->sub.list) return;
EINA_LIST_FOREACH(ec->comp_data->sub.list, l, sec)
evas_object_layer_set(sec->frame, evas_object_layer_get(ec->frame));
sec = eina_list_last_data_get(ec->comp_data->sub.list);
evas_object_stack_above(sec->frame, ec->frame);
EINA_LIST_REVERSE_FOREACH_SAFE(ec->comp_data->sub.list, l, ll, sec)
{
E_Client *nsec = eina_list_data_get(ll);
if (nsec)
evas_object_stack_below(nsec->frame, sec->frame);
}
}
static void
_e_comp_wl_evas_cb_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
E_Client *sec, *ec = data;
Eina_List *l;
if (e_object_is_del(E_OBJECT(ec))) return;
ec->comp_data->moved = 1;
EINA_LIST_FOREACH(ec->comp_data->sub.list, l, sec)
{
if (!sec->comp_data->sub.data->position.set)
evas_object_move(sec->frame, ec->client.x + sec->comp_data->sub.data->position.x,
ec->client.y + sec->comp_data->sub.data->position.y);
}
_e_comp_wl_surface_outputs_update(ec);
}
static void
_e_comp_wl_evas_cb_unmaximize_pre(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
{
E_Client *ec = data;
E_Maximize *max = event_info;
if (ec->need_fullscreen || e_object_is_del(E_OBJECT(ec))) return;
if (ec->comp_data->in_commit)
ec->comp_data->maximizing = 1;
else if (!e_client_has_xwindow(ec))
{
int w, h, ew = 0, eh = 0, *ecw, *ech;
unsigned int pmax = ec->maximized;
ec->comp_data->unmax = *max;
if (ec->internal)
ecw = &ec->client.w, ech = &ec->client.h;
else
ecw = &ec->w, ech = &ec->h;
if ((!e_config->window_maximize_animate) || ec->maximize_anims_disabled)
{
e_client_unmaximize_geometry_get(ec, *max, NULL, NULL, &w, &h);
if (ec->internal)
e_comp_object_frame_wh_unadjust(ec->frame, w, h, &w, &h);
ew = *ecw, eh = *ech;
*ecw = w, *ech = h;
}
ec->maximized = 0;
_e_comp_wl_configure_send(ec, 0);
if ((!e_config->window_maximize_animate) || ec->maximize_anims_disabled)
*ecw = ew, *ech = eh;
ec->maximized = pmax;
*max = 0;
}
}
static void
_e_comp_wl_evas_cb_maximize_pre(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
{
E_Client *ec = data;
E_Maximize *max = event_info;
if (e_object_is_del(E_OBJECT(ec))) return;
if (!ec->comp_data->buffer_commit)
ec->maximize_anims_disabled = 1;
if (ec->comp_data->in_commit)
ec->comp_data->maximizing = 1;
else if (!e_client_has_xwindow(ec))
{
int w, h, ew = 0, eh = 0, *ecw, *ech;
unsigned int pmax = ec->maximized;
ec->comp_data->max = *max;
if (ec->internal)
ecw = &ec->client.w, ech = &ec->client.h;
else
ecw = &ec->w, ech = &ec->h;
if ((!e_config->window_maximize_animate) ||
ec->maximize_anims_disabled || (!ec->comp_data->buffer_commit))
{
e_client_maximize_geometry_get(ec, *max, NULL, NULL, &w, &h);
if (ec->internal)
e_comp_object_frame_wh_unadjust(ec->frame, w, h, &w, &h);
ew = *ecw, eh = *ech;
*ecw = w, *ech = h;
}
ec->maximized = *max;
_e_comp_wl_configure_send(ec, 0);
if ((!e_config->window_maximize_animate) || ec->maximize_anims_disabled)
*ecw = ew, *ech = eh;
ec->maximized = pmax;
*max = 0;
}
}
static void
_e_comp_wl_evas_cb_resize(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Client *ec;
if (!(ec = data)) return;
if (e_object_is_del(E_OBJECT(ec))) return;
if ((ec->shading) || (ec->shaded)) return;
if (!ec->comp_data->shell.configure_send) return;
if (ec->comp_data->maximizing) return;
if (e_client_util_resizing_get(ec) && e_comp_wl->resize.edges)
{
int x, y;
x = ec->mouse.last_down[ec->moveinfo.down.button - 1].w;
y = ec->mouse.last_down[ec->moveinfo.down.button - 1].h;
if (e_comp_object_frame_exists(ec->frame))
e_comp_object_frame_wh_unadjust(ec->frame, x, y, &x, &y);
switch (ec->resize_mode)
{
case E_POINTER_RESIZE_TL:
case E_POINTER_RESIZE_L:
case E_POINTER_RESIZE_BL:
x += ec->mouse.last_down[ec->moveinfo.down.button - 1].mx -
ec->mouse.current.mx;
break;
case E_POINTER_RESIZE_TR:
case E_POINTER_RESIZE_R:
case E_POINTER_RESIZE_BR:
x += ec->mouse.current.mx - ec->mouse.last_down[ec->moveinfo.down.button - 1].mx;
break;
default:
break;;
}
switch (ec->resize_mode)
{
case E_POINTER_RESIZE_TL:
case E_POINTER_RESIZE_T:
case E_POINTER_RESIZE_TR:
y += ec->mouse.last_down[ec->moveinfo.down.button - 1].my -
ec->mouse.current.my;
break;
case E_POINTER_RESIZE_BL:
case E_POINTER_RESIZE_B:
case E_POINTER_RESIZE_BR:
y += ec->mouse.current.my - ec->mouse.last_down[ec->moveinfo.down.button - 1].my;
break;
default:
break;
}
x = E_CLAMP(x, 1, x);
y = E_CLAMP(y, 1, y);
ec->comp_data->shell.configure_send(ec->comp_data->shell.surface,
e_comp_wl->resize.edges,
x, y);
}
else
_e_comp_wl_configure_send(ec, 1);
}
static void
_e_comp_wl_evas_cb_state_update(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Client *ec = data;
if (e_object_is_del(E_OBJECT(ec))) return;
/* check for wayland pixmap */
if (ec->comp_data->shell.configure_send)
_e_comp_wl_configure_send(ec, 0);
}
static void
_e_comp_wl_evas_cb_maximize_done(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
E_Client *ec = data;
if (e_object_is_del(E_OBJECT(ec))) return;
ec->comp_data->maximizing = 0;
ec->maximize_anims_disabled = ec->comp_data->maximize_anims_disabled;
}
static void
_e_comp_wl_evas_cb_delete_request(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Client *ec = data;
e_comp_ignore_win_del(E_PIXMAP_TYPE_WL, e_pixmap_window_get(ec->pixmap));
if (!e_client_has_xwindow(ec))
{
if (ec->internal_elm_win)
evas_object_del(ec->internal_elm_win);
else
e_object_del(E_OBJECT(ec));
}
}
static void
_e_comp_wl_evas_cb_kill_request(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Client *ec;
if (!(ec = data)) return;
e_comp_ignore_win_del(E_PIXMAP_TYPE_WL, e_pixmap_window_get(ec->pixmap));
evas_object_pass_events_set(ec->frame, EINA_TRUE);
if (ec->visible) evas_object_hide(ec->frame);
if (ec->internal)
{
ec->ignored = 1;
if (!e_object_is_del(E_OBJECT(ec)))
ec->comp_data->mapped = EINA_FALSE;
}
else e_object_del(E_OBJECT(ec));
}
static void
_e_comp_wl_evas_cb_ping(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Client *ec;
if (!(ec = data)) return;
if (e_object_is_del(E_OBJECT(ec))) return;
if (!(ec->comp_data->shell.ping)) return;
if (!(ec->comp_data->shell.surface)) return;
ec->comp_data->shell.ping(ec->comp_data->shell.surface);
}
static void
_e_comp_wl_evas_cb_color_set(void *data, Evas_Object *obj, void *event EINA_UNUSED)
{
E_Client *ec;
int a = 0;
if (!(ec = data)) return;
evas_object_color_get(obj, NULL, NULL, NULL, &a);
if (ec->netwm.opacity == a) return;
ec->netwm.opacity = a;
ec->netwm.opacity_changed = EINA_TRUE;
}
static void
_e_comp_wl_buffer_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
E_Comp_Wl_Buffer *buffer;
buffer = container_of(listener, E_Comp_Wl_Buffer, destroy_listener);
wl_signal_emit(&buffer->destroy_signal, buffer);
buffer->destroyed = EINA_TRUE;
if (!buffer->busy)
free(buffer);
}
static void
_e_comp_wl_client_evas_init(E_Client *ec)
{
if (ec->comp_data->evas_init) return;
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_SHOW,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_show, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_HIDE,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_hide, ec);
/* setup input callbacks */
evas_object_smart_callback_add(ec->frame, "mouse_in",
(Evas_Smart_Cb)_e_comp_wl_cb_ssd_mouse_in, ec);
evas_object_smart_callback_add(ec->frame, "mouse_out",
(Evas_Smart_Cb)_e_comp_wl_cb_ssd_mouse_out, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_MOUSE_IN,
EVAS_CALLBACK_PRIORITY_AFTER,
(Evas_Object_Event_Cb)_e_comp_wl_evas_cb_mouse_in, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_MOUSE_OUT,
EVAS_CALLBACK_PRIORITY_AFTER,
(Evas_Object_Event_Cb)_e_comp_wl_evas_cb_mouse_out, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_MOUSE_MOVE,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_mouse_move, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_MOUSE_DOWN,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_mouse_down, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_MOUSE_UP,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_mouse_up, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_MOUSE_WHEEL,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_mouse_wheel, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_MULTI_DOWN,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_multi_down, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_MULTI_UP,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_multi_up, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_MULTI_MOVE,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_multi_move, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_FOCUS_IN,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_focus_in, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_FOCUS_OUT,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_focus_out, ec);
if (!ec->override)
{
evas_object_smart_callback_add(ec->frame, "client_resize",
_e_comp_wl_evas_cb_resize, ec);
evas_object_smart_callback_add(ec->frame, "maximize_pre",
_e_comp_wl_evas_cb_maximize_pre, ec);
evas_object_smart_callback_add(ec->frame, "unmaximize_pre",
_e_comp_wl_evas_cb_unmaximize_pre, ec);
evas_object_smart_callback_add(ec->frame, "maximize_done",
_e_comp_wl_evas_cb_maximize_done, ec);
evas_object_smart_callback_add(ec->frame, "unmaximize_done",
_e_comp_wl_evas_cb_maximize_done, ec);
evas_object_smart_callback_add(ec->frame, "fullscreen",
_e_comp_wl_evas_cb_state_update, ec);
}
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_MOVE,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_move, ec);
evas_object_event_callback_priority_add(ec->frame, EVAS_CALLBACK_RESTACK,
EVAS_CALLBACK_PRIORITY_AFTER,
_e_comp_wl_evas_cb_restack, ec);
if (ec->internal_elm_win)
{
/* setup delete/kill callbacks */
evas_object_smart_callback_add(ec->frame, "delete_request",
_e_comp_wl_evas_cb_delete_request, ec);
evas_object_smart_callback_add(ec->frame, "kill_request",
_e_comp_wl_evas_cb_kill_request, ec);
}
/* setup ping callback */
evas_object_smart_callback_add(ec->frame, "ping",
_e_comp_wl_evas_cb_ping, ec);
evas_object_smart_callback_add(ec->frame, "color_set",
_e_comp_wl_evas_cb_color_set, ec);
ec->comp_data->evas_init = EINA_TRUE;
E_LIST_HANDLER_APPEND(handlers, E_EVENT_SCREENSAVER_ON, _e_comp_wl_screensaver_on, NULL);
E_LIST_HANDLER_APPEND(handlers, E_EVENT_SCREENSAVER_OFF, _e_comp_wl_screensaver_off, NULL);
e_comp_wl_notidle();
e_screensaver_attrs_set(e_screensaver_timeout_get(EINA_TRUE),
e_config->screensaver_blanking,
e_config->screensaver_expose);
// XXX: maybe later like x work on explicit suspend of compositor stuff?
// e_desklock_show_hook_add(_e_comp_x_desklock_show);
// e_desklock_hide_hook_add(_e_comp_x_desklock_hide);
}
static inline int
_rotation_get(int rotation)
{
int transform;
switch (rotation)
{
case 90:
transform = WL_OUTPUT_TRANSFORM_90;
break;
case 180:
transform = WL_OUTPUT_TRANSFORM_180;
break;
case 270:
transform = WL_OUTPUT_TRANSFORM_270;
break;
case 0:
default:
transform = WL_OUTPUT_TRANSFORM_NORMAL;
break;
}
return transform;
}
static Eina_Bool
_e_comp_wl_cb_randr_change(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
{
const Eina_List *l;
E_Randr2_Screen *screen;
if (e_xinerama_fake_screens_exist())
{
E_Screen *scr;
EINA_LIST_FOREACH(e_xinerama_screens_get(), l, scr)
{
e_comp_wl_output_init(NULL, NULL, NULL,
scr->x, scr->y, scr->w, scr->h,
0, 0, 0, 0, _rotation_get(scr->rotation), scr->escreen);
}
return ECORE_CALLBACK_RENEW;
}
if (!e_randr2) return ECORE_CALLBACK_RENEW;
EINA_LIST_FOREACH(e_randr2->screens, l, screen)
{
if (!screen->config.enabled)
{
e_comp_wl_output_remove(screen->id);
continue;
}
if (!e_comp_wl_output_init(screen->id, screen->info.name,
screen->info.screen,
screen->config.geom.x, screen->config.geom.y,
screen->config.geom.w, screen->config.geom.h,
screen->info.size.w, screen->info.size.h,
screen->config.mode.refresh, screen->info.subpixel,
_rotation_get(screen->config.rotation), 0))
ERR("Could not initialize screen %s", screen->info.name);
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
_e_comp_wl_cb_comp_object_add(void *data EINA_UNUSED, int type EINA_UNUSED, E_Event_Comp_Object *ev)
{
E_Client *ec;
/* try to get the client from the object */
if (!(ec = e_comp_object_client_get(ev->comp_object)))
return ECORE_CALLBACK_RENEW;
/* check for client being deleted */
if (e_object_is_del(E_OBJECT(ec))) return ECORE_CALLBACK_RENEW;
/* check for wayland pixmap */
if (e_pixmap_type_get(ec->pixmap) != E_PIXMAP_TYPE_WL)
return ECORE_CALLBACK_RENEW;
/* if we have not setup evas callbacks for this client, do it */
if (!ec->comp_data->evas_init) _e_comp_wl_client_evas_init(ec);
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
_e_comp_wl_cb_mouse_move(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Mouse_Move *ev)
{
_last_event_time = ecore_loop_time_get();
e_comp_wl->ptr.x = ev->x;
e_comp_wl->ptr.y = ev->y;
e_comp_canvas_notidle();
if (e_comp_wl->selection.target &&
(!e_client_has_xwindow(e_comp_wl->selection.target)) &&
e_comp_wl->drag)
{
struct wl_resource *res;
int x, y;
res = e_comp_wl_data_find_for_client(wl_resource_get_client(e_comp_wl->selection.target->comp_data->surface));
x = ev->x - e_comp_wl->selection.target->client.x;
y = ev->y - e_comp_wl->selection.target->client.y;
wl_data_device_send_motion(res, ev->timestamp, wl_fixed_from_int(x), wl_fixed_from_int(y));
}
if (e_comp_wl->drag &&
e_comp_wl->drag_client &&
e_client_has_xwindow(e_comp_wl->drag_client) &&
(!e_comp_wl->drag_client->override))
_e_comp_wl_send_mouse_move(e_comp_wl->drag_client, ev->x, ev->y, ev->timestamp);
return ECORE_CALLBACK_RENEW;
}
static void
_e_comp_wl_surface_state_size_update(E_Client *ec, E_Comp_Wl_Surface_State *state)
{
Eina_Rectangle *window;
/* double scale = 0.0; */
/* scale = e_comp_wl->output.scale; */
/* switch (e_comp_wl->output.transform) */
/* { */
/* case WL_OUTPUT_TRANSFORM_90: */
/* case WL_OUTPUT_TRANSFORM_270: */
/* case WL_OUTPUT_TRANSFORM_FLIPPED_90: */
/* case WL_OUTPUT_TRANSFORM_FLIPPED_270: */
/* w = ec->comp_data->buffer_ref.buffer->h / scale; */
/* h = ec->comp_data->buffer_ref.buffer->w / scale; */
/* break; */
/* default: */
/* w = ec->comp_data->buffer_ref.buffer->w / scale; */
/* h = ec->comp_data->buffer_ref.buffer->h / scale; */
/* break; */
/* } */
if (!e_pixmap_size_get(ec->pixmap, &state->bw, &state->bh)) return;
if (e_client_has_xwindow(ec) || e_comp_object_frame_exists(ec->frame)) return;
window = &ec->comp_data->shell.window;
if (window->x || window->y || window->w || window->h)
e_comp_object_frame_geometry_set(ec->frame, -window->x,
(window->x + window->w) - state->bw,
-window->y,
(window->y + window->h) - state->bh);
else
e_comp_object_frame_geometry_set(ec->frame, 0, 0, 0, 0);
}
static void
_e_comp_wl_surface_state_cb_buffer_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
E_Comp_Wl_Surface_State *state;
state =
container_of(listener, E_Comp_Wl_Surface_State, buffer_destroy_listener);
state->buffer = NULL;
}
static void
_e_comp_wl_surface_state_init(E_Comp_Wl_Surface_State *state)
{
state->new_attach = EINA_FALSE;
state->buffer = NULL;
state->buffer_destroy_listener.notify =
_e_comp_wl_surface_state_cb_buffer_destroy;
state->sx = state->sy = 0;
state->input = NULL;
state->opaque = NULL;
}
static void
_e_comp_wl_surface_state_finish(E_Comp_Wl_Surface_State *state)
{
struct wl_resource *cb;
Eina_Rectangle *dmg;
Eina_List *free_list;
/* The resource destroy callback will walk the state->frames list,
* so move the list to a temporary first.
*/
free_list = state->frames;
state->frames = NULL;
EINA_LIST_FREE(free_list, cb)
wl_resource_destroy(cb);
EINA_LIST_FREE(state->damages, dmg)
eina_rectangle_free(dmg);
if (state->opaque) eina_tiler_free(state->opaque);
state->opaque = NULL;
if (state->input) eina_tiler_free(state->input);
state->input = NULL;
if (state->buffer) wl_list_remove(&state->buffer_destroy_listener.link);
state->buffer = NULL;
}
static void
_e_comp_wl_surface_state_buffer_set(E_Comp_Wl_Surface_State *state, E_Comp_Wl_Buffer *buffer)
{
if (state->buffer == buffer) return;
if (state->buffer)
wl_list_remove(&state->buffer_destroy_listener.link);
state->buffer = buffer;
if (state->buffer)
wl_signal_add(&state->buffer->destroy_signal,
&state->buffer_destroy_listener);
}
static void
_e_comp_wl_surface_state_attach(E_Client *ec, E_Comp_Wl_Surface_State *state)
{
/* set usable early because shell module checks this */
e_pixmap_usable_set(ec->pixmap, (state->buffer != NULL));
e_pixmap_resource_set(ec->pixmap, state->buffer);
e_pixmap_dirty(ec->pixmap);
e_pixmap_refresh(ec->pixmap);
}
static void
_e_comp_wl_surface_state_commit(E_Client *ec, E_Comp_Wl_Surface_State *state)
{
Eina_Bool first = EINA_FALSE;
Eina_Rectangle *dmg;
int x = 0, y = 0, w, h;
first = !e_pixmap_usable_get(ec->pixmap);
#ifndef HAVE_WAYLAND_ONLY
if (first && e_client_has_xwindow(ec))
first = !e_pixmap_usable_get(e_comp_x_client_pixmap_get(ec));
#endif
if (e_client_util_is_popup(ec) && (!ec->parent) && (!e_client_has_xwindow(ec)))
{
wl_resource_post_error(ec->comp_data->surface,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"Popup requires a parent shell surface before commit");
return;
}
ec->comp_data->in_commit = 1;
if (ec->ignored && ec->comp_data->shell.surface)
{
EC_CHANGED(ec);
ec->new_client = 1;
e_comp->new_clients++;
e_client_unignore(ec);
}
if (state->new_attach)
{
_e_comp_wl_surface_state_attach(ec, state);
if (first && (!ec->comp_data->cursor) && (!e_client_util_is_popup(ec)))
{
if (!e_client_has_xwindow(ec))
{
ec->take_focus = !starting || ec->internal_elm_win;
ec->want_focus = ec->override && !ec->parent;
if (ec->parent && (!ec->lock_user_location))
e_comp_object_util_center_on(ec->frame, ec->parent->frame);
}
}
}
_e_comp_wl_surface_state_buffer_set(state, NULL);
if (ec->comp_data->shell.surface)
{
if (ec->comp_data->shell.set.min_size.w)
ec->icccm.min_w = ec->comp_data->shell.set.min_size.w;
ec->comp_data->shell.set.min_size.w = 0;
if (ec->comp_data->shell.set.min_size.h)
ec->icccm.min_h = ec->comp_data->shell.set.min_size.h;
ec->comp_data->shell.set.min_size.h = 0;
if (ec->comp_data->shell.set.max_size.w)
ec->icccm.max_w = ec->comp_data->shell.set.max_size.w;
ec->comp_data->shell.set.max_size.w = 0;
if (ec->comp_data->shell.set.max_size.h)
ec->icccm.max_h = ec->comp_data->shell.set.max_size.h;
ec->comp_data->shell.set.max_size.h = 0;
if (ec->icccm.min_w && ec->icccm.max_w && (ec->icccm.min_w > ec->icccm.max_w))
wl_resource_post_error(ec->comp_data->shell.surface,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"min surface width cannot be larger than max surface width");
if (ec->icccm.min_h && ec->icccm.max_h && (ec->icccm.min_h > ec->icccm.max_h))
wl_resource_post_error(ec->comp_data->shell.surface,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"min surface height cannot be larger than max surface height");
if (ec->comp_data->shell.set.fullscreen && (!ec->fullscreen))
{
E_Zone *zone = e_comp_zone_id_get(ec->comp_data->shell.set.fs_zone);
if (zone && (zone != ec->zone))
e_client_zone_set(ec, zone);
e_client_fullscreen(ec, E_FULLSCREEN_RESIZE);
}
if (ec->comp_data->shell.set.unfullscreen)
e_client_unfullscreen(ec);
if (ec->comp_data->shell.set.maximize)
{
e_client_maximize(ec, (e_config->maximize_policy & E_MAXIMIZE_TYPE) | ec->comp_data->max);
ec->comp_data->max = 0;
}
if (ec->comp_data->shell.set.unmaximize)
{
e_client_unmaximize(ec, (e_config->maximize_policy & E_MAXIMIZE_TYPE) | ec->comp_data->unmax);
ec->comp_data->unmax = 0;
}
if (ec->comp_data->shell.set.minimize)
e_client_iconify(ec);
ec->comp_data->shell.set.fullscreen =
ec->comp_data->shell.set.unfullscreen =
ec->comp_data->shell.set.maximize =
ec->comp_data->shell.set.unmaximize =
ec->comp_data->shell.set.minimize = 0;
}
_e_comp_wl_surface_state_size_update(ec, state);
if (state->new_attach)
{
if (ec->changes.pos || ec->internal_elm_win)
e_comp_object_frame_xy_unadjust(ec->frame, ec->x, ec->y, &x, &y);
else
{
x = ec->client.x, y = ec->client.y;
if (first)
x -= ec->comp_data->shell.window.x, y -= ec->comp_data->shell.window.y;
}
if ((!ec->comp_data->buffer_commit) && e_client_has_xwindow(ec))
/* use client geometry to avoid race condition from x11 configure request */
x = ec->x, y = ec->y;
else
{
ec->client.w = state->bw;
ec->client.h = state->bh;
if (!ec->changes.size)
e_comp_object_frame_wh_adjust(ec->frame, ec->client.w, ec->client.h, &ec->w, &ec->h);
}
w = ec->client.w;
h = ec->client.h;
if (ec->comp_data->need_center)
{
Eina_List *l;
E_Client *cec;
EINA_LIST_FOREACH(ec->transients, l, cec)
{
cec->placed = 0;
if (!cec->ignored) EC_CHANGED(cec);
}
ec->comp_data->need_center = 0;
}
}
else
w = state->bw, h = state->bh;
if (!e_pixmap_usable_get(ec->pixmap))
{
if (ec->comp_data->mapped)
{
if ((ec->comp_data->shell.surface) && (ec->comp_data->shell.unmap))
ec->comp_data->shell.unmap(ec->comp_data->shell.surface);
else if (ec->comp_data->cursor || e_client_has_xwindow(ec) || ec->internal_elm_win ||
(ec->comp_data->sub.data && ec->comp_data->sub.data->parent->comp_data->mapped) ||
(ec == e_comp_wl->drag_client))
{
ec->visible = EINA_FALSE;
evas_object_hide(ec->frame);
ec->comp_data->mapped = 0;
}
}
}
else
{
if (!ec->comp_data->mapped)
{
if ((ec->comp_data->shell.surface) && (ec->comp_data->shell.map))
ec->comp_data->shell.map(ec->comp_data->shell.surface);
else if ((ec == e_comp->pointer->client.ec) || e_client_has_xwindow(ec) || ec->internal_elm_win ||
(ec->comp_data->sub.data && ec->comp_data->sub.data->parent->comp_data->mapped) ||
(ec == e_comp_wl->drag_client))
{
ec->visible = EINA_TRUE;
ec->ignored = 0;
evas_object_show(ec->frame);
ec->comp_data->mapped = 1;
if (e_client_has_xwindow(ec))
{
if (!e_client_util_desk_visible(ec, e_desk_current_get(ec->zone)))
evas_object_hide(ec->frame);
}
}
}
}
if (state->new_attach)
{
if ((ec->comp_data->shell.surface) && (ec->comp_data->shell.configure))
ec->comp_data->shell.configure(ec->comp_data->shell.surface,
x, y, state->bw, state->bh);
else
{
if (ec->netwm.sync.wait)
{
E_Client_Pending_Resize *pnd = NULL;
ec->netwm.sync.wait--;
/* skip pending for which we didn't get a reply */
while (ec->pending_resize)
{
pnd = eina_list_data_get(ec->pending_resize);
ec->pending_resize = eina_list_remove(ec->pending_resize, pnd);
if ((state->bw == pnd->w) && (state->bh == pnd->h))
break;
E_FREE(pnd);
}
if (pnd)
{
e_comp_object_frame_wh_adjust(ec->frame, pnd->w, pnd->h, &ec->w, &ec->h);
E_FREE(pnd);
}
ecore_evas_pointer_xy_get(e_comp->ee, &ec->mouse.current.mx, &ec->mouse.current.my);
ec->netwm.sync.send_time = ecore_loop_time_get();
}
if (e_comp_wl->drag && e_comp_wl->drag_client &&
(e_comp_wl->drag_client == ec))
{
e_comp_wl->drag->dx -= state->sx;
e_comp_wl->drag->dy -= state->sy;
e_drag_move(e_comp_wl->drag,
e_comp_wl->drag->x + state->sx, e_comp_wl->drag->y + state->sy);
e_drag_resize(e_comp_wl->drag, state->bw, state->bh);
}
else
e_client_util_move_resize_without_frame(ec, x, y, w, h);
}
if ((!ec->comp_data->sub.data) && (!ec->comp_data->buffer_commit))
{
if (e_client_has_xwindow(ec))
ec->placed = 0;
else if (!ec->placed)
ec->placed = ec->netwm.type != E_WINDOW_TYPE_NORMAL;
ec->want_focus |= ec->icccm.accepts_focus && (!ec->override);
}
}
else if (ec->comp_data->need_xdg_configure && ec->comp_data->shell.surface && !ec->iconic)
_e_comp_wl_configure_send(ec, 0);
state->sx = 0;
state->sy = 0;
if (state->new_attach)
ec->comp_data->buffer_commit = 1;
state->new_attach = EINA_FALSE;
/* insert state frame callbacks into comp_data->frames
* NB: This clears state->frames list */
ec->comp_data->frames = eina_list_merge(ec->comp_data->frames,
state->frames);
state->frames = NULL;
/* put state damages into surface */
if ((!e_comp->nocomp) && (ec->frame))
{
EINA_LIST_FREE(state->damages, dmg)
{
e_comp_object_damage(ec->frame, dmg->x, dmg->y, dmg->w, dmg->h);
eina_rectangle_free(dmg);
}
}
/* put state opaque into surface */
if (state->opaque)
{
if (!eina_tiler_empty(state->opaque))
{
Eina_Rectangle *rect;
Eina_Iterator *itr;
/* This is seriously wrong and results in only the first
* rectangle in the region being set, but in the usual
* case there's only one rectangle.
*/
itr = eina_tiler_iterator_new(state->opaque);
EINA_ITERATOR_FOREACH(itr, rect)
{
Eina_Rectangle r;
EINA_RECTANGLE_SET(&r, rect->x, rect->y, rect->w, rect->h);
E_RECTS_CLIP_TO_RECT(r.x, r.y, r.w, r.h, 0, 0, state->bw, state->bh);
e_pixmap_image_opaque_set(ec->pixmap, r.x, r.y, r.w, r.h);
break;
}
eina_iterator_free(itr);
eina_tiler_free(state->opaque);
state->opaque = NULL;
}
else
e_pixmap_image_opaque_set(ec->pixmap, 0, 0, 0, 0);
}
/* put state input into surface */
if (e_client_has_xwindow(ec))
{
Eina_Tiler *t = eina_tiler_new(ec->client.w, ec->client.h);
eina_tiler_tile_size_set(t, 1, 1);
if (ec->shaped_input)
{
Eina_Rectangle *r;
unsigned int i;
for (i = 0, r = ec->shape_input_rects; i < ec->shape_input_rects_num; i++, r++)
eina_tiler_rect_add(t, r);
}
else
eina_tiler_rect_add(t, &(Eina_Rectangle){0, 0, 65535, 65535});
e_comp_object_input_area_set(ec->frame, t);
eina_tiler_free(t);
}
else if (state->input)
{
e_comp_object_input_area_set(ec->frame, state->input);
eina_tiler_free(state->input);
state->input = NULL;
}
ec->comp_data->in_commit = 0;
_e_comp_wl_surface_outputs_update(ec);
}
static void
_e_comp_wl_surface_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
E_Client *ec;
DBG("Surface Cb Destroy: %d", wl_resource_get_id(resource));
ec = wl_resource_get_user_data(resource);
if (ec && (!e_object_is_del(E_OBJECT(ec)))) ec->comp_data->surface = NULL;
wl_resource_destroy(resource);
}
static void
_e_comp_wl_surface_cb_attach(struct wl_client *client, struct wl_resource *resource, struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
{
E_Client *ec;
E_Comp_Wl_Buffer *buffer = NULL;
if (!(ec = wl_resource_get_user_data(resource))) return;
if (e_object_is_del(E_OBJECT(ec))) return;
if (ec->comp_data->need_xdg_configure)
{
ec->comp_data->shell.buffer_attach_error(ec);
return;
}
if (buffer_resource)
{
if (!(buffer = e_comp_wl_buffer_get(buffer_resource)))
{
ERR("Could not get buffer from resource");
wl_client_post_no_memory(client);
return;
}
}
_e_comp_wl_surface_state_buffer_set(&ec->comp_data->pending, buffer);
ec->comp_data->pending.sx = sx;
ec->comp_data->pending.sy = sy;
ec->comp_data->pending.new_attach = EINA_TRUE;
}
static void
_e_comp_wl_surface_cb_damage_buffer(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
E_Client *ec;
Eina_Rectangle *dmg = NULL;
if (!(ec = wl_resource_get_user_data(resource))) return;
if (e_object_is_del(E_OBJECT(ec))) return;
if (!(dmg = eina_rectangle_new(x, y, w, h))) return;
ec->comp_data->pending.damages =
eina_list_append(ec->comp_data->pending.damages, dmg);
}
/*
* 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
_e_comp_wl_surface_cb_damage(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
_e_comp_wl_surface_cb_damage_buffer(client, resource, x, y, w, h);
}
static void
_e_comp_wl_frame_cb_destroy(struct wl_resource *resource)
{
E_Client *ec;
if (!(ec = wl_resource_get_user_data(resource))) return;
if (e_object_is_del(E_OBJECT(ec))) return;
ec->comp_data->frames =
eina_list_remove(ec->comp_data->frames, resource);
ec->comp_data->pending.frames =
eina_list_remove(ec->comp_data->pending.frames, resource);
if (!ec->comp_data->sub.data) return;
ec->comp_data->sub.data->cached.frames =
eina_list_remove(ec->comp_data->sub.data->cached.frames, resource);
}
static void
_e_comp_wl_surface_cb_frame(struct wl_client *client, struct wl_resource *resource, uint32_t callback)
{
E_Client *ec;
struct wl_resource *res;
if (!(ec = wl_resource_get_user_data(resource))) return;
if (e_object_is_del(E_OBJECT(ec))) return;
/* create frame callback */
if (!(res =
wl_resource_create(client, &wl_callback_interface, 1, callback)))
{
wl_resource_post_no_memory(resource);
return;
}
wl_resource_set_implementation(res, NULL, ec, _e_comp_wl_frame_cb_destroy);
ec->comp_data->pending.frames =
eina_list_append(ec->comp_data->pending.frames, res);
}
static void
_e_comp_wl_surface_cb_opaque_region_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource)
{
E_Client *ec;
if (!(ec = wl_resource_get_user_data(resource))) return;
if (e_object_is_del(E_OBJECT(ec))) return;
if (ec->comp_data->pending.opaque)
eina_tiler_free(ec->comp_data->pending.opaque);
ec->comp_data->pending.opaque = eina_tiler_new(65535, 65535);
eina_tiler_tile_size_set(ec->comp_data->pending.opaque, 1, 1);
if (region_resource)
{
Eina_Tiler *tmp;
if (!(tmp = wl_resource_get_user_data(region_resource)))
return;
eina_tiler_union(ec->comp_data->pending.opaque, tmp);
}
}
static void
_e_comp_wl_surface_cb_input_region_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource)
{
E_Client *ec;
if (!(ec = wl_resource_get_user_data(resource))) return;
if (e_object_is_del(E_OBJECT(ec))) return;
if (ec->comp_data->cursor || (ec == e_comp_wl->drag_client)) return;
if (ec->comp_data->pending.input)
eina_tiler_free(ec->comp_data->pending.input);
ec->comp_data->pending.input = eina_tiler_new(65535, 65535);
eina_tiler_tile_size_set(ec->comp_data->pending.input, 1, 1);
if (region_resource)
{
Eina_Tiler *tmp;
if (!(tmp = wl_resource_get_user_data(region_resource)))
return;
eina_tiler_union(ec->comp_data->pending.input, tmp);
}
else
{
eina_tiler_rect_add(ec->comp_data->pending.input,
&(Eina_Rectangle){0, 0, 65535, 65535});
}
}
static void
_e_comp_wl_surface_cb_commit(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
E_Client *ec, *subc;
Eina_List *l;
if (!(ec = wl_resource_get_user_data(resource))) return;
if (e_object_is_del(E_OBJECT(ec))) return;
if (e_comp_wl_subsurface_commit(ec)) return;
e_comp_wl_surface_commit(ec);
EINA_LIST_FOREACH(ec->comp_data->sub.list, l, subc)
{
if (ec != subc)
_e_comp_wl_subsurface_parent_commit(subc, EINA_FALSE);
}
}
static void
_e_comp_wl_surface_cb_buffer_transform_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, int32_t transform EINA_UNUSED)
{
/* DBG("Surface Buffer Transform: %d", wl_resource_get_id(resource)); */
}
static void
_e_comp_wl_surface_cb_buffer_scale_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, int32_t scale EINA_UNUSED)
{
/* DBG("Surface Buffer Scale: %d", wl_resource_get_id(resource)); */
}
static const struct wl_surface_interface _e_surface_interface =
{
_e_comp_wl_surface_cb_destroy,
_e_comp_wl_surface_cb_attach,
_e_comp_wl_surface_cb_damage,
_e_comp_wl_surface_cb_frame,
_e_comp_wl_surface_cb_opaque_region_set,
_e_comp_wl_surface_cb_input_region_set,
_e_comp_wl_surface_cb_commit,
_e_comp_wl_surface_cb_buffer_transform_set,
_e_comp_wl_surface_cb_buffer_scale_set,
_e_comp_wl_surface_cb_damage_buffer
};
static void
_e_comp_wl_surface_destroy(struct wl_resource *resource)
{
E_Client *ec;
if (!(ec = wl_resource_get_user_data(resource))) return;
if (!e_object_is_del(E_OBJECT(ec)))
{
if (ec->comp_data->mapped)
ec->comp_data->mapped = EINA_FALSE;
evas_object_hide(ec->frame);
}
if (ec->internal_elm_win)
{
e_pixmap_alias(ec->pixmap, E_PIXMAP_TYPE_WL, wl_resource_get_id(resource));
ec->ignored = 1;
}
else
e_object_del(E_OBJECT(ec));
}
static void
_e_comp_wl_compositor_cb_surface_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
struct wl_resource *res;
E_Client *wc, *ec = NULL;
Eina_List *l;
pid_t pid;
DBG("Compositor Cb Surface Create: %d", id);
/* try to create an internal surface */
if (!(res = wl_resource_create(client, &wl_surface_interface,
wl_resource_get_version(resource), id)))
{
ERR("Could not create compositor surface");
wl_client_post_no_memory(client);
return;
}
DBG("\tCreated Resource: %d", wl_resource_get_id(res));
/* set implementation on resource */
wl_resource_set_implementation(res, &_e_surface_interface, NULL,
_e_comp_wl_surface_destroy);
wl_client_get_credentials(client, &pid, NULL, NULL);
if ((client != e_comp_wl->xwl_client) && (pid == getpid())) //internal!
ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, (int64_t)id);
if (ec)
{
if (e_object_is_del(E_OBJECT(ec))) return;
}
else
{
E_Pixmap *ep = NULL;
/* try to create new pixmap */
do
{
if (--surface_id >= 0) surface_id = -1;
ep = e_pixmap_find(E_PIXMAP_TYPE_WL, surface_id);
} while (ep);
if (!(ep = e_pixmap_new(E_PIXMAP_TYPE_WL, surface_id)))
{
ERR("Could not create new pixmap");
wl_resource_destroy(res);
wl_client_post_no_memory(client);
return;
}
DBG("\tUsing Pixmap: %p", ep);
ec = e_client_new(ep, 0, 0);
}
if (ec->new_client)
e_comp->new_clients--;
ec->new_client = 0;
if ((!ec->client.w) && (!ec->client.h))
ec->client.w = ec->client.h = 1;
ec->comp_data->surface = res;
ec->netwm.pid = pid;
if (!ec->internal_elm_win)
{
ec->icccm.min_w = ec->icccm.min_h =
ec->icccm.max_w = ec->icccm.max_h = 0;
}
if (client != e_comp_wl->xwl_client)
ec->internal = pid == getpid();
if (ec->internal && (!e_comp_wl->wl.client_ec))
{
e_comp_wl->wl.client_ec = ec;
if (!e_client_focused_get())
{
e_comp_wl->kbd.focus = e_comp_wl->wl.client_ec->comp_data->surface;
e_comp_wl_data_device_keyboard_focus_set();
}
}
ec->icccm.delete_request |= ec->internal;
/* set reference to pixmap so we can fetch it later */
DBG("\tUsing Client: %p", ec);
wl_resource_set_user_data(res, ec);
#ifndef HAVE_WAYLAND_ONLY
EINA_LIST_FOREACH(e_comp_wl->xwl_pending, l, wc)
{
if (!e_pixmap_is_x(wc->pixmap)) continue;
if (wl_resource_get_id(res) !=
((E_Comp_X_Client_Data*)wc->comp_data)->surface_id) continue;
// wc, ec is correct! first arg should be the x client, 2nd wl one
e_comp_x_xwayland_client_setup(wc, ec);
break;
}
#endif
/* emit surface create signal */
wl_signal_emit(&e_comp_wl->signals.surface.create, res);
e_object_ref(E_OBJECT(ec));
}
static void
_e_comp_wl_region_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
DBG("Region Destroy: %d", wl_resource_get_id(resource));
wl_resource_destroy(resource);
}
static void
_e_comp_wl_region_cb_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;
DBG("Region Add: %d", wl_resource_get_id(resource));
DBG("\tGeom: %d %d %d %d", x, y, w, h);
/* get the tiler from the resource */
if ((tiler = wl_resource_get_user_data(resource)))
eina_tiler_rect_add(tiler, &(Eina_Rectangle){x, y, w, h});
}
static void
_e_comp_wl_region_cb_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;
DBG("Region Subtract: %d", wl_resource_get_id(resource));
DBG("\tGeom: %d %d %d %d", x, y, w, h);
/* get the tiler from the resource */
if ((tiler = wl_resource_get_user_data(resource)))
eina_tiler_rect_del(tiler, &(Eina_Rectangle){x, y, w, h});
}
static const struct wl_region_interface _e_region_interface =
{
_e_comp_wl_region_cb_destroy,
_e_comp_wl_region_cb_add,
_e_comp_wl_region_cb_subtract
};
static void
_e_comp_wl_compositor_cb_region_destroy(struct wl_resource *resource)
{
Eina_Tiler *tiler;
DBG("Compositor Region Destroy: %d", wl_resource_get_id(resource));
/* try to get the tiler from the region resource */
if ((tiler = wl_resource_get_user_data(resource)))
eina_tiler_free(tiler);
}
static void
_e_comp_wl_compositor_cb_region_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
Eina_Tiler *tiler;
struct wl_resource *res;
DBG("Region Create: %d", wl_resource_get_id(resource));
/* try to create new tiler */
if (!(tiler = eina_tiler_new(65535, 65535)))
{
ERR("Could not create Eina_Tiler");
wl_resource_post_no_memory(resource);
return;
}
/* set tiler size */
eina_tiler_tile_size_set(tiler, 1, 1);
if (!(res = wl_resource_create(client, &wl_region_interface, 1, id)))
{
ERR("\tFailed to create region resource");
wl_resource_post_no_memory(resource);
return;
}
wl_resource_set_implementation(res, &_e_region_interface, tiler,
_e_comp_wl_compositor_cb_region_destroy);
}
static const struct wl_compositor_interface _e_comp_interface =
{
_e_comp_wl_compositor_cb_surface_create,
_e_comp_wl_compositor_cb_region_create
};
static void
_e_comp_wl_compositor_cb_bind(struct wl_client *client, void *data EINA_UNUSED, uint32_t version, uint32_t id)
{
struct wl_resource *res;
if (!(res =
wl_resource_create(client, &wl_compositor_interface,
version, id)))
{
ERR("Could not create compositor resource");
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(res, &_e_comp_interface, e_comp, NULL);
}
static void
_e_comp_wl_subsurface_destroy(struct wl_resource *resource)
{
E_Client *ec;
E_Comp_Wl_Subsurf_Data *sdata;
/* try to get the client from resource data */
if (!(ec = wl_resource_get_user_data(resource))) return;
if (!ec->comp_data) return;
if (!(sdata = ec->comp_data->sub.data)) return;
if (sdata->parent)
{
/* remove this client from parents sub list */
sdata->parent->comp_data->sub.list =
eina_list_remove(sdata->parent->comp_data->sub.list, ec);
}
_e_comp_wl_surface_state_finish(&sdata->cached);
/* the client is getting deleted, which means the pixmap will be getting
* freed. We need to unset the surface user data */
/* wl_resource_set_user_data(ec->comp_data->surface, NULL); */
E_FREE(sdata);
ec->comp_data->sub.data = NULL;
}
static Eina_Bool
_e_comp_wl_subsurface_synchronized_get(E_Comp_Wl_Subsurf_Data *sdata)
{
while (sdata)
{
if (sdata->synchronized) return EINA_TRUE;
if (!sdata->parent) return EINA_FALSE;
sdata = sdata->parent->comp_data->sub.data;
}
return EINA_FALSE;
}
static void
_e_comp_wl_subsurface_commit_to_cache(E_Client *ec)
{
E_Comp_Client_Data *cdata;
E_Comp_Wl_Subsurf_Data *sdata;
if (!(cdata = ec->comp_data)) return;
if (!(sdata = cdata->sub.data)) return;
DBG("Subsurface Commit to Cache");
/* move pending damage to cached */
sdata->cached.damages = eina_list_merge(sdata->cached.damages,
cdata->pending.damages);
cdata->pending.damages = NULL;
if (cdata->pending.new_attach)
{
sdata->cached.new_attach = EINA_TRUE;
_e_comp_wl_surface_state_buffer_set(&sdata->cached,
cdata->pending.buffer);
e_pixmap_resource_set(ec->pixmap, cdata->pending.buffer);
e_pixmap_dirty(ec->pixmap);
e_pixmap_refresh(ec->pixmap);
}
sdata->cached.sx = cdata->pending.sx;
sdata->cached.sy = cdata->pending.sy;
/* sdata->cached.buffer = cdata->pending.buffer; */
sdata->cached.new_attach = cdata->pending.new_attach;
/* _e_comp_wl_surface_state_buffer_set(&cdata->pending, NULL); */
/* cdata->pending.sx = 0; */
/* cdata->pending.sy = 0; */
/* cdata->pending.new_attach = EINA_FALSE; */
sdata->cached.opaque = cdata->pending.opaque;
cdata->pending.opaque = NULL;
sdata->cached.input = cdata->pending.input;
cdata->pending.input = NULL;
sdata->cached.frames = eina_list_merge(sdata->cached.frames,
cdata->pending.frames);
cdata->pending.frames = NULL;
sdata->cached.has_data = EINA_TRUE;
}
static void
_cb_early_frame_cb(void *data, const Efl_Event *event EINA_UNUSED)
{
E_Client *ec;
ec = data;
if (ec->comp_data->early_frame == 1)
e_pixmap_image_clear(ec->pixmap, 1);
/* If some client does a surface frame and never follows with
* a render, it's just an idiot, so stop trying to help it with
* timing.
*/
if (ec->comp_data->early_frame++ > 15)
{
ec->comp_data->early_frame = 0;
efl_event_callback_del(e_comp->evas, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK,
_cb_early_frame_cb, data);
}
}
static void
_e_comp_wl_surface_early_frame(E_Client *ec)
{
if (e_comp_object_damage_exists(ec->frame))
{
efl_event_callback_del(e_comp->evas, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK,
_cb_early_frame_cb, ec);
ec->comp_data->early_frame = 0;
return;
}
if (ec->on_post_updates) return;
if (!ec->comp_data->early_frame)
efl_event_callback_add(e_comp->evas, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK,
_cb_early_frame_cb, ec);
ec->comp_data->early_frame = 1;
}
static void
_e_comp_wl_subsurface_commit_from_cache(E_Client *ec)
{
E_Comp_Client_Data *cdata;
E_Comp_Wl_Subsurf_Data *sdata;
if (!(cdata = ec->comp_data)) return;
if (!(sdata = cdata->sub.data)) return;
DBG("Subsurface Commit from Cache");
_e_comp_wl_surface_state_commit(ec, &sdata->cached);
_e_comp_wl_surface_early_frame(ec);
}
static void
_e_comp_wl_subsurface_parent_commit(E_Client *ec, Eina_Bool parent_synchronized)
{
E_Client *parent;
E_Comp_Wl_Subsurf_Data *sdata;
if (!(sdata = ec->comp_data->sub.data)) return;
if (!(parent = sdata->parent)) return;
if (sdata->position.set)
{
evas_object_move(ec->frame, parent->client.x + sdata->position.x,
parent->client.y + sdata->position.y);
sdata->position.set = EINA_FALSE;
}
if ((parent_synchronized) || (sdata->synchronized))
{
E_Client *subc;
Eina_List *l;
if (sdata->cached.has_data)
_e_comp_wl_subsurface_commit_from_cache(ec);
EINA_LIST_FOREACH(ec->comp_data->sub.list, l, subc)
{
if (ec != subc)
_e_comp_wl_subsurface_parent_commit(subc, EINA_TRUE);
}
}
}
static void
_e_comp_wl_subsurface_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void
_e_comp_wl_subsurface_cb_position_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y)
{
E_Client *ec;
E_Comp_Wl_Subsurf_Data *sdata;
DBG("Subsurface Cb Position Set: %d", wl_resource_get_id(resource));
/* try to get the client from resource data */
if (!(ec = wl_resource_get_user_data(resource))) return;
if (!(sdata = ec->comp_data->sub.data)) return;
sdata->position.x = x;
sdata->position.y = y;
sdata->position.set = EINA_TRUE;
}
static void
_e_comp_wl_subsurface_cb_place_above(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *sibling_resource)
{
E_Client *ec, *ecs;
E_Client *parent;
DBG("Subsurface Cb Place Above: %d", wl_resource_get_id(resource));
/* try to get the client from resource data */
if (!(ec = wl_resource_get_user_data(resource))) return;
if (!ec->comp_data->sub.data) return;
/* try to get the client from the sibling resource */
if (!(ecs = wl_resource_get_user_data(sibling_resource))) return;
if (!ecs->comp_data->sub.data) return;
if (!(parent = ec->comp_data->sub.data->parent)) return;
parent->comp_data->sub.list =
eina_list_remove(parent->comp_data->sub.list, ec);
parent->comp_data->sub.list =
eina_list_append_relative(parent->comp_data->sub.list, ec, ecs);
parent->comp_data->sub.restack_target = parent;
}
static void
_e_comp_wl_subsurface_cb_place_below(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *sibling_resource)
{
E_Client *ec, *ecs;
E_Client *parent