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.

3224 lines
96 KiB

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ecore_evas_wayland_private.h"
#include <Evas_Engine_Wayland.h>
#include "ecore_wl2_internal.h"
EAPI extern Eina_List *_evas_canvas_image_data_unset(Evas *eo_e);
EAPI extern void _evas_canvas_image_data_regenerate(Eina_List *list);
#define _smart_frame_type "ecore_evas_wl_frame"
static const char *interface_wl_name = "wayland";
static const int interface_wl_version = 1;
Eina_List *ee_list;
/* local structure for evas devices with IDs */
typedef struct _EE_Wl_Device EE_Wl_Device;
struct _EE_Wl_Device
{
Evas_Device *seat;
Evas_Device *pointer;
Evas_Device *keyboard;
Evas_Device *touch;
unsigned int id;
};
/* local variables */
static int _ecore_evas_wl_init_count = 0;
static Eina_Array *_ecore_evas_wl_event_hdls;
static void _ecore_evas_wayland_resize(Ecore_Evas *ee, int location);
static void _ecore_evas_wl_common_rotation_set(Ecore_Evas *ee, int rotation, int resize);
static void _ecore_evas_wl_selection_init(Ecore_Evas *ee);
/* local functions */
static void
_anim_cb_tick(Ecore_Wl2_Window *win EINA_UNUSED, uint32_t timestamp, void *data)
{
Ecore_Evas *ee = data;
Ecore_Evas_Engine_Wl_Data *edata;
double t;//, rt;
/* static double pt = 0.0, prt = 0.0; */
edata = ee->engine.data;
if (!edata->ticking) return;
t = ((double)timestamp / 1000.0);
ecore_loop_time_set(t);
/* rt = ecore_time_get(); */
/* printf("ECORE_EVAS: wl client anim tick %p | %p - %1.5f @ %1.5f delt=%1.5f | %1.5f\n", ee, edata, t, ecore_time_get(), t - pt, rt - prt); */
ecore_evas_animator_tick(ee, NULL, t);
/* pt = t; */
/* prt = rt; */
}
static void
_ecore_evas_wl_common_animator_register(Ecore_Evas *ee)
{
Ecore_Evas_Engine_Wl_Data *edata;
edata = (Ecore_Evas_Engine_Wl_Data *)ee->engine.data;
EINA_SAFETY_ON_TRUE_RETURN(edata->ticking);
EINA_SAFETY_ON_TRUE_RETURN(edata->frame != NULL);
edata->frame =
ecore_wl2_window_frame_callback_add(edata->win, _anim_cb_tick, ee);
if (!ecore_wl2_window_pending_get(edata->win) && !ee->in_async_render &&
!ee->animator_ticked && !ee->animator_ran && !ee->draw_block)
ecore_wl2_window_false_commit(edata->win);
edata->ticking = EINA_TRUE;
}
static void
_ecore_evas_wl_common_animator_unregister(Ecore_Evas *ee)
{
Ecore_Evas_Engine_Wl_Data *edata;
edata = ee->engine.data;
edata->ticking = EINA_FALSE;
if (edata->frame)
ecore_wl2_window_frame_callback_del(edata->frame);
edata->frame = NULL;
}
static void
_ecore_evas_wl_common_evas_changed(Ecore_Evas *ee, Eina_Bool changed)
{
Ecore_Evas_Engine_Wl_Data *edata;
if (changed) return;
edata = (Ecore_Evas_Engine_Wl_Data *)ee->engine.data;
if (edata->ticking && !ecore_wl2_window_pending_get(edata->win))
ecore_wl2_window_false_commit(edata->win);
}
static void
_ecore_evas_wl_common_state_update(Ecore_Evas *ee)
{
if (ee->func.fn_state_change) ee->func.fn_state_change(ee);
}
static void
_ecore_evas_wl_common_wm_rotation_protocol_set(Ecore_Evas *ee)
{
Ecore_Evas_Engine_Wl_Data *wdata;
wdata = ee->engine.data;
ee->prop.wm_rot.supported =
ecore_wl2_window_wm_rotation_supported_get(wdata->win);
}
static Eina_Bool
_ecore_evas_wl_common_cb_mouse_in(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
Ecore_Evas *ee;
Ecore_Event_Mouse_IO *ev;
LOGFN;
ev = event;
ee = ecore_event_window_match((Ecore_Window)ev->window);
if ((!ee) || (ee->ignore_events)) return ECORE_CALLBACK_PASS_ON;
if ((Ecore_Window)ev->window != ee->prop.window) return ECORE_CALLBACK_PASS_ON;
if (_ecore_evas_mouse_in_check(ee, ev->dev)) return ECORE_CALLBACK_PASS_ON;
_ecore_evas_mouse_inout_set(ee, ev->dev, EINA_TRUE, EINA_FALSE);
ecore_event_evas_seat_modifier_lock_update(ee->evas, ev->modifiers, ev->dev);
evas_event_feed_mouse_in(ee->evas, ev->timestamp, NULL);
_ecore_evas_mouse_device_move_process(ee, ev->dev, ev->x, ev->y, ev->timestamp);
return ECORE_CALLBACK_PASS_ON;
}
static Eina_Bool
_ecore_evas_wl_common_cb_mouse_out(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
Ecore_Evas *ee;
Ecore_Event_Mouse_IO *ev;
LOGFN;
ev = event;
ee = ecore_event_window_match((Ecore_Window)ev->window);
if ((!ee) || (ee->ignore_events)) return ECORE_CALLBACK_PASS_ON;
if ((Ecore_Window)ev->window != ee->prop.window)
return ECORE_CALLBACK_PASS_ON;
if (!_ecore_evas_mouse_in_check(ee, ev->dev)) return ECORE_CALLBACK_PASS_ON;
ecore_event_evas_seat_modifier_lock_update(ee->evas,
ev->modifiers, ev->dev);
_ecore_evas_mouse_device_move_process(ee, ev->dev, ev->x, ev->y, ev->timestamp);
evas_event_feed_mouse_out(ee->evas, ev->timestamp, NULL);
_ecore_evas_mouse_inout_set(ee, ev->dev, EINA_FALSE, EINA_FALSE);
return ECORE_CALLBACK_PASS_ON;
}
static Eina_Bool
_ecore_evas_wl_common_cb_focus_in(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
Ecore_Evas *ee;
Ecore_Wl2_Event_Focus_In *ev;
LOGFN;
ev = event;
ee = ecore_event_window_match((Ecore_Window)ev->window);
if ((!ee) || (ee->ignore_events)) return ECORE_CALLBACK_PASS_ON;
if ((Ecore_Window)ev->window != ee->prop.window)
return ECORE_CALLBACK_PASS_ON;
_ecore_evas_focus_device_set(ee, ev->dev, EINA_TRUE);
return ECORE_CALLBACK_PASS_ON;
}
static Eina_Bool
_ecore_evas_wl_common_cb_focus_out(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
Ecore_Evas *ee;
Ecore_Wl2_Event_Focus_Out *ev;
LOGFN;
ev = event;
ee = ecore_event_window_match((Ecore_Window)ev->window);
if ((!ee) || (ee->ignore_events)) return ECORE_CALLBACK_PASS_ON;
if ((Ecore_Window)ev->window != ee->prop.window)
return ECORE_CALLBACK_PASS_ON;
_ecore_evas_focus_device_set(ee, ev->dev, EINA_FALSE);
return ECORE_CALLBACK_PASS_ON;
}
static void
_ee_display_unset(Ecore_Evas *ee)
{
Evas_Engine_Info_Wayland *einfo;
Ecore_Evas_Engine_Wl_Data *wdata;
einfo = (Evas_Engine_Info_Wayland *)evas_engine_info_get(ee->evas);
if (!einfo) return;
wdata = ee->engine.data;
if (!strcmp(ee->driver, "wayland_egl"))
wdata->regen_objs = _evas_canvas_image_data_unset(ecore_evas_get(ee));
if (!evas_engine_info_set(ee->evas, (Evas_Engine_Info *)einfo))
WRN("Failed to set Evas Engine Info for '%s'", ee->driver);
}
static Eina_Bool
_ecore_evas_wl_common_cb_disconnect(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
Ecore_Wl2_Event_Disconnect *ev = event;
Eina_List *l;
Ecore_Evas *ee;
EINA_LIST_FOREACH(ee_list, l, ee)
{
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
if (wdata->display != ev->display) continue;
wdata->sync_done = EINA_FALSE;
wdata->defer_show = EINA_TRUE;
ee->visible = EINA_FALSE;
wdata->reset_pending = 1;
ee->draw_block = EINA_TRUE;
_ee_display_unset(ee);
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
ee_needs_alpha(Ecore_Evas *ee)
{
return ee->shadow.l || ee->shadow.r || ee->shadow.t || ee->shadow.b ||
ee->alpha;
}
static void
_ecore_evas_wayland_window_update(Ecore_Evas *ee, Ecore_Evas_Engine_Wl_Data *wdata, Eina_Bool new_alpha)
{
Evas_Engine_Info_Wayland *einfo;
Eina_Bool has_shadow, needs_alpha, change;
int w, h, fw, fh, shw = 0, shh = 0;
int fullw, fullh;
einfo = (Evas_Engine_Info_Wayland *)evas_engine_info_get(ee->evas);
EINA_SAFETY_ON_NULL_RETURN(einfo);
change = ee->shadow.changed || (new_alpha != ee->alpha);
ee->alpha = new_alpha;
has_shadow = ee->shadow.l || ee->shadow.r || ee->shadow.t || ee->shadow.b;
needs_alpha = ee_needs_alpha(ee);
if (einfo->info.destination_alpha != needs_alpha)
{
ecore_wl2_window_alpha_set(wdata->win, needs_alpha);
einfo->info.destination_alpha = needs_alpha;
if (!evas_engine_info_set(ee->evas, (Evas_Engine_Info *)einfo))
ERR("Failed to set Evas Engine Info for '%s'", ee->driver);
change |= EINA_TRUE;
}
ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
evas_output_framespace_get(ee->evas, NULL, NULL, &fw, &fh);
if (has_shadow)
{
shh = ee->shadow.r + ee->shadow.l;
shw = ee->shadow.t + ee->shadow.b;
}
fullw = w + fw - shw;
fullh = h + fh - shh;
/* shadow but no window alpha means we have a translucent visual but the
* window contents are always opaque - we can set the opaque region
* hint so the compositor can render more efficiently
*/
if (has_shadow && !ee->alpha)
{
ecore_wl2_window_opaque_region_set(wdata->win,
ee->shadow.l, ee->shadow.t,
fullw, fullh);
}
/* No alpha, no shadows - this should really not be a visual capable
* of translucent behaviour, but just in case things are sketchy in
* the back-end, let the compositor know we're fully opaque.
*/
else if (!ee->alpha)
{
ecore_wl2_window_opaque_region_set(wdata->win, 0, 0, fullw, fullh);
}
/* alpha is set and we might use it, so we'd better clear the
* opaque region, let the compositor blend it all.
*/
else
{
ecore_wl2_window_opaque_region_set(wdata->win, 0, 0, 0, 0);
}
ecore_wl2_window_input_region_set(wdata->win,
ee->shadow.l, ee->shadow.t,
fullw, fullh);
ecore_wl2_window_geometry_set(wdata->win,
ee->shadow.l, ee->shadow.t,
fullw, fullh);
if (!change) return;
if (ECORE_EVAS_PORTRAIT(ee))
evas_damage_rectangle_add(ee->evas, 0, 0, fullw, fullh);
else
evas_damage_rectangle_add(ee->evas, 0, 0, fullh, fullw);
ee->shadow.changed = EINA_FALSE;
}
static void
_ecore_evas_wl_common_resize(Ecore_Evas *ee, int w, int h)
{
Ecore_Evas_Engine_Wl_Data *wdata;
int ow, oh, ew, eh, fw, fh;
int diff = 0;
LOGFN;
if (!ee) return;
wdata = ee->engine.data;
if (!wdata) return;
ee->req.w = w;
ee->req.h = h;
evas_output_framespace_get(ee->evas, NULL, NULL, &fw, &fh);
/* TODO: wayland client can resize the ecore_evas directly.
* In the future, we will remove ee->req value in wayland backend */
ew = ee->w;
eh = ee->h;
ee->w = w;
ee->h = h;
if (wdata->win->xdg_set_min_size && wdata->win->xdg_toplevel &&
wdata->win->pending.min)
{
wdata->win->xdg_set_min_size(wdata->win->xdg_toplevel,
ee->prop.min.w + fw, ee->prop.min.h + fh);
wdata->win->pending.min = 0;
}
if (wdata->win->xdg_set_max_size && wdata->win->xdg_toplevel &&
wdata->win->pending.max)
{
wdata->win->xdg_set_max_size(wdata->win->xdg_toplevel,
ee->prop.max.w + fw, ee->prop.max.h + fh);
wdata->win->pending.max = 0;
}
if (wdata->win->zxdg_set_min_size && wdata->win->zxdg_toplevel &&
wdata->win->pending.min)
{
wdata->win->zxdg_set_min_size(wdata->win->zxdg_toplevel,
ee->prop.min.w + fw, ee->prop.min.h + fh);
wdata->win->pending.min = 0;
}
if (wdata->win->zxdg_set_max_size && wdata->win->zxdg_toplevel &&
wdata->win->pending.max)
{
wdata->win->zxdg_set_max_size(wdata->win->zxdg_toplevel,
ee->prop.max.w + fw, ee->prop.max.h + fh);
wdata->win->pending.max = 0;
}
if (!ee->prop.fullscreen)
{
int maxw = 0, maxh = 0;
int minw = 0, minh = 0;
if (ee->prop.min.w > 0) minw = (ee->prop.min.w);
if (ee->prop.min.h > 0) minh = (ee->prop.min.h);
if (ee->prop.max.w > 0) maxw = (ee->prop.max.w);
if (ee->prop.max.h > 0) maxh = (ee->prop.max.h);
if ((maxw > 0) && (w > maxw)) w = maxw;
else if (w < minw) w = minw;
if ((maxh > 0) && (h > maxh)) h = maxh;
else if (h < minh) h = minh;
if (!ee->prop.maximized)
{
/* calc new size using base size & step size */
if (ee->prop.step.w > 1)
{
int bw = ee->prop.base.w;
w = (bw + (((w - bw) / ee->prop.step.w) * ee->prop.step.w));
}
if (ee->prop.step.h > 1)
{
int bh = ee->prop.base.h;
h = (bh + (((h - bh) / ee->prop.step.h) * ee->prop.step.h));
}
if (!wdata->win->display->wl.efl_hints &&
EINA_DBL_NONZERO(ee->prop.aspect))
{
/* copied from e_client.c */
Evas_Aspect_Control aspect;
int aw, ah;
double val, a;
if (h > 0)
{
a = (double)w / (double)h;
if (fabs(a - ee->prop.aspect) > 0.001)
{
int step_w = ee->prop.step.w ?: 1;
int step_h = ee->prop.step.h ?: 1;
if (wdata->resizing ||
ecore_wl2_window_resizing_get(wdata->win))
ew = wdata->cw, eh = wdata->ch;
if (abs(w - ew) > abs(h - eh))
aspect = EVAS_ASPECT_CONTROL_HORIZONTAL;
else
aspect = EVAS_ASPECT_CONTROL_VERTICAL;
switch (aspect)
{
case EVAS_ASPECT_CONTROL_HORIZONTAL:
val = ((h - (w / ee->prop.aspect)) *
step_h) / step_h;
if (val > 0) ah = ceil(val);
else ah = floor(val);
if ((h - ah > minh) || (minh < 1))
{
h -= ah;
break;
}
EINA_FALLTHROUGH;
/* no break */
default:
val = (((h * ee->prop.aspect) - w) *
step_w) / step_w;
if (val > 0) aw = ceil(val);
else aw = floor(val);
if ((w + aw < maxw) || (maxw < 1))
w += aw;
break;
}
}
}
}
}
ee->w = w;
ee->h = h;
ee->req.w = w;
ee->req.h = h;
if (ECORE_EVAS_PORTRAIT(ee))
{
w += fw;
h += fh;
}
else
{
w += fh;
h += fw;
}
}
if (ECORE_EVAS_PORTRAIT(ee))
evas_output_size_get(ee->evas, &ow, &oh);
else
evas_output_size_get(ee->evas, &oh, &ow);
if (ECORE_EVAS_PORTRAIT(ee) && ((ow != w) || (oh != h)))
diff = 1;
if (!ECORE_EVAS_PORTRAIT(ee) && ((ow != h) || (oh != w)))
diff = 1;
if (diff)
{
if (ECORE_EVAS_PORTRAIT(ee))
{
evas_output_size_set(ee->evas, w, h);
evas_output_viewport_set(ee->evas, 0, 0, w, h);
}
else
{
evas_output_size_set(ee->evas, h, w);
evas_output_viewport_set(ee->evas, 0, 0, h, w);
}
if (ee->prop.avoid_damage)
{
int pdam = 0;
pdam = ecore_evas_avoid_damage_get(ee);
ecore_evas_avoid_damage_set(ee, 0);
ecore_evas_avoid_damage_set(ee, pdam);
}
if (ee->func.fn_resize) ee->func.fn_resize(ee);
}
_ecore_evas_wayland_window_update(ee, wdata, ee->alpha);
}
static void
_ecore_evas_wl_common_wm_rot_manual_rotation_done_job(void *data)
{
Ecore_Evas *ee = (Ecore_Evas *)data;
Ecore_Evas_Engine_Wl_Data *wdata;
wdata = ee->engine.data;
wdata->wm_rot.manual_mode_job = NULL;
ee->prop.wm_rot.manual_mode.wait_for_done = EINA_FALSE;
ecore_wl2_window_rotation_change_done_send
(wdata->win, ee->rotation, ee->w, ee->h);
wdata->wm_rot.done = EINA_FALSE;
}
static void
_ecore_evas_wl_common_wm_rot_manual_rotation_done(Ecore_Evas *ee)
{
if ((ee->prop.wm_rot.supported) &&
(ee->prop.wm_rot.app_set) &&
(ee->prop.wm_rot.manual_mode.set))
{
if (ee->prop.wm_rot.manual_mode.wait_for_done)
{
Ecore_Evas_Engine_Wl_Data *wdata;
wdata = ee->engine.data;
if (ee->prop.wm_rot.manual_mode.timer)
ecore_timer_del(ee->prop.wm_rot.manual_mode.timer);
ee->prop.wm_rot.manual_mode.timer = NULL;
if (wdata->wm_rot.manual_mode_job)
ecore_job_del(wdata->wm_rot.manual_mode_job);
wdata->wm_rot.manual_mode_job = ecore_job_add
(_ecore_evas_wl_common_wm_rot_manual_rotation_done_job, ee);
}
}
}
static Eina_Bool
_ecore_evas_wl_common_wm_rot_manual_rotation_done_timeout(void *data)
{
Ecore_Evas *ee = data;
ee->prop.wm_rot.manual_mode.timer = NULL;
_ecore_evas_wl_common_wm_rot_manual_rotation_done(ee);
return ECORE_CALLBACK_CANCEL;
}
static void
_ecore_evas_wl_common_wm_rot_manual_rotation_done_timeout_update(Ecore_Evas *ee)
{
if (ee->prop.wm_rot.manual_mode.timer)
ecore_timer_del(ee->prop.wm_rot.manual_mode.timer);
ee->prop.wm_rot.manual_mode.timer = ecore_timer_add
(4.0f, _ecore_evas_wl_common_wm_rot_manual_rotation_done_timeout, ee);
}
static Eina_Bool
_ecore_evas_wl_common_cb_window_configure(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
Ecore_Evas *ee;
Ecore_Evas_Engine_Wl_Data *wdata;
Ecore_Wl2_Event_Window_Configure *ev;
int nw = 0, nh = 0, fw, fh, pfw, pfh, sw, sh, contentw, contenth;
int ww, wh;
int framew, frameh;
Eina_Bool active, prev_max, prev_full, state_change = EINA_FALSE;
LOGFN;
ev = event;
ee = ecore_event_window_match((Ecore_Window)ev->win);
if (!ee) return ECORE_CALLBACK_PASS_ON;
if ((Ecore_Window)ev->win != ee->prop.window) return ECORE_CALLBACK_PASS_ON;
wdata = ee->engine.data;
if (!wdata) return ECORE_CALLBACK_PASS_ON;
if (!ecore_wl2_window_resizing_get(wdata->win) && !wdata->resizing)
wdata->cw = wdata->ch = 0;
prev_max = ee->prop.maximized;
prev_full = ee->prop.fullscreen;
ee->prop.maximized =
(ev->states & ECORE_WL2_WINDOW_STATE_MAXIMIZED) ==
ECORE_WL2_WINDOW_STATE_MAXIMIZED;
ee->prop.fullscreen =
(ev->states & ECORE_WL2_WINDOW_STATE_FULLSCREEN) ==
ECORE_WL2_WINDOW_STATE_FULLSCREEN;
active = wdata->activated;
wdata->activated = ecore_wl2_window_activated_get(wdata->win);
/* If the compositor set these, we need to update internal state
* so things like CSD continue to function */
wdata->win->set_config.maximized = ee->prop.maximized;
wdata->win->set_config.fullscreen = ee->prop.fullscreen;
nw = ev->w;
nh = ev->h;
ecore_evas_geometry_get(ee, NULL, NULL, &ww, &wh);
sw = ee->shadow.l + ee->shadow.r;
sh = ee->shadow.t + ee->shadow.b;
evas_output_framespace_get(ee->evas, NULL, NULL, &framew, &frameh);
contentw = ww - (framew - sw);
contenth = wh - (frameh - sh);
pfw = fw = ww - contentw;
pfh = fh = wh - contenth;
if ((prev_max != ee->prop.maximized) ||
(prev_full != ee->prop.fullscreen) ||
(active != wdata->activated))
{
state_change = EINA_TRUE;
_ecore_evas_wl_common_state_update(ee);
sw = ee->shadow.l + ee->shadow.r;
sh = ee->shadow.t + ee->shadow.b;
evas_output_framespace_get(ee->evas, NULL, NULL, &framew, &frameh);