From b591105d8f6da8b8edde7a01d07fc596e7905746 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 27 Sep 2016 17:00:54 -0500 Subject: [PATCH] Add wp_viewporter support (Based heavily on initial work by Boram Park) --- src/bin/Makefile.mk | 13 ++- src/bin/e_comp_object.c | 20 ++++ src/bin/e_comp_object.h | 1 + src/bin/e_comp_wl.c | 170 ++++++++++++++++++++++++++++++ src/bin/e_comp_wl.h | 28 +++++ src/bin/e_comp_wl_extensions.c | 3 + src/bin/e_comp_wl_scaler.c | 186 +++++++++++++++++++++++++++++++++ src/bin/e_comp_wl_scaler.h | 7 ++ 8 files changed, 425 insertions(+), 3 deletions(-) create mode 100644 src/bin/e_comp_wl_scaler.c create mode 100644 src/bin/e_comp_wl_scaler.h diff --git a/src/bin/Makefile.mk b/src/bin/Makefile.mk index f199ef0d7..9cc0c9d69 100644 --- a/src/bin/Makefile.mk +++ b/src/bin/Makefile.mk @@ -225,7 +225,8 @@ ENLIGHTENMENTHEADERS += \ src/bin/e_comp_wl_data.h \ src/bin/e_comp_wl_dmabuf.h \ src/bin/e_comp_wl_input.h \ -src/bin/e_comp_wl.h +src/bin/e_comp_wl.h \ +src/bin/e_comp_wl_scaler.h endif enlightenment_gen_src = @@ -431,7 +432,8 @@ src/bin/e_comp_wl_data.c \ src/bin/e_comp_wl_input.c \ src/bin/e_comp_wl_dmabuf.c \ src/bin/e_comp_wl.c \ -src/bin/e_comp_wl_extensions.c +src/bin/e_comp_wl_extensions.c \ +src/bin/e_comp_wl_scaler.c enlightenment_gen_src += \ src/bin/generated/linux-dmabuf-unstable-v1-server-protocol.h \ @@ -441,7 +443,9 @@ src/bin/generated/session-recovery-server-protocol.h \ src/bin/generated/www-protocol.c \ src/bin/generated/www-server-protocol.h \ src/bin/generated/screenshooter-protocol.c \ -src/bin/generated/screenshooter-server-protocol.h +src/bin/generated/screenshooter-server-protocol.h \ +src/bin/generated/viewporter-protocol.c \ +src/bin/generated/viewporter-server-protocol.h src/bin/e_comp_wl_extensions.c: \ src/bin/generated/screenshooter-server-protocol.h \ @@ -453,6 +457,9 @@ src/bin/e_comp_wl.c: \ src/bin/e_comp_wl_dmabuf.c: \ src/bin/generated/linux-dmabuf-unstable-v1-server-protocol.h +src/bin/e_comp_wl_scaler.c: \ + src/bin/generated/viewporter-server-protocol.h + endif src_bin_enlightenment_CPPFLAGS = $(E_CPPFLAGS) -DE_LOGGING=1 @WAYLAND_CFLAGS@ @WAYLAND_EGL_CFLAGS@ @ECORE_X_CFLAGS@ diff --git a/src/bin/e_comp_object.c b/src/bin/e_comp_object.c index b1fc5cae9..4f59ec8f5 100644 --- a/src/bin/e_comp_object.c +++ b/src/bin/e_comp_object.c @@ -3867,6 +3867,26 @@ e_comp_object_dirty(Evas_Object *obj) e_comp_object_render(obj); } +E_API void +e_comp_object_map_set(Evas_Object *obj, Evas_Map *map) +{ + Eina_List *l; + Evas_Object *o; + + API_ENTRY; + + evas_object_map_set(obj, map); + evas_object_map_enable_set(obj, map ? EINA_TRUE : EINA_FALSE); + + EINA_LIST_FOREACH(cw->obj_mirror, l, o) + { + Evas_Map *map2 = evas_map_dup(map); + evas_object_map_set(o, map2); + evas_object_map_enable_set(o, map2 ? EINA_TRUE : EINA_FALSE); + evas_map_free(map2); + } +} + E_API Eina_Bool e_comp_object_render(Evas_Object *obj) { diff --git a/src/bin/e_comp_object.h b/src/bin/e_comp_object.h index 1701aceb8..55392faeb 100644 --- a/src/bin/e_comp_object.h +++ b/src/bin/e_comp_object.h @@ -91,6 +91,7 @@ E_API Eina_Bool e_comp_object_effect_start(Evas_Object *obj, Edje_Signal_Cb end_ E_API Eina_Bool e_comp_object_effect_stop(Evas_Object *obj, Edje_Signal_Cb end_cb); E_API E_Comp_Object_Mover *e_comp_object_effect_mover_add(int pri, const char *sig, E_Comp_Object_Mover_Cb provider, const void *data); E_API void e_comp_object_effect_mover_del(E_Comp_Object_Mover *prov); +E_API void e_comp_object_map_set(Evas_Object *obj, Evas_Map *map); #endif #endif diff --git a/src/bin/e_comp_wl.c b/src/bin/e_comp_wl.c index 5a8843041..496e4bddb 100644 --- a/src/bin/e_comp_wl.c +++ b/src/bin/e_comp_wl.c @@ -1247,6 +1247,12 @@ _e_comp_wl_surface_state_init(E_Comp_Wl_Surface_State *state, int w, int h) state->opaque = eina_tiler_new(w, h); eina_tiler_tile_size_set(state->opaque, 1, 1); + + state->buffer_viewport.buffer.transform = WL_OUTPUT_TRANSFORM_NORMAL; + state->buffer_viewport.buffer.scale = 1; + state->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1); + state->buffer_viewport.surface.width = -1; + state->buffer_viewport.changed = 0; } static void @@ -1299,6 +1305,157 @@ _e_comp_wl_surface_state_attach(E_Client *ec, E_Comp_Wl_Surface_State *state) e_pixmap_refresh(ec->pixmap); } +static void +_e_comp_wl_map_transform(int width, int height, uint32_t transform, int32_t scale, + int sx, int sy, int *dx, int *dy) +{ + switch (transform) + { + case WL_OUTPUT_TRANSFORM_NORMAL: + default: + *dx = sx, *dy = sy; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED: + *dx = width - sx, *dy = sy; + break; + case WL_OUTPUT_TRANSFORM_90: + *dx = height - sy, *dy = sx; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + *dx = height - sy, *dy = width - sx; + break; + case WL_OUTPUT_TRANSFORM_180: + *dx = width - sx, *dy = height - sy; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + *dx = sx, *dy = height - sy; + break; + case WL_OUTPUT_TRANSFORM_270: + *dx = sy, *dy = width - sx; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + *dx = sy, *dy = sx; + break; + } + + *dx *= scale; + *dy *= scale; +} + +static void +_e_comp_wl_map_size_cal_from_buffer(E_Client *ec, int32_t *ow, int32_t *oh) +{ + E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport; + int32_t width, height, pw, ph; + + if (!e_pixmap_size_get(ec->pixmap, &pw, &ph)) return; + + switch (vp->buffer.transform) + { + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + width = ph / vp->buffer.scale; + height = pw / vp->buffer.scale; + break; + default: + width = pw / vp->buffer.scale; + height = ph / vp->buffer.scale; + break; + } + + *ow = width; + *oh = height; +} + +static void +_e_comp_wl_map_size_cal_from_viewport(E_Client *ec, int32_t bw, int32_t bh, int32_t *ow, int32_t *oh) +{ + E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport; + int32_t width, height; + + width = bw; + height = bh; + + if (width != 0 && vp->surface.width != -1) + { + *ow = vp->surface.width; + *oh = vp->surface.height; + return; + } + + if (width != 0 && vp->buffer.src_width != wl_fixed_from_int(-1)) + { + int32_t w = wl_fixed_to_int(wl_fixed_from_int(1) - 1 + vp->buffer.src_width); + int32_t h = wl_fixed_to_int(wl_fixed_from_int(1) - 1 + vp->buffer.src_height); + + *ow = w ?: 1; + *oh = h ?: 1; + return; + } + + *ow = width; + *oh = height; +} + +static void +_e_comp_wl_map_apply(E_Client *ec) +{ + E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport; + Evas_Map *map; + int x1, y1, x2, y2, x, y; + int32_t w, h, vw, vh; + + _e_comp_wl_map_size_cal_from_buffer(ec, &w, &h); + _e_comp_wl_map_size_cal_from_viewport(ec, w, h, &vw, &vh); + + map = evas_map_new(4); + + evas_map_util_points_populate_from_geometry(map, + ec->x, ec->y, + vw, vh, 0); + + if (vp->buffer.src_width == wl_fixed_from_int(-1)) + { + x1 = 0.0; + y1 = 0.0; + x2 = w; + y2 = h; + } + else + { + x1 = wl_fixed_to_int(vp->buffer.src_x); + y1 = wl_fixed_to_int(vp->buffer.src_y); + x2 = wl_fixed_to_int(vp->buffer.src_x + vp->buffer.src_width); + y2 = wl_fixed_to_int(vp->buffer.src_y + vp->buffer.src_height); + } + + _e_comp_wl_map_transform(w, h, + vp->buffer.transform, vp->buffer.scale, + x1, y1, &x, &y); + evas_map_point_image_uv_set(map, 0, x, y); + + _e_comp_wl_map_transform(w, h, + vp->buffer.transform, vp->buffer.scale, + x2, y1, &x, &y); + evas_map_point_image_uv_set(map, 1, x, y); + + _e_comp_wl_map_transform(w, h, + vp->buffer.transform, vp->buffer.scale, + x2, y2, &x, &y); + evas_map_point_image_uv_set(map, 2, x, y); + + _e_comp_wl_map_transform(w, h, + vp->buffer.transform, vp->buffer.scale, + x1, y2, &x, &y); + evas_map_point_image_uv_set(map, 3, x, y); + + e_comp_object_map_set(ec->frame, map); + + evas_map_free(map); +} + static void _e_comp_wl_surface_state_commit(E_Client *ec, E_Comp_Wl_Surface_State *state) { @@ -1322,6 +1479,8 @@ _e_comp_wl_surface_state_commit(E_Client *ec, E_Comp_Wl_Surface_State *state) e_client_unignore(ec); } + ec->comp_data->scaler.buffer_viewport = state->buffer_viewport; + if (state->new_attach) _e_comp_wl_surface_state_attach(ec, state); @@ -1470,6 +1629,12 @@ _e_comp_wl_surface_state_commit(E_Client *ec, E_Comp_Wl_Surface_State *state) } } + if (e_pixmap_usable_get(ec->pixmap) && state->buffer_viewport.changed) + { + _e_comp_wl_map_apply(ec); + state->buffer_viewport.changed = 0; + } + state->sx = 0; state->sy = 0; state->new_attach = EINA_FALSE; @@ -2420,6 +2585,11 @@ _e_comp_wl_client_cb_new(void *data EINA_UNUSED, E_Client *ec) /* set initial client data properties */ ec->comp_data->mapped = EINA_FALSE; + ec->comp_data->scaler.buffer_viewport.buffer.transform = WL_OUTPUT_TRANSFORM_NORMAL; + ec->comp_data->scaler.buffer_viewport.buffer.scale = 1; + ec->comp_data->scaler.buffer_viewport.buffer.src_width = wl_fixed_from_int(-1); + ec->comp_data->scaler.buffer_viewport.surface.width = -1; + /* add this client to the hash */ /* eina_hash_add(clients_win_hash, &win, ec); */ } diff --git a/src/bin/e_comp_wl.h b/src/bin/e_comp_wl.h index 2ee3aedca..1945732f2 100644 --- a/src/bin/e_comp_wl.h +++ b/src/bin/e_comp_wl.h @@ -43,6 +43,7 @@ }) typedef struct _E_Comp_Wl_Buffer E_Comp_Wl_Buffer; +typedef struct _E_Comp_Wl_Buffer_Viewport E_Comp_Wl_Buffer_Viewport; typedef struct _E_Comp_Wl_Subsurf_Data E_Comp_Wl_Subsurf_Data; typedef struct _E_Comp_Wl_Surface_State E_Comp_Wl_Surface_State; typedef struct _E_Comp_Wl_Client_Data E_Comp_Wl_Client_Data; @@ -64,6 +65,26 @@ struct _E_Comp_Wl_Buffer uint32_t busy; }; +struct _E_Comp_Wl_Buffer_Viewport { + struct + { + uint32_t transform; /* wl_surface.set_buffer_transform */ + int32_t scale; /* wl_surface.set_scaling_factor */ + + /* If src_width != wl_fixed_from_int(-1), then and only then src_* are used. */ + wl_fixed_t src_x, src_y; + wl_fixed_t src_width, src_height; + } buffer; + + struct + { + /* If width == -1, the size is inferred from the buffer. */ + int32_t width, height; + } surface; + + int changed; +}; + struct _E_Comp_Wl_Surface_State { int sx, sy; @@ -72,6 +93,7 @@ struct _E_Comp_Wl_Surface_State struct wl_listener buffer_destroy_listener; Eina_List *damages, *frames; Eina_Tiler *input, *opaque; + E_Comp_Wl_Buffer_Viewport buffer_viewport; Eina_Bool new_attach : 1; Eina_Bool has_data : 1; }; @@ -309,6 +331,12 @@ struct _E_Comp_Wl_Client_Data int32_t x, y; } popup; + struct + { + struct wl_resource *viewport; + E_Comp_Wl_Buffer_Viewport buffer_viewport; + } scaler; + int32_t on_outputs; /* Bitfield of the outputs this client is present on */ E_Maximize max; diff --git a/src/bin/e_comp_wl_extensions.c b/src/bin/e_comp_wl_extensions.c index 2ead071be..1ea7d1f3e 100644 --- a/src/bin/e_comp_wl_extensions.c +++ b/src/bin/e_comp_wl_extensions.c @@ -6,6 +6,7 @@ #include "screenshooter-server-protocol.h" #include "session-recovery-server-protocol.h" #include "www-server-protocol.h" +#include "e_comp_wl_scaler.h" static void _e_comp_wl_extensions_client_move_begin(void *d EINA_UNUSED, E_Client *ec) @@ -280,6 +281,8 @@ e_comp_wl_extensions_init(void) GLOBAL_CREATE_OR_RETURN(screenshooter, zwp_screenshooter_interface, 1); GLOBAL_CREATE_OR_RETURN(www, www_interface, 1); + e_scaler_init(); + ecore_event_handler_add(ECORE_WL2_EVENT_SYNC_DONE, _dmabuf_add, NULL); e_client_hook_add(E_CLIENT_HOOK_MOVE_BEGIN, _e_comp_wl_extensions_client_move_begin, NULL); diff --git a/src/bin/e_comp_wl_scaler.c b/src/bin/e_comp_wl_scaler.c new file mode 100644 index 000000000..9e417698d --- /dev/null +++ b/src/bin/e_comp_wl_scaler.c @@ -0,0 +1,186 @@ +#define E_COMP_WL +#include "e.h" +#include "viewporter-server-protocol.h" + +static void +_e_viewport_destroy(struct wl_resource *resource) +{ + E_Client *ec = wl_resource_get_user_data(resource); + E_Comp_Client_Data *cdata; + + if (e_object_is_del(E_OBJECT(ec))) return; + + EINA_SAFETY_ON_NULL_RETURN(cdata = ec->comp_data); + EINA_SAFETY_ON_NULL_RETURN(cdata->scaler.viewport); + + cdata->scaler.viewport = NULL; + cdata->pending.buffer_viewport.buffer.src_width = wl_fixed_from_int(-1); + cdata->pending.buffer_viewport.surface.width = -1; + cdata->pending.buffer_viewport.changed = 1; +} + +static void +_e_viewport_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +_e_viewport_cb_set_source(struct wl_client *client EINA_UNUSED, + struct wl_resource *resource, + wl_fixed_t src_x, + wl_fixed_t src_y, + wl_fixed_t src_width, + wl_fixed_t src_height) +{ + E_Client *ec = wl_resource_get_user_data(resource); + E_Comp_Client_Data *cdata; + + if (e_object_is_del(E_OBJECT(ec))) return; + + EINA_SAFETY_ON_NULL_RETURN(cdata = ec->comp_data); + EINA_SAFETY_ON_NULL_RETURN(cdata->scaler.viewport); + + if (src_width == wl_fixed_from_int(-1) && src_height == wl_fixed_from_int(-1)) + { + /* unset source size */ + cdata->pending.buffer_viewport.buffer.src_width = wl_fixed_from_int(-1); + cdata->pending.buffer_viewport.changed = 1; + return; + } + + if (src_width <= 0 || src_height <= 0) + { + wl_resource_post_error(resource, + WP_VIEWPORT_ERROR_BAD_VALUE, + "source size must be positive (%fx%f)", + wl_fixed_to_double(src_width), + wl_fixed_to_double(src_height)); + return; + } + + cdata->pending.buffer_viewport.buffer.src_x = src_x; + cdata->pending.buffer_viewport.buffer.src_y = src_y; + cdata->pending.buffer_viewport.buffer.src_width = src_width; + cdata->pending.buffer_viewport.buffer.src_height = src_height; + cdata->pending.buffer_viewport.changed = 1; +} + +static void +_e_viewport_cb_set_destination(struct wl_client *client EINA_UNUSED, + struct wl_resource *resource, + int32_t dst_width, + int32_t dst_height) +{ + E_Client *ec = wl_resource_get_user_data(resource); + E_Comp_Client_Data *cdata; + + if (e_object_is_del(E_OBJECT(ec))) return; + + EINA_SAFETY_ON_NULL_RETURN(cdata = ec->comp_data); + EINA_SAFETY_ON_NULL_RETURN(cdata->scaler.viewport); + + if (dst_width == -1 && dst_height == -1) + { + /* unset destination size */ + cdata->pending.buffer_viewport.surface.width = -1; + cdata->pending.buffer_viewport.changed = 1; + return; + } + + if (dst_width <= 0 || dst_height <= 0) + { + wl_resource_post_error(resource, + WP_VIEWPORT_ERROR_BAD_VALUE, + "destination size must be positive (%dx%d)", + dst_width, dst_height); + return; + } + + cdata->pending.buffer_viewport.surface.width = dst_width; + cdata->pending.buffer_viewport.surface.height = dst_height; + cdata->pending.buffer_viewport.changed = 1; +} + +static const struct wp_viewport_interface _e_viewport_interface = { + _e_viewport_cb_destroy, + _e_viewport_cb_set_source, + _e_viewport_cb_set_destination +}; + +static void +_e_scaler_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +_e_scaler_cb_get_viewport(struct wl_client *client EINA_UNUSED, struct wl_resource *scaler, uint32_t id, struct wl_resource *surface_resource) +{ + int version = wl_resource_get_version(scaler); + E_Client *ec; + struct wl_resource *res; + E_Comp_Client_Data *cdata; + + if (!(ec = wl_resource_get_user_data(surface_resource))) return; + if (e_object_is_del(E_OBJECT(ec))) return; + if (!(cdata = ec->comp_data)) return; + + if (cdata->scaler.viewport) + { + wl_resource_post_error(scaler, + WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS, + "a viewport for that surface already exists"); + return; + } + + res = wl_resource_create(client, &wp_viewport_interface, version, id); + if (res == NULL) + { + wl_client_post_no_memory(client); + return; + } + + cdata->scaler.viewport = res; + wl_resource_set_implementation(res, &_e_viewport_interface, ec, _e_viewport_destroy); +} + +static const struct wp_viewporter_interface _e_scaler_interface = +{ + _e_scaler_cb_destroy, + _e_scaler_cb_get_viewport +}; + +static void +_e_scaler_cb_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct wl_resource *res; + + if (!(res = wl_resource_create(client, &wp_viewporter_interface, MIN(version, 2), id))) + { + ERR("Could not create scaler resource: %m"); + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(res, &_e_scaler_interface, NULL, NULL); +} + +Eina_Bool +e_scaler_init(void) +{ + E_Comp_Wl_Data *cdata; + + if (!e_comp) return EINA_FALSE; + if (!(cdata = e_comp->wl_comp_data)) return EINA_FALSE; + if (!cdata->wl.disp) return EINA_FALSE; + /* try to add scaler to wayland globals */ + if (!wl_global_create(cdata->wl.disp, &wp_viewporter_interface, 1, + cdata, _e_scaler_cb_bind)) + { + ERR("Could not add scaler to wayland globals: %m"); + return EINA_FALSE; + } + + return EINA_TRUE; +} diff --git a/src/bin/e_comp_wl_scaler.h b/src/bin/e_comp_wl_scaler.h new file mode 100644 index 000000000..ce9a21acd --- /dev/null +++ b/src/bin/e_comp_wl_scaler.h @@ -0,0 +1,7 @@ +#ifndef E_COMP_WL_SCALER_H +#define E_COMP_WL_SCALER_H + +Eina_Bool e_scaler_init(void); + +#endif +