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.

3812 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;
6 years ago
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)
{
6 years ago
int w = 0, h = 0;
if (e_object_is_del(E_OBJECT(ec))) return;
if (ec->changes.size || e_pixmap_usable_get(ec->pixmap))
6 years ago
{
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;