Add the ei_touchscreen.cancel event and ei_touch_cancel()

In the protocol it's a new request/event that is sent instead of the
touch up event.

In the library this is implemented as ei_touch_cancel() which
transparently sends cancel() or up depending on the EIS implementation
support. This is mirrored for the EIS implementation.

Where touch cancel is received as an event it is presented
as EI_EVENT_TOUCH_UP with an ei_event_touch_is_cancel() flag to
check if it was a cancel. This is required for backwards compatbility,
we cannot replace the TOUCH_UP event with a TOUCH_CANCEL event without
breaking existing callers. To add a new event type we would need clients
announcing support for those event types but that's an effort that's
better postponed until we have a stronger need for it (see #68).

Closes #60

Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/308>
This commit is contained in:
Peter Hutterer 2024-11-26 13:05:49 +10:00
parent 739ea0f357
commit b1c1c5d579
16 changed files with 561 additions and 2 deletions

View file

@ -1449,6 +1449,10 @@
up. The touchid is the unique id for this touch previously
sent with ei_touchscreen.down.
If a touch is cancelled via ei_touchscreen.cancel, the ei_touchscreen.up
request must not be sent for this same touch. Likewise, a touch released
with ei_touchscreen.up must not be cancelled.
The touchid may be re-used after this request.
It is a protocol violation to send a touch up in the same
@ -1457,6 +1461,30 @@
<arg name="touchid" type="uint32" summary="a unique touch id to identify this touch"/>
</request>
<!-- ei_touchscreen client requests version 2 -->
<request name="cancel" since="2" context-type="sender">
<description summary="touch cancel request">
Notifies the EIS implementation about an existing touch being cancelled.
This typically means that any effects the touch may have had on the
user interface should be reverted or otherwise made inconsequential.
This request replaces ei_touchscreen.up for the same touch.
If a touch is cancelled via ei_touchscreen.cancel, the ei_touchscreen.up
request must not be sent for this same touch. Likewise, a touch released
with ei_touchscreen.up must not be cancelled.
The touchid is the unique id for this touch previously
sent with ei_touchscreen.down.
The touchid may be re-used after this request.
It is a protocol violation to send a touch cancel
in the same frame as a touch motion or touch down.
</description>
<arg name="touchid" type="uint32" summary="a unique touch id to identify this touch"/>
</request>
<!-- ei_touchscreen events version 1 -->
<event name="destroyed" type="destructor" since="1">
@ -1508,10 +1536,33 @@
It is a protocol violation to send this request for a client
of an ei_handshake.context_type other than receiver.
If a touch is released via ei_touchscreen.up, no ei_touchscreen.cancel
event is sent for this same touch. Likewise, a touch released
with ei_touchscreen.cancel must not be released via ei_touchscreen.up.
It is a protocol violation to send a touch up in the same
frame as a touch motion or touch down.
</description>
<arg name="touchid" type="uint32"/>
</event>
<!-- ei_touchscreen events version 2 -->
<event name="cancel" since="2" context-type="receiver">
<description summary="touch cancel event">
See the ei_touchscreen.cancel request for details.
It is a protocol violation to send this event for a client
of an ei_handshake.context_type other than receiver.
If a touch is cancelled via ei_touchscreen.cancel, no ei_touchscreen.up
event is sent for this same touch. Likewise, a touch released
with ei_touchscreen.up must not be cancelled via ei_touchscreen.cancel.
It is a protocol violation to send a touch cancel event in the same
frame as a touch motion or touch down.
</description>
<arg name="touchid" type="uint32"/>
</event>
</interface>
</protocol>

View file

@ -858,6 +858,31 @@ handle_msg_touch_up(struct ei_touchscreen *touchscreen, uint32_t touchid)
return maybe_error_on_device_state(device, "touch up");
}
static struct brei_result *
handle_msg_touch_cancel(struct ei_touchscreen *touchscreen, uint32_t touchid)
{
struct ei_device *device = ei_touchscreen_get_device(touchscreen);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Touch cancel event for non-touch device");
}
struct ei *ei = ei_device_get_context(device);
if (ei->interface_versions.ei_touchscreen < EI_TOUCHSCREEN_EVENT_CANCEL_SINCE_VERSION) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Touch cancel event for touchscreen version v1");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_touch_cancel_event(device, touchid);
return NULL;
}
return maybe_error_on_device_state(device, "touch cancel");
}
static struct brei_result *
handle_msg_touchscreen_destroy(struct ei_touchscreen *touchscreen, uint32_t serial)
{
@ -875,6 +900,7 @@ static const struct ei_touchscreen_interface touchscreen_interface = {
.down = handle_msg_touch_down,
.motion = handle_msg_touch_motion,
.up = handle_msg_touch_up,
.cancel = handle_msg_touch_cancel,
};
const struct ei_touchscreen_interface *
@ -1648,6 +1674,21 @@ ei_send_touch_up(struct ei_device *device, uint32_t tid)
return rc;
}
static int
ei_send_touch_cancel(struct ei_device *device, uint32_t tid)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
device->send_frame_event = true;
int rc = ei_touchscreen_request_cancel(device->touchscreen, tid);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ struct ei_touch *
ei_device_touch_new(struct ei_device *device)
@ -1741,6 +1782,32 @@ ei_touch_up(struct ei_touch *touch)
ei_send_touch_up(touch->device, touch->tracking_id);
}
_public_ void
ei_touch_cancel(struct ei_touch *touch)
{
struct ei_device *device = ei_touch_get_device(touch);
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
if (touch->state != TOUCH_IS_DOWN) {
log_bug_client(ei_device_get_context(device),
"%s: touch %u is not currently down", __func__, touch->tracking_id);
return;
}
touch->state = TOUCH_IS_UP;
struct ei *ei = ei_device_get_context(device);
if (ei->interface_versions.ei_touchscreen >= EI_TOUCHSCREEN_REQUEST_CANCEL_SINCE_VERSION)
ei_send_touch_cancel(touch->device, touch->tracking_id);
else
ei_send_touch_up(touch->device, touch->tracking_id);
}
_public_ void
ei_device_frame(struct ei_device *device, uint64_t time)
{

View file

@ -347,6 +347,14 @@ ei_event_touch_get_y(struct ei_event *event)
return event->touch.y;
}
_public_ bool
ei_event_touch_get_is_cancel(struct ei_event *event)
{
require_event_type(event,false, EI_EVENT_TOUCH_UP);
return event->touch.is_cancel;
}
_public_ uint64_t
ei_event_get_time(struct ei_event *event)
{

View file

@ -64,6 +64,7 @@ struct ei_event {
struct {
uint32_t touchid;
double x, y;
bool is_cancel;
} touch;
struct {
uint32_t sequence;

View file

@ -234,6 +234,9 @@ ei_queue_touch_motion_event(struct ei_device *device, uint32_t touchid,
void
ei_queue_touch_up_event(struct ei_device *device, uint32_t touchid);
void
ei_queue_touch_cancel_event(struct ei_device *device, uint32_t touchid);
_printf_(6, 7) void
ei_log_msg(struct ei *ei,

View file

@ -133,7 +133,7 @@ ei_create_context(bool is_sender, void *user_data)
.ei_scroll = VERSION_V(1),
.ei_button = VERSION_V(1),
.ei_keyboard = VERSION_V(1),
.ei_touchscreen = VERSION_V(1),
.ei_touchscreen = VERSION_V(2),
};
/* This must be v1 until the server tells us otherwise */
ei->handshake = ei_handshake_new(ei, VERSION_V(1));
@ -576,6 +576,19 @@ ei_queue_touch_up_event(struct ei_device *device, uint32_t touchid)
e->type = EI_EVENT_TOUCH_UP;
e->touch.touchid = touchid,
e->touch.is_cancel = false;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_touch_cancel_event(struct ei_device *device, uint32_t touchid)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_TOUCH_UP;
e->touch.touchid = touchid,
e->touch.is_cancel = true;
queue_event(ei_device_get_context(device), e);
}

View file

@ -1735,6 +1735,20 @@ ei_touch_motion(struct ei_touch *touch, double x, double y);
void
ei_touch_up(struct ei_touch *touch);
/**
* @ingroup libei-sender
*
* Cancel this touch. After this call, the touch event becomes inert and
* no longer responds to either ei_touch_down(), ei_touch_motion() or
* ei_touch_up() and the caller should call ei_touch_unref().
*
* This is only available if the EIS implementation supports version 2
* or later of the ei_touchscreen protocol interface. Otherwise,
* this function is equivalent to calling ei_touch_up().
*/
void
ei_touch_cancel(struct ei_touch *touch);
/**
* @ingroup libei-sender
*
@ -2010,6 +2024,19 @@ ei_event_touch_get_x(struct ei_event *event);
double
ei_event_touch_get_y(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* For an event of type @ref EI_EVENT_TOUCH_UP
* return true if the event was cancelled instead of
* logically released.
*
* Support for touch cancellation requires the EIS implementation and client to
* support version 2 or later of the ei_touchscreen protocol interface.
*/
bool
ei_event_touch_get_is_cancel(struct ei_event *event);
/**
* @}
*/

View file

@ -450,7 +450,7 @@ eis_client_new(struct eis *eis, int fd)
.ei_scroll = VERSION_V(1),
.ei_button = VERSION_V(1),
.ei_keyboard = VERSION_V(1),
.ei_touchscreen = VERSION_V(1),
.ei_touchscreen = VERSION_V(2),
};
struct source *s = source_new(fd, client_dispatch, client);
int rc = sink_add_source(eis->sink, s);

View file

@ -673,6 +673,32 @@ client_msg_touch_up(struct eis_touchscreen *touchscreen, uint32_t touchid)
return maybe_error_on_device_state(device, "touch up");
}
static struct brei_result *
client_msg_touch_cancel(struct eis_touchscreen *touchscreen, uint32_t touchid)
{
struct eis_device *device = eis_touchscreen_get_device(touchscreen);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_TOUCH)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Touch cancel event for non-touch device");
}
struct eis_client *client = eis_device_get_client(device);
if (client->interface_versions.ei_touchscreen < EIS_TOUCHSCREEN_EVENT_CANCEL_SINCE_VERSION) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Touch cancel event for touchscreen version v1");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_touch_cancel_event(device, touchid);
return NULL;
}
return maybe_error_on_device_state(device, "touch cancel");
}
static struct brei_result *
client_msg_touchscreen_release(struct eis_touchscreen *touchscreen)
{
@ -688,6 +714,7 @@ static const struct eis_touchscreen_interface touchscreen_interface = {
.down = client_msg_touch_down,
.motion = client_msg_touch_motion,
.up = client_msg_touch_up,
.cancel = client_msg_touch_cancel,
};
const struct eis_touchscreen_interface *
@ -1317,6 +1344,27 @@ eis_touch_up(struct eis_touch *touch)
eis_touchscreen_event_up(device->touchscreen, touch->tracking_id);
}
_public_ void
eis_touch_cancel(struct eis_touch *touch)
{
struct eis_device *device = eis_touch_get_device(touch);
if (touch->state != TOUCH_IS_DOWN) {
log_bug_client(eis_device_get_context(device),
"%s: touch %u is not currently down", __func__, touch->tracking_id);
return;
}
touch->state = TOUCH_IS_UP;
device->send_frame_event = true;
struct eis_client *client = eis_device_get_client(device);
if (client->interface_versions.ei_touchscreen >= EIS_TOUCHSCREEN_EVENT_CANCEL_SINCE_VERSION)
eis_touchscreen_event_cancel(device->touchscreen, touch->tracking_id);
else
eis_touchscreen_event_up(device->touchscreen, touch->tracking_id);
}
_public_ void
eis_device_frame(struct eis_device *device, uint64_t time)
{

View file

@ -405,3 +405,11 @@ eis_event_touch_get_y(struct eis_event *event)
return event->touch.y;
}
_public_ bool
eis_event_touch_get_is_cancel(struct eis_event *event)
{
require_event_type(event, false, EIS_EVENT_TOUCH_UP);
return event->touch.is_cancel;
}

View file

@ -58,6 +58,7 @@ struct eis_event {
struct {
uint32_t touchid;
double x, y;
bool is_cancel;
} touch;
struct {
uint32_t sequence;

View file

@ -146,6 +146,9 @@ eis_queue_touch_motion_event(struct eis_device *device, uint32_t touchid,
void
eis_queue_touch_up_event(struct eis_device *device, uint32_t touchid);
void
eis_queue_touch_cancel_event(struct eis_device *device, uint32_t touchid);
_printf_(6, 7) void
eis_log_msg(struct eis *eis,
enum eis_log_priority priority,

View file

@ -389,10 +389,21 @@ eis_queue_touch_motion_event(struct eis_device *device, uint32_t touchid,
void
eis_queue_touch_up_event(struct eis_device *device, uint32_t touchid)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_TOUCH_UP;
e->touch.touchid = touchid;
e->touch.is_cancel = false;
eis_queue_event(e);
}
void
eis_queue_touch_cancel_event(struct eis_device *device, uint32_t touchid)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_TOUCH_UP;
e->touch.touchid = touchid,
e->touch.is_cancel = true;
eis_queue_event(e);
}

View file

@ -1400,6 +1400,14 @@ eis_touch_motion(struct eis_touch *touch, double x, double y);
void
eis_touch_up(struct eis_touch *touch);
/**
* @ingroup libeis-receiver
*
* see @ref ei_touch_cancel
*/
void
eis_touch_cancel(struct eis_touch *touch);
/**
* @ingroup libeis-receiver
*
@ -1618,6 +1626,19 @@ eis_event_touch_get_x(struct eis_event *event);
double
eis_event_touch_get_y(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* For an event of type @ref EIS_EVENT_TOUCH_UP
* return true if the touch was cancelled instead
* of logically released.
*
* Support for touch cancellation requires the EIS implementation and client to
* support version 2 or later of the ei_touchscreen protocol interface.
*/
bool
eis_event_touch_get_is_cancel(struct eis_event *event);
/**
* @returns a timestamp for the current time to pass into
* eis_device_frame().

View file

@ -1355,6 +1355,95 @@ MUNIT_TEST(test_ei_device_touch)
return MUNIT_OK;
}
MUNIT_TEST(test_ei_device_touch_cancel)
{
_unref_(peck) *peck = peck_new();
struct ei_device *device = NULL;
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_TOUCH);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
peck_dispatch_until_stable(peck);
with_client(peck) {
device = peck_ei_get_default_touch(peck);
/* We know our default device has one region */
_unref_(ei_touch) *t = ei_device_touch_new(device);
ei_touch_down(t, 1, 2);
ei_device_frame(device, peck_ei_now(peck));
ei_touch_motion(t, 200, 500);
ei_device_frame(device, peck_ei_now(peck));
ei_touch_cancel(t);
ei_device_frame(device, peck_ei_now(peck));
}
peck_dispatch_until_stable(peck);
with_server(peck) {
_unref_(eis_event) *down = peck_eis_touch_down(eis, 1, 2);
uint32_t tid = eis_event_touch_get_id(down);
_unref_(eis_event) *motion = peck_eis_touch_motion(eis, 200, 500);
munit_assert_uint32(eis_event_touch_get_id(motion), ==, tid);
_unref_(eis_event) *up = peck_eis_touch_up(eis);
munit_assert_uint32(eis_event_touch_get_id(up), ==, tid);
munit_assert_true(eis_event_touch_get_is_cancel(up));
peck_assert_no_eis_events(eis);
}
peck_dispatch_until_stable(peck);
with_client(peck) {
/* up + cancel, latter is ignored */
_unref_(ei_touch) *t = ei_device_touch_new(device);
ei_touch_down(t, 100, 200);
ei_device_frame(device, peck_ei_now(peck));
ei_touch_motion(t, 300, 400);
ei_device_frame(device, peck_ei_now(peck));
ei_touch_up(t);
with_nonfatal_ei_bug(peck)
ei_touch_cancel(t); /* ignored */
ei_device_frame(device, peck_ei_now(peck));
}
peck_dispatch_until_stable(peck);
with_server(peck) {
_unref_(eis_event) *down = peck_eis_touch_down(eis, 100, 200);
_unref_(eis_event) *motion = peck_eis_touch_motion(eis, 300, 400);
_unref_(eis_event) *up = peck_eis_touch_up(eis);
munit_assert_false(eis_event_touch_get_is_cancel(up));
peck_assert_no_eis_events(eis);
}
with_client(peck) {
/* cancel + up, latter is ignored */
_unref_(ei_touch) *t = ei_device_touch_new(device);
ei_touch_down(t, 100, 200);
ei_device_frame(device, peck_ei_now(peck));
ei_touch_motion(t, 300, 400);
ei_device_frame(device, peck_ei_now(peck));
ei_touch_cancel(t);
with_nonfatal_ei_bug(peck)
ei_touch_up(t); /* ignored */
ei_device_frame(device, peck_ei_now(peck));
}
peck_dispatch_until_stable(peck);
with_server(peck) {
_unref_(eis_event) *down = peck_eis_touch_down(eis, 100, 200);
_unref_(eis_event) *motion = peck_eis_touch_motion(eis, 300, 400);
_unref_(eis_event) *up = peck_eis_touch_up(eis);
munit_assert_true(eis_event_touch_get_is_cancel(up));
peck_assert_no_eis_events(eis);
}
return MUNIT_OK;
}
MUNIT_TEST(test_ei_device_multitouch)
{
_unref_(peck) *peck = peck_new();
@ -2611,6 +2700,114 @@ MUNIT_TEST(test_passive_ei_device_touch)
return MUNIT_OK;
}
/* same as test_ei_device_touch_cancel but for a passive context */
MUNIT_TEST(test_passive_ei_device_touch_cancel)
{
_unref_(peck) *peck = peck_new_context(PECK_EI_RECEIVER);
struct eis_device *device = NULL;
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_TOUCH);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
peck_dispatch_until_stable(peck);
uint32_t sequence = 123;
with_server(peck) {
device = peck_eis_get_default_touch(peck);
eis_device_start_emulating(device, sequence);
}
peck_dispatch_until_stable(peck);
with_client(peck) {
_unref_(ei_event) *start =
peck_ei_next_event(ei, EI_EVENT_DEVICE_START_EMULATING);
munit_assert_uint(ei_event_emulating_get_sequence(start), ==, sequence);
}
with_server(peck) {
device = peck_eis_get_default_touch(peck);
_unref_(eis_touch) *t = eis_device_touch_new(device);
eis_touch_down(t, 1, 2);
eis_device_frame(device, peck_eis_now(peck));
eis_touch_motion(t, 200, 500);
eis_device_frame(device, peck_eis_now(peck));
eis_touch_cancel(t);
eis_device_frame(device, peck_eis_now(peck));
}
peck_dispatch_until_stable(peck);
with_client(peck) {
_unref_(ei_event) *down = peck_ei_touch_down(ei, 1, 2);
uint32_t tid = ei_event_touch_get_id(down);
_unref_(ei_event) *motion = peck_ei_touch_motion(ei, 200, 500);
munit_assert_uint32(ei_event_touch_get_id(motion), ==, tid);
_unref_(ei_event) *up = peck_ei_touch_up(ei);
munit_assert_uint32(ei_event_touch_get_id(up), ==, tid);
munit_assert_true(ei_event_touch_get_is_cancel(up));
peck_assert_no_ei_events(ei);
}
peck_dispatch_until_stable(peck);
with_server(peck) {
/* up + cancel, latter is ignored */
_unref_(eis_touch) *t = eis_device_touch_new(device);
eis_touch_down(t, 100, 200);
eis_device_frame(device, peck_eis_now(peck));
eis_device_frame(device, peck_eis_now(peck));
eis_touch_motion(t, 300, 400);
eis_device_frame(device, peck_eis_now(peck));
eis_touch_up(t);
eis_device_frame(device, peck_eis_now(peck));
with_nonfatal_eis_bug(peck)
eis_touch_cancel(t); /* ignored */
eis_device_frame(device, peck_eis_now(peck));
}
peck_dispatch_until_stable(peck);
with_client(peck) {
_unref_(ei_event) *down = peck_ei_touch_down(ei, 100, 200);
_unref_(ei_event) *motion = peck_ei_touch_motion(ei, 300, 400);
_unref_(ei_event) *up = peck_ei_touch_up(ei);
munit_assert_false(ei_event_touch_get_is_cancel(up));
peck_assert_no_ei_events(ei);
}
with_server(peck) {
/* cancel + up, latter is ignored */
_unref_(eis_touch) *t = eis_device_touch_new(device);
eis_touch_down(t, 100, 200);
eis_device_frame(device, peck_eis_now(peck));
eis_device_frame(device, peck_eis_now(peck));
eis_touch_motion(t, 300, 400);
eis_device_frame(device, peck_eis_now(peck));
eis_touch_cancel(t);
eis_device_frame(device, peck_eis_now(peck));
with_nonfatal_eis_bug(peck)
eis_touch_up(t); /* ignored */
eis_device_frame(device, peck_eis_now(peck));
}
peck_dispatch_until_stable(peck);
with_client(peck) {
_unref_(ei_event) *down = peck_ei_touch_down(ei, 100, 200);
_unref_(ei_event) *motion = peck_ei_touch_motion(ei, 300, 400);
_unref_(ei_event) *up = peck_ei_touch_up(ei);
munit_assert_true(ei_event_touch_get_is_cancel(up));
peck_assert_no_ei_events(ei);
}
return MUNIT_OK;
}
/* same as test_ei_device_multitouch but for a passive context */
MUNIT_TEST(test_passive_ei_device_multitouch)
{

View file

@ -44,8 +44,10 @@ try:
MessageHeader,
EiCallback,
EiConnection,
EiDevice,
EiHandshake,
EiSeat,
EiTouchscreen,
)
except ModuleNotFoundError as e:
# This file needs to be processed by meson, so let's skip when this fails in the source dir
@ -1141,3 +1143,101 @@ class TestEiProtocol:
assert status.pointers[wanted_pointer] is not None
assert len(status.pointers) == 1
@pytest.mark.parametrize("ei_touchscreen_version", (1, 2))
def test_touch_cancel_check_version(self, eis, ei_touchscreen_version):
"""
Ensure EIS disconnects us (or not) if we send a touch cancel event,
depending whether it's supported.
"""
ei = eis.ei
@dataclass
class Status:
device: EiDevice = None
touchscreen: Optional[EiTouchscreen] = None
disconnected: bool = False
resumed: bool = False
serial: int = 0
status = Status()
def on_interface(device, object, name, version, new_objects):
logger.debug(
"new capability",
device=device,
object=object,
name=name,
version=version,
)
if name == InterfaceName.EI_TOUCHSCREEN:
assert status.touchscreen is None
status.touchscreen = new_objects["object"]
def on_device_resumed(device, serial):
status.resumed = True
status.serial = serial
def on_new_device(seat, device, version, new_objects):
logger.debug("new device", object=new_objects["device"])
status.device = new_objects["device"]
status.device.connect("Interface", on_interface)
status.device.connect("Resumed", on_device_resumed)
def on_new_object(o: Interface):
logger.debug("new object", object=o)
if o.name == InterfaceName.EI_SEAT:
ei.seat_fill_capability_masks(o)
o.connect("Device", on_new_device)
ei.context.connect("register", on_new_object)
ei.dispatch()
def on_disconnected(connection, last_serial, reason, explanation):
status.disconnected = True
def on_connection(setup, serial, id, version, new_objects={}):
connection = new_objects["connection"]
connection.connect("Disconnected", on_disconnected)
setup = ei.handshake
setup.connect("Connection", on_connection)
ei.init_default_sender_connection(
interface_versions={"ei_touchscreen": ei_touchscreen_version}
)
assert ei.interface_versions[InterfaceName.EI_TOUCHSCREEN] == VERSION_V(
ei_touchscreen_version
)
ei.wait_for_seat()
seat = ei.seats[0]
ei.send(seat.Bind(seat.bind_mask([InterfaceName.EI_TOUCHSCREEN])))
ei.wait_for(lambda: status.touchscreen and status.resumed)
assert status.touchscreen is not None
ei.send(status.device.StartEmulating(status.serial, 123))
logger.debug("Sending touch events")
touchid = 1
touchscreen = status.touchscreen
device = status.device
ei.send(touchscreen.Down(touchid, 10, 20))
ei.send(device.Frame(status.serial, int(time.time())))
ei.send(touchscreen.Motion(touchid, 10, 25))
ei.send(device.Frame(status.serial, int(time.time())))
ei.send(touchscreen.Cancel(touchid))
try:
ei.send(device.Frame(status.serial, int(time.time())))
except BrokenPipeError:
pass
ei.dispatch()
ei.wait_for(lambda: status.disconnected)
if ei_touchscreen_version == 1:
assert status.disconnected is True
else:
ei.callback_roundtrip()
assert status.disconnected is False