From 6095a0d99fad4612c2eef20854a98dde06a8536e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 23 Jul 2021 10:30:35 +1000 Subject: [PATCH] Add a physical scale factor for the regions This isn't something that libei itself uses but clients like synergy need to know about this to be able to map relative pointer motion from one host into the right physical pixel on another host. This is required for mutter in the x11-compat mode where a 4k screen is logically twice the size of a 2k screen, despite having the same physical size. Signed-off-by: Peter Hutterer --- proto/ei.proto | 1 + src/libei-private.h | 4 +++ src/libei-proto.c | 1 + src/libei-proto.h | 1 + src/libei-region.c | 4 +++ src/libei.c | 7 +++-- src/libei.h | 70 +++++++++++++++++++++++++++++++++++++++++++ src/libeis-private.h | 1 + src/libeis-proto.c | 1 + src/libeis-region.c | 8 +++++ src/libeis.h | 11 +++++++ test/test-ei-device.c | 6 ++++ 12 files changed, 113 insertions(+), 2 deletions(-) diff --git a/proto/ei.proto b/proto/ei.proto index 64b4c66..65e922e 100644 --- a/proto/ei.proto +++ b/proto/ei.proto @@ -183,6 +183,7 @@ message DeviceRegion { uint32 offset_y = 3; uint32 width = 4; uint32 height = 5; + double scale = 6; } message DeviceAddedDone { diff --git a/src/libei-private.h b/src/libei-private.h index 1def074..16638d7 100644 --- a/src/libei-private.h +++ b/src/libei-private.h @@ -110,6 +110,7 @@ struct ei_region { struct list link; uint32_t x, y; uint32_t width, height; + double physical_scale; }; struct ei_device { @@ -275,6 +276,9 @@ ei_region_set_size(struct ei_region *region, uint32_t w, uint32_t h); void ei_region_set_offset(struct ei_region *region, uint32_t x, uint32_t y); +void +ei_region_set_physical_scale(struct ei_region *region, double scale); + bool ei_region_contains(struct ei_region *region, double x, double y); diff --git a/src/libei-proto.c b/src/libei-proto.c index 4cf6941..5b201ca 100644 --- a/src/libei-proto.c +++ b/src/libei-proto.c @@ -137,6 +137,7 @@ ei_proto_parse_message(struct brei_message *bmsg, size_t *consumed) .device_region.y = r->offset_y, .device_region.w = r->width, .device_region.h = r->height, + .device_region.scale = r->scale, }; } break; diff --git a/src/libei-proto.h b/src/libei-proto.h index fb70334..e02fba4 100644 --- a/src/libei-proto.h +++ b/src/libei-proto.h @@ -103,6 +103,7 @@ struct message { uint32_t y; uint32_t w; uint32_t h; + double scale; } device_region; struct message_device_removed { uint32_t deviceid; diff --git a/src/libei-region.c b/src/libei-region.c index e23304e..46e95e5 100644 --- a/src/libei-region.c +++ b/src/libei-region.c @@ -50,12 +50,16 @@ _public_ OBJECT_IMPLEMENT_GETTER(ei_region, width, uint32_t); _public_ OBJECT_IMPLEMENT_GETTER(ei_region, height, uint32_t); +_public_ +OBJECT_IMPLEMENT_GETTER(ei_region, physical_scale, double); +OBJECT_IMPLEMENT_SETTER(ei_region, physical_scale, double); struct ei_region * ei_region_new(void) { struct ei_region *region = ei_region_create(NULL); + region->physical_scale = 1.0; list_init(®ion->link); return region; diff --git a/src/libei.c b/src/libei.c index 4f51fc8..caf3926 100644 --- a/src/libei.c +++ b/src/libei.c @@ -597,7 +597,8 @@ handle_msg_device_added_done(struct ei *ei, uint32_t deviceid) static int handle_msg_device_region(struct ei *ei, uint32_t deviceid, uint32_t x, uint32_t y, - uint32_t w, uint32_t h) + uint32_t w, uint32_t h, + double scale) { log_debug(ei, "Adding device region for %#x\n", deviceid); @@ -608,6 +609,7 @@ handle_msg_device_region(struct ei *ei, uint32_t deviceid, _unref_(ei_region) *r = ei_region_new(); ei_region_set_offset(r, x, y); ei_region_set_size(r, w, h); + ei_region_set_physical_scale(r, scale); ei_device_add_region(device, r); @@ -894,7 +896,8 @@ connection_connected_handle_msg(struct ei *ei, struct message *msg) case MESSAGE_DEVICE_REGION: rc = handle_msg_device_region(ei, msg->device_region.deviceid, msg->device_region.x, msg->device_region.y, - msg->device_region.w, msg->device_region.h); + msg->device_region.w, msg->device_region.h, + msg->device_region.scale); break; case MESSAGE_DEVICE_REMOVED: rc = handle_msg_device_removed(ei, msg->device_removed.deviceid); diff --git a/src/libei.h b/src/libei.h index 89761fb..db8eb41 100644 --- a/src/libei.h +++ b/src/libei.h @@ -825,6 +825,76 @@ ei_region_get_width(struct ei_region *region); uint32_t ei_region_get_height(struct ei_region *region); +/** + * Return the physical scale for this region. The default scale is 1.0. + * + * The regions' coordinate space is in logical pixels in the EIS range. The + * logical pixels may or may not match the physical pixels on the output + * range but the mapping from logical pixels to physical pixels is performed + * by the EIS implementation. + * + * In some use-cases though, relative data from a remote input source needs + * to be converted by the libei client into an absolute movement on an EIS + * region. In that case, the physical scale provides the factor to multiply + * the relative logical input to provide the expected physical relative + * movement. + * + * For example consider the following dual-monitor setup comprising a 2k and + * a 4k monitor **of the same physical size**: + * The physical layout of the monitors appears like this: + * @code + * 2k 4k + * +-------------++-------------+ + * | || | + * | a b || c d | + * | || | + * +-------------++-------------+ + * @endcode + * + * The physical distance `ab` is the same as the physical distance `cd`. + * Where the EIS implementation supports high-dpi screens, the logical + * distance (in pixels) are identical too. + * + * Where the EIS implementation does not support high-dpi screens, the + * logical layout of these two monitors appears like this: + * + * @code + * 2k 4k + * +-------------++--------------------------+ + * | || | + * | a b || | + * | || | + * +-------------+| c d | + * | | + * | | + * | | + * +--------------------------+ + * @endcode + * + * While the two physical distances `ab` and `cd` are still identical, the + * logical distance `cd` (in pixels) is twice that of `ab`. + * Where a libei client receives relative deltas from an input source and + * converts that relative input into an absolute position on the screen, it + * needs to take this into account. + * + * For example, if a remote input source moves by relative 100 logical + * pixels, the libei client would convert this as `a + 100 = b` on the + * region for the 2k screen and send the absolute events to logically change + * the position from `a` to `b`. If the same remote input source moves by + * relative 100 logical pixels, the libei client would convert this as + * `c + 100 * scale = d` on the region for the 4k screen to logically + * change the position from `c` to `d`. While the pixel movement differs, + * the physical movement as seen by the user is thus identical. + * + * A second possible use-case for the physical scale is to match pixels from + * one region to their respective counterpart on a different region. + * For example, if the bottom-right corner of the 2k screen in the + * illustration above has a coordinate of x/y, the neighbouring pixel on the + * **physical** 4k screen is (0/y * scale). + */ +double +ei_region_get_physical_scale(struct ei_region *region); + /** * Return the keymap for this device or `NULL`. The keymap is constant for * the lifetime of the device after the @ref EI_EVENT_DEVICE_ADDED was diff --git a/src/libeis-private.h b/src/libeis-private.h index 528e473..e1315c6 100644 --- a/src/libeis-private.h +++ b/src/libeis-private.h @@ -119,6 +119,7 @@ struct eis_region { struct list link; uint32_t x, y; uint32_t width, height; + double physical_scale; }; struct eis_device { diff --git a/src/libeis-proto.c b/src/libeis-proto.c index 82d1de3..05c78e5 100644 --- a/src/libeis-proto.c +++ b/src/libeis-proto.c @@ -227,6 +227,7 @@ eis_proto_send_device_region(struct eis_client *client, struct eis_device *devic region.offset_y = r->y; region.width = r->width; region.height = r->height; + region.scale = r->physical_scale; msg.device_region = ®ion; msg.msg_case = SERVER_MESSAGE__MSG_DEVICE_REGION; diff --git a/src/libeis-region.c b/src/libeis-region.c index 83b8717..7dd6c34 100644 --- a/src/libeis-region.c +++ b/src/libeis-region.c @@ -48,6 +48,7 @@ eis_region_new(void) { struct eis_region *region = eis_region_create(NULL); + region->physical_scale = 1.0; list_init(®ion->link); return region; @@ -67,6 +68,13 @@ eis_region_set_size(struct eis_region *region, uint32_t w, uint32_t h) region->height = h; } +_public_ void +eis_region_set_physical_scale(struct eis_region *region, double scale) +{ + if (scale > 0.0) + region->physical_scale = scale; +} + bool eis_region_contains(struct eis_region *r, double x, double y) { diff --git a/src/libeis.h b/src/libeis.h index 1fcb407..44996e9 100644 --- a/src/libeis.h +++ b/src/libeis.h @@ -465,6 +465,17 @@ eis_region_set_size(struct eis_region *region, uint32_t w, uint32_t h); void eis_region_set_offset(struct eis_region *region, uint32_t x, uint32_t y); +/** + * Set the physical scale for this region. If unset, the scale defaults to + * 1.0. + * + * A @a scale value of less or equal to 0.0 will be silently ignored. + * + * See ei_region_get_physical_scale() for details. + */ +void +eis_region_set_physical_scale(struct eis_region *region, double scale); + struct eis_region * eis_region_ref(struct eis_region *region); diff --git a/test/test-ei-device.c b/test/test-ei-device.c index 0c0bb82..ef2977c 100644 --- a/test/test-ei-device.c +++ b/test/test-ei-device.c @@ -282,16 +282,19 @@ MUNIT_TEST(test_ei_device_regions) _unref_(eis_region) *r1 = eis_region_new(); eis_region_set_size(r1, 100, 200); eis_region_set_offset(r1, 300, 400); + /* no scale, default to 1.0 */ eis_device_configure_region(device, r1); _unref_(eis_region) *r2 = eis_region_new(); eis_region_set_size(r2, 500, 600); eis_region_set_offset(r2, 700, 800); + eis_region_set_physical_scale(r2, 3.9); eis_device_configure_region(device, r2); _unref_(eis_region) *r3 = eis_region_new(); eis_region_set_size(r3, 900, 1000); eis_region_set_offset(r3, 1100, 1200); + eis_region_set_physical_scale(r3, 0.3); eis_device_configure_region(device, r3); /* Add the same region twice, should be ignored */ @@ -311,18 +314,21 @@ MUNIT_TEST(test_ei_device_regions) munit_assert_int(ei_region_get_height(r), ==, 200); munit_assert_int(ei_region_get_x(r), ==, 300); munit_assert_int(ei_region_get_y(r), ==, 400); + munit_assert_double_equal(ei_region_get_physical_scale(r), 1.0, 2 /* precision */); r = ei_device_get_region(device, 1); munit_assert_int(ei_region_get_width(r), ==, 500); munit_assert_int(ei_region_get_height(r), ==, 600); munit_assert_int(ei_region_get_x(r), ==, 700); munit_assert_int(ei_region_get_y(r), ==, 800); + munit_assert_double_equal(ei_region_get_physical_scale(r), 3.9, 2 /* precision */); r = ei_device_get_region(device, 2); munit_assert_int(ei_region_get_width(r), ==, 900); munit_assert_int(ei_region_get_height(r), ==, 1000); munit_assert_int(ei_region_get_x(r), ==, 1100); munit_assert_int(ei_region_get_y(r), ==, 1200); + munit_assert_double_equal(ei_region_get_physical_scale(r), 0.3, 2 /* precision */); munit_assert_ptr_null(ei_device_get_region(device, 3)); }