This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch wl/browser-all
in repository enlightenment.
View the commit online.
commit 72943a6d9dcd9a5d681ff104381d57c9d282250a
Author: Cedric BAIL <[email protected]>
AuthorDate: Fri Aug 14 22:27:29 2026 -0600
e_comp_wl - convert between surface-local and canvas coordinates explicitly
Wayland hands a client coordinates in surface-local units and tells it, via
wl_output.scale, how many real pixels each one covers. E's canvas, its zones
and every ec->x/y/w/h are in real screen pixels. Today the two are treated as
the same thing, which is only true while that scale is 1.
Nothing here changes behaviour: e_comp_wl_client_scale_get() returns 1 and
every conversion below is an identity. This is the plumbing on its own, so
that turning the scale on is a separate commit that can be reverted by
itself.
The conversions are not all the same, which is the reason for doing this as
its own step rather than inline with the switch-on:
- sizes and input regions scale by the OUTPUT scale, because they end up as
canvas geometry and as evas rectangles hit-tested against it;
- the opaque region scales by the BUFFER scale and clips to the buffer, not
the surface, because it is consumed by evas_object_image_border_set()
which counts in buffer pixels;
- pointer and touch positions divide, and do it in fixed point rather than
whole units: at scale N one canvas pixel is 1/N of a surface-local unit,
and quantising to whole units would peg the pointer to every Nth position
inside the window.
A wl_region is a shared object - the same region can be set on several
surfaces - so it cannot be scaled where its rectangles are added. It is
scaled at commit, where the surface it is being applied to, and therefore the
factor, is known.
wlcs 1.8.1: 754 passed, 15 failed, failure set unchanged.
---
src/bin/e_comp_wl.c | 166 +++++++++++++++++++++++++++++++++++++++++++---------
src/bin/e_comp_wl.h | 1 +
2 files changed, 138 insertions(+), 29 deletions(-)
diff --git a/src/bin/e_comp_wl.c b/src/bin/e_comp_wl.c
index 790a977f2..42a14c629 100644
--- a/src/bin/e_comp_wl.c
+++ b/src/bin/e_comp_wl.c
@@ -214,6 +214,53 @@ e_comp_wl_pointer_frame_send(struct wl_resource *res)
wl_pointer_send_frame(res);
}
+/* A position on E's canvas as the surface-local coordinate to hand a client.
+ *
+ * Two things happen here: the client's origin comes off, and the output scale
+ * is divided out. The division is in fixed point rather than whole units
+ * because at scale N one canvas pixel is 1/N of a surface-local unit, and
+ * wl_fixed_t has 8 fractional bits to say so - rounding it to whole units
+ * would quantise the pointer to every Nth position inside the window. */
+static void
+_e_comp_wl_surface_coord_from_canvas(const E_Client *ec, int cx, int cy, wl_fixed_t *sx, wl_fixed_t *sy)
+{
+ int scale = e_comp_wl_client_scale_get(ec);
+
+ if (sx) *sx = wl_fixed_from_double((double)(cx - ec->client.x) / scale);
+ if (sy) *sy = wl_fixed_from_double((double)(cy - ec->client.y) / scale);
+}
+
+/* Multiply a surface-local region up into whatever space its consumer wants.
+ *
+ * A wl_region is a shared object - one region can be set on several surfaces -
+ * so it cannot be scaled where its rectangles are added. It has to happen here,
+ * where the surface, and therefore the factor, is known.
+ *
+ * Replaces *t with a scaled copy and frees the original. A factor of 1 is left
+ * alone rather than copied, which is the case every client hits today.
+ */
+static void
+_e_comp_wl_region_scale(Eina_Tiler **t, int scale)
+{
+ Eina_Tiler *out;
+ Eina_Iterator *it;
+ Eina_Rectangle *r;
+
+ if ((scale == 1) || (!t) || (!*t)) return;
+
+ out = eina_tiler_new(65535, 65535);
+ eina_tiler_tile_size_set(out, 1, 1);
+
+ it = eina_tiler_iterator_new(*t);
+ EINA_ITERATOR_FOREACH(it, r)
+ eina_tiler_rect_add(out, &(Eina_Rectangle){r->x * scale, r->y * scale,
+ r->w * scale, r->h * scale});
+ eina_iterator_free(it);
+
+ eina_tiler_free(*t);
+ *t = out;
+}
+
static void
_e_comp_wl_mouse_in(E_Client *ec, Evas_Event_Mouse_In *ev)
{
@@ -246,9 +293,12 @@ _e_comp_wl_mouse_in(E_Client *ec, Evas_Event_Mouse_In *ev)
* rather than quietly re-entering. */
if (ptr->entered) 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));
+ {
+ wl_fixed_t sx, sy;
+
+ _e_comp_wl_surface_coord_from_canvas(ec, ev->canvas.x, ev->canvas.y, &sx, &sy);
+ wl_pointer_send_enter(res, serial, ec->comp_data->surface, sx, sy);
+ }
e_comp_wl_pointer_frame_send(res);
e_comp_wl_input_pointer_cursor_update(ptr);
}
@@ -429,9 +479,12 @@ _e_comp_wl_send_mouse_move(E_Client *ec, int x, int y, unsigned int timestamp)
{
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));
+ {
+ wl_fixed_t sx, sy;
+
+ _e_comp_wl_surface_coord_from_canvas(ec, x, y, &sx, &sy);
+ wl_pointer_send_motion(res, timestamp, sx, sy);
+ }
e_comp_wl_pointer_frame_send(res);
}
}
@@ -654,8 +707,7 @@ _e_comp_wl_evas_cb_multi_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *o
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);
+ _e_comp_wl_surface_coord_from_canvas(ec, ev->canvas.x, ev->canvas.y, &x, &y);
EINA_LIST_FOREACH(e_comp_wl->touch.resources, l, res)
{
@@ -718,8 +770,7 @@ _e_comp_wl_evas_cb_multi_move(void *data, Evas *evas EINA_UNUSED, Evas_Object *o
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);
+ _e_comp_wl_surface_coord_from_canvas(ec, ev->cur.canvas.x, ev->cur.canvas.y, &x, &y);
EINA_LIST_FOREACH(e_comp_wl->touch.resources, l, res)
{
@@ -976,11 +1027,14 @@ _e_comp_wl_evas_cb_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_U
{
if (wl_resource_get_client(res) != wc) continue;
if (!e_comp_wl_input_touch_check(res)) continue;
- wl_touch_send_motion(res,
- (unsigned int)lround(ecore_loop_time_get() * 1000),
- tp->device,
- wl_fixed_from_int(tp->x - ec->client.x),
- wl_fixed_from_int(tp->y - ec->client.y));
+ {
+ wl_fixed_t sx, sy;
+
+ _e_comp_wl_surface_coord_from_canvas(ec, tp->x, tp->y, &sx, &sy);
+ wl_touch_send_motion(res,
+ (unsigned int)lround(ecore_loop_time_get() * 1000),
+ tp->device, sx, sy);
+ }
wl_touch_send_frame(res);
}
}
@@ -1421,12 +1475,12 @@ _e_comp_wl_cb_mouse_move(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Mou
e_comp_wl->drag)
{
struct wl_resource *res;
- int x, y;
+ wl_fixed_t sx, sy;
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));
+ _e_comp_wl_surface_coord_from_canvas(e_comp_wl->selection.target,
+ ev->x, ev->y, &sx, &sy);
+ wl_data_device_send_motion(res, ev->timestamp, sx, sy);
}
if (e_comp_wl->drag &&
e_comp_wl->drag_client &&
@@ -1436,6 +1490,31 @@ _e_comp_wl_cb_mouse_move(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Event_Mou
return ECORE_CALLBACK_RENEW;
}
+/* How many canvas pixels one of this client's surface-local units is worth.
+ *
+ * Wayland gives a client coordinates in surface-local units and tells it, via
+ * wl_output.scale, how many real pixels each one covers. E's canvas, its zones
+ * and every ec->x/y/w/h are in real screen pixels, so this factor is the whole
+ * of the conversion between the two: sizes and regions coming from a client
+ * are multiplied by it, positions and sizes going back out are divided.
+ *
+ * Note this is NOT e_scale. e_scale is a double that magnifies E's own
+ * widgets - it goes to elm_config_scale_set() and edje_scale_set() and nothing
+ * else - and a user is free to set it on an ordinary display just to make
+ * things readable. wl_output.scale claims something quite different and much
+ * stronger: that the panel really does have that many pixels per logical unit.
+ * Advertising the one as the other is what makes a client size itself against
+ * a screen half the size of the real one, which is the bug E-00 works around.
+ *
+ * Returns 1 for now, so every conversion below is an identity and nothing
+ * changes. Turning it on is a separate commit from teaching the code to ask.
+ */
+E_API int
+e_comp_wl_client_scale_get(const E_Client *ec EINA_UNUSED)
+{
+ return 1;
+}
+
/* wp_viewport's two commit-time errors.
*
* Neither can be answered when the request arrives. out_of_buffer needs the
@@ -1518,18 +1597,22 @@ _e_comp_wl_viewport_state_check(E_Client *ec, E_Comp_Wl_Surface_State *state)
* source. Rounding goes outwards on purpose: repainting a pixel that did not
* change costs a pixel, missing one that did leaves the screen wrong.
*
- * state->bw/bh is the surface size this commit is applying, so this must run
- * after _e_comp_wl_surface_state_size_update().
+ * state->bw/bh is the size this commit is applying, so this must run after
+ * _e_comp_wl_surface_state_size_update() - but that leaves it in canvas
+ * pixels, and the damage being converted is in surface-local ones, so the
+ * output scale has to come back off before the two can be divided.
*/
static void
_e_comp_wl_surface_damage_to_buffer(E_Client *ec, E_Comp_Wl_Surface_State *state, Eina_Rectangle *r)
{
E_Comp_Wl_Viewport_State *v = &ec->comp_data->viewport;
int scale = (ec->comp_data->buffer_scale > 0) ? ec->comp_data->buffer_scale : 1;
+ int oscale = e_comp_wl_client_scale_get(ec);
+ int surf_w = state->bw / oscale, surf_h = state->bh / oscale;
double sx, sy, sw, sh;
double x1, y1, x2, y2;
- if ((state->bw < 1) || (state->bh < 1)) return;
+ if ((surf_w < 1) || (surf_h < 1)) return;
/* The source rectangle in buffer pixels; without one, the whole buffer. */
if (v->src.set)
@@ -1553,13 +1636,13 @@ _e_comp_wl_surface_damage_to_buffer(E_Client *ec, E_Comp_Wl_Surface_State *state
/* Nothing to undo in the common case, and no rounding error to introduce
* by pretending otherwise. */
if ((!v->src.set) && (scale == 1) &&
- (state->bw == (int)sw) && (state->bh == (int)sh))
+ (surf_w == (int)sw) && (surf_h == (int)sh))
return;
- x1 = sx + (r->x * sw / state->bw);
- y1 = sy + (r->y * sh / state->bh);
- x2 = sx + ((r->x + r->w) * sw / state->bw);
- y2 = sy + ((r->y + r->h) * sh / state->bh);
+ x1 = sx + (r->x * sw / surf_w);
+ y1 = sy + (r->y * sh / surf_h);
+ x2 = sx + ((r->x + r->w) * sw / surf_w);
+ y2 = sy + ((r->y + r->h) * sh / surf_h);
r->x = (int)floor(x1);
r->y = (int)floor(y1);
@@ -1621,6 +1704,16 @@ _e_comp_wl_surface_state_size_update(E_Client *ec, E_Comp_Wl_Surface_State *stat
if (state->bw < 1) state->bw = 1;
if (state->bh < 1) state->bh = 1;
+ /* Everything above this line is in surface-local units, which is the space
+ * the buffer scale and the viewport are both defined in. From here down it
+ * has to be canvas pixels, because that is what ec->client.w/h means.
+ *
+ * A client that matches the output - buffer scale N on a scale N output -
+ * comes back out at exactly its buffer size, drawn one buffer pixel to one
+ * screen pixel, which is the whole point of it having sent a scaled buffer. */
+ state->bw *= e_comp_wl_client_scale_get(ec);
+ state->bh *= e_comp_wl_client_scale_get(ec);
+
/* Everything from the resize path down clamps geometry to the buffer,
* which is right until a viewport or a buffer scale makes the surface a
* size the buffer is not. Record the difference where the generic code
@@ -2157,13 +2250,24 @@ _e_comp_wl_surface_state_commit(E_Client *ec, E_Comp_Wl_Surface_State *state)
* rectangle in the region being set, but in the usual
* case there's only one rectangle.
*/
+ /* The opaque region ends up in evas_object_image_border_set(),
+ * which counts in buffer pixels - so this one scales by the
+ * BUFFER scale, not the output scale the input region uses, and
+ * clips to the buffer rather than to the surface. */
+ int bscale = (ec->comp_data->buffer_scale > 0) ?
+ ec->comp_data->buffer_scale : 1;
+ int raw_w = 0, raw_h = 0;
+
+ e_pixmap_size_get(ec->pixmap, &raw_w, &raw_h);
+
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);
+ EINA_RECTANGLE_SET(&r, rect->x * bscale, rect->y * bscale,
+ rect->w * bscale, rect->h * bscale);
+ E_RECTS_CLIP_TO_RECT(r.x, r.y, r.w, r.h, 0, 0, raw_w, raw_h);
e_pixmap_image_opaque_set(ec->pixmap, r.x, r.y, r.w, r.h);
break;
}
@@ -2196,6 +2300,10 @@ _e_comp_wl_surface_state_commit(E_Client *ec, E_Comp_Wl_Surface_State *state)
}
else if (state->input)
{
+ /* The input region arrives in surface-local units and is hit-tested
+ * against real evas rectangles on the canvas, so it scales by the
+ * output scale on the way in. */
+ _e_comp_wl_region_scale(&state->input, e_comp_wl_client_scale_get(ec));
e_comp_object_input_area_set(ec->frame, state->input);
eina_tiler_free(state->input);
diff --git a/src/bin/e_comp_wl.h b/src/bin/e_comp_wl.h
index b4f6285ed..08deb76ac 100644
--- a/src/bin/e_comp_wl.h
+++ b/src/bin/e_comp_wl.h
@@ -506,6 +506,7 @@ E_API struct wl_signal e_comp_wl_surface_create_signal_get(void);
E_API double e_comp_wl_idle_time_get(void);
E_API Eina_Bool e_comp_wl_output_init(const char *id, const char *make, const char *model, int x, int y, int w, int h, int pw, int ph, unsigned int refresh, unsigned int subpixel, unsigned int transform, unsigned int num);
E_API void e_comp_wl_output_remove(const char *id);
+E_API int e_comp_wl_client_scale_get(const E_Client *ec);
EINTERN Eina_Bool e_comp_wl_key_down(Ecore_Event_Key *ev, E_Client *ec);
EINTERN Eina_Bool e_comp_wl_key_up(Ecore_Event_Key *ev, E_Client *ec);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.