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.

3255 lines
96 KiB

#define E_COMP_WL
#include "e.h"
#include "e_comp_wl_screenshooter_server.h"
/* handle include for printing uint64_t */
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#define COMPOSITOR_VERSION 3
E_API int E_EVENT_WAYLAND_GLOBAL_ADD = -1;
#include "session-recovery-server-protocol.h"
#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);
/* local variables */
/* static Eina_Hash *clients_win_hash = NULL; */
static Eina_List *handlers = NULL;
static double _last_event_time = 0.0;
/* local functions */
static void
_e_comp_wl_configure_send(E_Client *ec, Eina_Bool edges)
{
int w, h;
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_focus_down_set(E_Client *ec)
{
Ecore_Window win = 0;
win = e_client_util_pwin_get(ec);
e_bindings_mouse_grab(E_BINDING_CONTEXT_WINDOW, win);
e_bindings_wheel_grab(E_BINDING_CONTEXT_WINDOW, win);
}
static void
_e_comp_wl_focus_check(void)
{
E_Client *ec;
if (stopping) return;
ec = e_client_focused_get();
if ((!ec) || e_pixmap_is_x(ec->pixmap))
e_grabinput_focus(e_comp->ee_win, E_FOCUS_METHOD_PASSIVE);
}
static void
_e_comp_wl_log_cb_print(const char *format, va_list args)
{
EINA_LOG_DOM_INFO(e_log_dom, format, args);
}
static Eina_Bool
_e_comp_wl_cb_read(void *data EINA_UNUSED, Ecore_Fd_Handler *hdlr EINA_UNUSED)
{
/* dispatch pending wayland events */
wl_event_loop_dispatch(e_comp_wl->wl.loop, 0);
return ECORE_CALLBACK_RENEW;
}
static void
_e_comp_wl_cb_prepare(void *data EINA_UNUSED, Ecore_Fd_Handler *hdlr EINA_UNUSED)
{
/* flush pending client events */
wl_display_flush_clients(e_comp_wl->wl.disp);
}
static Eina_Bool
_e_comp_wl_cb_module_idle(void *data EINA_UNUSED)
{
const char **m, *mods[] =
{
"wl_desktop_shell",
#ifdef HAVE_XWAYLAND
"xwayland",
#endif
NULL
};
/* check if we are still loading modules */
if (e_module_loading_get()) return ECORE_CALLBACK_RENEW;
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);
}
/* FIXME: NB:
* Do we need to dispatch pending wl events here ?? */
return ECORE_CALLBACK_CANCEL;
}
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, *tmp;
Eina_List *l;
if (!(ec = data)) return;
if (e_object_is_del(data)) return;
if (!ec->override) e_hints_window_visible_set(ec);
if ((!ec->override) && (!ec->re_manage) && (!ec->comp_data->reparented) &&
(!ec->comp_data->need_reparent))
{
ec->comp_data->need_reparent = EINA_TRUE;
ec->visible = EINA_TRUE;
}
if (!e_client_util_ignored_get(ec))
{
ec->take_focus = !starting;
EC_CHANGED(ec);
}
if (!ec->comp_data->need_reparent)
{
if ((ec->hidden) || (ec->iconic))
{
evas_object_hide(ec->frame);
e_comp_object_damage(ec->frame, 0, 0, ec->w, ec->h);
}
else if (!ec->internal_elm_win)
evas_object_show(ec->frame);
}
EINA_LIST_FOREACH(ec->e.state.video_child, l, tmp)
evas_object_show(tmp->frame);
}
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, *tmp;
Eina_List *l;
if (!(ec = data)) return;
EINA_LIST_FOREACH(ec->e.state.video_child, l, tmp)
evas_object_hide(tmp->frame);
}
static void
_e_comp_wl_evas_cb_mouse_in(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Client *ec;
Evas_Event_Mouse_In *ev;
struct wl_resource *res;
struct wl_client *wc;
Eina_List *l;
uint32_t serial;
ev = event;
if (!(ec = data)) return;
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)
{
if (!e_comp_wl_input_pointer_check(res)) continue;
if (wl_resource_get_client(res) != wc) continue;
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));
}
}
static void
_e_comp_wl_evas_cb_mouse_out(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Client *ec;
struct wl_resource *res;
struct wl_client *wc;
Eina_List *l;
uint32_t serial;
if (!(ec = data)) return;
if (ec->cur_mouse_action) return;
/* FIXME? this is a hack to just reset the cursor whenever we mouse out. not sure if accurate */
{
Evas_Object *o;
ecore_evas_cursor_get(e_comp->ee, &o, NULL, NULL, NULL);
if (e_comp->pointer->o_ptr != o)
e_pointer_object_set(e_comp->pointer, NULL, 0, 0);
}
if (e_comp_wl->ptr.ec == ec)
e_comp_wl->ptr.ec = NULL;
if (e_object_is_del(E_OBJECT(ec))) 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)
{
if (!e_comp_wl_input_pointer_check(res)) continue;
if (wl_resource_get_client(res) != wc) continue;
wl_pointer_send_leave(res, serial, ec->comp_data->surface);
}
}
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;
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 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->cur_mouse_action) 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_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;
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;
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->cur_mouse_action) return;
if (e_object_is_del(E_OBJECT(ec))) return;
if (ec->ignored) 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(ev->z));
else
dir = wl_fixed_from_int(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 (!ec->comp_data->surface) 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 (!ec->comp_data->surface) 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 (!ec->comp_data->surface) 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_adjust(int pid, int set, int adj, Eina_Bool use_adj, Eina_Bool adj_child, Eina_Bool do_child)
{
Eina_List *files;
char *file, buff[PATH_MAX];
FILE *f;
int pid2, ppid;
int num_read;
int n;
if (use_adj)
n = (getpriority(PRIO_PROCESS, pid) + adj);
else
n = set;
setpriority(PRIO_PROCESS, pid, n);
if (adj_child)
use_adj = EINA_TRUE;
if (!do_child) return;
files = ecore_file_ls("/proc");
EINA_LIST_FREE(files, file)
{
if (!isdigit(file[0]))
continue;
snprintf(buff, sizeof(buff), "/proc/%s/stat", file);
if ((f = fopen(buff, "r")))
{
pid2 = -1;
ppid = -1;
num_read = fscanf(f, "%i %*s %*s %i %*s", &pid2, &ppid);
fclose(f);
if (num_read == 2 && ppid == pid)
_e_comp_wl_client_priority_adjust(pid2, set,
adj, use_adj,
adj_child, do_child);
}
free(file);
}
}
static void
_e_comp_wl_client_priority_raise(E_Client *ec)
{
if (ec->netwm.pid <= 0) return;
if (ec->netwm.pid == getpid()) return;
_e_comp_wl_client_priority_adjust(ec->netwm.pid,
e_config->priority - 1, -1,
EINA_FALSE, EINA_TRUE, EINA_FALSE);
}
static void
_e_comp_wl_client_priority_normal(E_Client *ec)
{
if (ec->netwm.pid <= 0) return;
if (ec->netwm.pid == getpid()) return;
_e_comp_wl_client_priority_adjust(ec->netwm.pid, e_config->priority, 1,
EINA_FALSE, EINA_TRUE, EINA_FALSE);
}
static Eina_Bool
_e_comp_wl_evas_cb_focus_in_timer(E_Client *ec)
{
uint32_t serial, *k;
struct wl_resource *res;
Eina_List *l;
double t;
ec->comp_data->on_focus_timer = NULL;
if (!e_comp_wl->kbd.focused) return EINA_FALSE;
serial = wl_display_next_serial(e_comp_wl->wl.disp);
t = ecore_time_unix_get();
EINA_LIST_FOREACH(e_comp_wl->kbd.focused, l, res)
wl_array_for_each(k, &e_comp_wl->kbd.keys)
wl_keyboard_send_key(res, serial, t,
*k, WL_KEYBOARD_KEY_STATE_PRESSED);
return EINA_FALSE;
}
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;
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);
EINA_LIST_FOREACH(e_comp_wl->kbd.resources, l, res)
if (wl_resource_get_client(res) == wc)
e_comp_wl->kbd.focused = eina_list_append(e_comp_wl->kbd.focused, res);
if (!e_comp_wl->kbd.focused) return;
e_comp_wl_input_keyboard_enter_send(ec);
e_comp_wl_data_device_keyboard_focus_set();
ec->comp_data->on_focus_timer =
ecore_timer_add(0.8, (Ecore_Task_Cb)_e_comp_wl_evas_cb_focus_in_timer, ec);
}
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;
struct wl_resource *res;
uint32_t serial, *k;
Eina_List *l, *ll;
double t;
if (!(ec = data)) return;
if (!ec->comp_data) return;
E_FREE_FUNC(ec->comp_data->on_focus_timer, ecore_timer_del);
/* lower client priority */
if (!e_object_is_del(data))
_e_comp_wl_client_priority_normal(ec);
if (!ec->comp_data->surface) return;
if (!eina_list_count(e_comp_wl->kbd.resources)) return;
/* send keyboard_leave to all keyboard resources */
serial = wl_display_next_serial(e_comp_wl->wl.disp);
t = ecore_time_unix_get();
EINA_LIST_FOREACH_SAFE(e_comp_wl->kbd.focused, l, ll, res)
{
wl_array_for_each(k, &e_comp_wl->kbd.keys)
wl_keyboard_send_key(res, serial, t,
*k, WL_KEYBOARD_KEY_STATE_RELEASED);
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);
}
}
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 ((ec->shading) || (ec->shaded)) return;
if (!ec->comp_data->shell.configure_send) 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;
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;