Add a mapping_id to the regions

This allows a caller to match up a region with other data, e.g. in the
remote desktop case the same mapping_id can be assigned to the pipewire
stream that represents that output.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2023-08-28 21:24:33 +10:00
parent 2fbd22984f
commit 76652350cc
14 changed files with 187 additions and 12 deletions

View file

@ -555,7 +555,7 @@
</event>
</interface>
<interface name="ei_device" version="1">
<interface name="ei_device" version="2">
<description summary="device object">
An ei_device represents a single logical input devices. Like physical input devices
an ei_device may have multiple capabilities and may e.g. function as pointer
@ -871,6 +871,30 @@
<arg name="serial" type="uint32" summary="this event's serial number"/>
<arg name="timestamp" type="uint64" summary="timestamp in microseconds"/>
</event>
<!-- ei_device events version 2 -->
<event name="region_mapping_id" since="2">
<description summary="region id notification">
Notifies the client that the region specified in the next ei_device.region
event is to be assigned the given mapping_id.
This ID can be used by the client to identify an external resource that has a
relationship with this region.
For example the client may receive a data stream with the video
data that this region represents. By attaching the same identifier to the data
stream and this region the EIS implementation can inform the client
that the video data stream and the region represent paired data.
This event is optional and sent immediately after object creation but before
the corresponding ei_device.region event. Where a device has multiple regions,
this event may be sent zero or one time for each region.
It is a protocol violation to send this event after the ei_device.done event or
to send this event without a corresponding following ei_device.region event.
</description>
<arg name="mapping_id" type="string" summary="region mapping id"/>
</event>
</interface>
<interface name="ei_pointer" version="1">

View file

@ -91,6 +91,7 @@ ei_device_destroy(struct ei_device *device)
ei_keyboard_unref(device->keyboard);
ei_seat_unref(seat);
free(device->name);
free(device->pending_region_mapping_id);
}
_public_
@ -194,11 +195,26 @@ handle_msg_region(struct ei_device *device, uint32_t x, uint32_t y,
ei_region_set_offset(r, x, y);
ei_region_set_size(r, w, h);
ei_region_set_physical_scale(r, scale);
_cleanup_free_ char *mapping_id = steal(&device->pending_region_mapping_id);
if (mapping_id)
ei_region_set_mapping_id(r, mapping_id);
ei_device_add_region(device, r);
return NULL;
}
static struct brei_result *
handle_msg_region_mapping_id(struct ei_device *device, const char *mapping_id)
{
if (device->pending_region_mapping_id)
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"EIS sent the region mapping_id twice");
device->pending_region_mapping_id = xstrdup(mapping_id);
return NULL;
}
static struct brei_result *
handle_msg_done(struct ei_device *device)
{
@ -464,6 +480,8 @@ static const struct ei_device_interface interface = {
.stop_emulating = handle_msg_stop_emulating,
.frame = handle_msg_frame,
.interface = handle_msg_interface,
/* v2 */
.region_mapping_id = handle_msg_region_mapping_id,
};
const struct ei_device_interface *

View file

@ -86,6 +86,8 @@ struct ei_device {
} scroll_state;
struct ei_keymap *keymap;
char *pending_region_mapping_id;
};
struct ei_keymap {

View file

@ -24,12 +24,14 @@
#include "config.h"
#include "util-strings.h"
#include "libei-private.h"
static void
ei_region_destroy(struct ei_region *region)
{
free(region->mapping_id);
list_remove(&region->link);
}
@ -54,6 +56,8 @@ 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);
_public_
OBJECT_IMPLEMENT_GETTER(ei_region, mapping_id, const char *);
struct ei_region *
ei_region_new(void)
@ -80,6 +84,12 @@ ei_region_set_size(struct ei_region *region, uint32_t w, uint32_t h)
region->height = h;
}
void
ei_region_set_mapping_id(struct ei_region *region, const char *mapping_id)
{
region->mapping_id = xstrdup(mapping_id);
}
_public_ bool
ei_region_contains(struct ei_region *r, double x, double y)
{
@ -103,17 +113,19 @@ ei_region_convert_point(struct ei_region *r, double *x, double *y)
#include "util-munit.h"
MUNIT_TEST(test_region_setters)
{
struct ei_region r = {0};
_unref_(ei_region) *r = ei_region_new();
ei_region_set_size(&r, 1, 2);
ei_region_set_offset(&r, 3, 4);
ei_region_set_physical_scale(&r, 5.6);
ei_region_set_size(r, 1, 2);
ei_region_set_offset(r, 3, 4);
ei_region_set_physical_scale(r, 5.6);
ei_region_set_mapping_id(r, "foo");
munit_assert_int(ei_region_get_width(&r), ==, 1);
munit_assert_int(ei_region_get_height(&r), ==, 2);
munit_assert_int(ei_region_get_x(&r), ==, 3);
munit_assert_int(ei_region_get_y(&r), ==, 4);
munit_assert_double(ei_region_get_physical_scale(&r), ==, 5.6);
munit_assert_int(ei_region_get_width(r), ==, 1);
munit_assert_int(ei_region_get_height(r), ==, 2);
munit_assert_int(ei_region_get_x(r), ==, 3);
munit_assert_int(ei_region_get_y(r), ==, 4);
munit_assert_double(ei_region_get_physical_scale(r), ==, 5.6);
munit_assert_string_equal(ei_region_get_mapping_id(r), "foo");
return MUNIT_OK;
}

View file

@ -34,6 +34,7 @@ struct ei_region {
uint32_t x, y;
uint32_t width, height;
double physical_scale;
char *mapping_id;
};
struct ei_region *
@ -47,3 +48,6 @@ 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);
void
ei_region_set_mapping_id(struct ei_region *region, const char *mapping_id);

View file

@ -107,7 +107,7 @@ ei_create_context(bool is_sender, void *user_data)
.ei_callback = VERSION_V(1),
.ei_pingpong = VERSION_V(1),
.ei_seat = VERSION_V(1),
.ei_device = VERSION_V(1),
.ei_device = VERSION_V(2),
.ei_pointer = VERSION_V(1),
.ei_pointer_absolute = VERSION_V(1),
.ei_scroll = VERSION_V(1),

View file

@ -1217,6 +1217,36 @@ ei_region_get_width(struct ei_region *region);
uint32_t
ei_region_get_height(struct ei_region *region);
/**
* @ingroup libei-region
*
* Get the unique identifier (representing an external resource) that is
* attached to this region, if any. This is only available if the EIS
* implementation supports version 2 or later of the ei_device protocol
* interface *and* the EIS implementation chooses to attach such an identifer to
* the region.
*
* This ID can be used by the client to identify an external resource that has a
* relationship with this region.
*
* For example the client may receive a data stream with the video
* data that this region represents. By attaching the same identifier to the data
* stream and this region the EIS implementation can inform the client
* that the video data stream and the region represent paired data.
* Note that in this example use-case, if the stream is resized
* there may be a transition period where two regions have the same identifier -
* the old region and the new region with the updated size. A client must be
* able to handle the case where to mapping ids are identical.
*
* libei does not look at or modify the value of the mapping id. Because the ID is
* assigned by the caller libei makes no guarantee that the ID is unique
* and/or corresponds to any particular format.
*
* @since 1.1
*/
const char *
ei_region_get_mapping_id(struct ei_region *region);
/**
* @ingroup libei-region
*

View file

@ -444,7 +444,7 @@ eis_client_new(struct eis *eis, int fd)
.ei_callback = VERSION_V(1),
.ei_pingpong = VERSION_V(1),
.ei_seat = VERSION_V(1),
.ei_device = VERSION_V(1),
.ei_device = VERSION_V(2),
.ei_pointer = VERSION_V(1),
.ei_pointer_absolute = VERSION_V(1),
.ei_scroll = VERSION_V(1),

View file

@ -819,6 +819,17 @@ eis_device_add(struct eis_device *device)
if (device->type == EIS_DEVICE_TYPE_VIRTUAL) {
struct eis_region *r;
list_for_each(r, &device->regions, link) {
if (r->mapping_id) {
if (client->interface_versions.ei_device >= EIS_DEVICE_EVENT_REGION_MAPPING_ID_SINCE_VERSION) {
rc = eis_device_event_region_mapping_id(device, r->mapping_id);
if (rc < 0)
goto out;
} else {
/* If our client doesn't support mapping_id, drop it */
free(r->mapping_id);
r->mapping_id = NULL;
}
}
rc = eis_device_event_region(device, r->x, r->y, r->width, r->height, r->physical_scale);
if (rc < 0)
goto out;

View file

@ -24,11 +24,13 @@
#include "config.h"
#include "util-strings.h"
#include "libeis-private.h"
static void
eis_region_destroy(struct eis_region *region)
{
free(region->mapping_id);
list_remove(&region->link);
if (!region->added_to_device)
eis_device_unref(region->device);
@ -52,6 +54,8 @@ _public_
OBJECT_IMPLEMENT_GETTER(eis_region, height, uint32_t);
_public_
OBJECT_IMPLEMENT_GETTER(eis_region, physical_scale, double);
_public_
OBJECT_IMPLEMENT_GETTER(eis_region, mapping_id, const char *);
static
OBJECT_IMPLEMENT_CREATE(eis_region);
@ -109,6 +113,22 @@ eis_region_set_physical_scale(struct eis_region *region, double scale)
region->physical_scale = scale;
}
_public_ void
eis_region_set_mapping_id(struct eis_region *region, const char *mapping_id)
{
if (region->added_to_device)
return;
if (mapping_id == NULL) {
struct eis_device *device = region->device;
log_bug_client(eis_device_get_context(device),
"%s: a region's mapping_id must not be NULL", __func__);
return;
}
region->mapping_id = xstrdup(mapping_id);
}
_public_ void
eis_region_add(struct eis_region *region)
{

View file

@ -38,4 +38,5 @@ struct eis_region {
uint32_t x, y;
uint32_t width, height;
double physical_scale;
char *mapping_id;
};

View file

@ -920,6 +920,53 @@ eis_region_set_offset(struct eis_region *region, uint32_t x, uint32_t y);
void
eis_region_set_physical_scale(struct eis_region *region, double scale);
/**
* @ingroup libeis-region
*
* Attach a unique identifier representing an external resource to this region.
* libeis does not look at or modify the value, it is passed through to the
* client if the client supports ei_device interface version 2 or later. Because
* the ID is assigned by the caller libei makes no guarantee that the ID is
* indeed unique and/or corresponds to any particular format.
*
* This ID can be used by the caller to identify an external resource
* that has a relationship with this region. For example, a caller may have
* a data stream with the video data that this region represents.
* By attaching the same identifier to the data stream and this region a client
* (who needs to be aware of this approach) can pair the video data stream
* with the region. Note that in this example use-case, if the stream is resized
* there may be a transition period where two regions have the same identifier -
* the old region and the new region with the updated size. A client must be
* able to handle the case where two mapping_ids are identical.
*
* This function has no effect if called after eis_device_add()
*
* @since 1.1
*/
void
eis_region_set_mapping_id(struct eis_region *region, const char *mapping_id);
/**
* @ingroup libeis-region
*
* Get the unique ID for this region previously set by this
* caller, if any, or NULL if the client does not support region mapping id
* or no region ID has been set.
*
* Region IDs require the client to support the ei_device interface
* version 2 or later. This function can be used to detect support
* for this interface: after eis_region_add() this region's ID is set
* to NULL if the client does not support that interface version.
*
* In other words, if a caller sets a region ID with
* eis_region_set_mapping_id() and that region ID is returned after eis_region_add(),
* the client has been notified of the region ID.
*
* @since 1.1
*/
const char *
eis_region_get_mapping_id(struct eis_region *region);
/**
* @ingroup libeis-region
*

View file

@ -516,6 +516,7 @@ MUNIT_TEST(test_ei_device_regions)
_unref_(eis_region) *r1 = eis_device_new_region(device);
eis_region_set_size(r1, 100, 200);
eis_region_set_offset(r1, 300, 400);
eis_region_set_mapping_id(r1, "oo oo eye dee");
/* no scale, default to 1.0 */
eis_region_add(r1);
@ -551,6 +552,7 @@ MUNIT_TEST(test_ei_device_regions)
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 */);
munit_assert_string_equal(ei_region_get_mapping_id(r), "oo oo eye dee");
r2 = ei_device_get_region_at(device, 300, 400);
munit_assert_ptr_equal(r, r2);
@ -563,6 +565,7 @@ MUNIT_TEST(test_ei_device_regions)
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 */);
munit_assert_null(ei_region_get_mapping_id(r));
r2 = ei_device_get_region_at(device, 750, 850);
munit_assert_ptr_equal(r, r2);
@ -573,6 +576,7 @@ MUNIT_TEST(test_ei_device_regions)
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_null(ei_region_get_mapping_id(r));
r2 = ei_device_get_region_at(device, 1999, 2199);
munit_assert_ptr_equal(r, r2);

View file

@ -260,6 +260,7 @@ add_device(struct eis_demo_server *server, struct eis_client *client,
eis_device_configure_capability(ptr, EIS_DEVICE_CAP_BUTTON);
eis_device_configure_capability(ptr, EIS_DEVICE_CAP_SCROLL);
_unref_(eis_region) *rel_region = eis_device_new_region(ptr);
eis_region_set_mapping_id(rel_region, "demo region");
eis_region_set_size(rel_region, 1920, 1080);
eis_region_set_offset(rel_region, 0, 0);
eis_region_add(rel_region);
@ -280,6 +281,7 @@ add_device(struct eis_demo_server *server, struct eis_client *client,
eis_device_configure_capability(abs, EIS_DEVICE_CAP_BUTTON);
eis_device_configure_capability(abs, EIS_DEVICE_CAP_SCROLL);
_unref_(eis_region) *region = eis_device_new_region(abs);
eis_region_set_mapping_id(region, "demo region");
eis_region_set_size(region, 1920, 1080);
eis_region_set_offset(region, 0, 0);
eis_region_add(region);