proto: Define new ei_stylus protocol and implementation

Co-authored-by: Jason Gerecke <jason.gerecke@wacom.com>
This commit is contained in:
Joshua Dickens 2025-09-12 13:04:03 -07:00 committed by Peter Hutterer
parent 3f48d11958
commit bea231c4bf
33 changed files with 4176 additions and 9 deletions

View file

@ -6,6 +6,7 @@
Copyright © 2010-2011 Intel Corporation
Copyright © 2012-2013 Collabora, Ltd.
Copyright © 2023 Red Hat, Inc.
Copyright © 2025 Wacom Co., Ltd.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
@ -752,8 +753,9 @@
any relative movement on this region for that movement to match the same
*physical* movement on another region.
It is an EIS implementation bug to advertise the touch and/or absolute pointer capability
on a device_type.virtual device without advertising an ei_region for this device.
It is an EIS implementation bug to advertise the touch, absolute pointer, and/or
stylus capabilities on a device_type.virtual device without advertising an ei_region
for this device.
This event is optional and sent immediately after object creation. Where a device
has multiple regions, this event is sent once for each region.
@ -781,6 +783,7 @@
- "ei_button"
- "ei_keyboard"
- "ei_touchscreen"
- "ei_stylus"
The interface version is equal or less to the client-supported
version in ei_handshake.interface_version for the respective interface.
@ -1584,4 +1587,529 @@
<arg name="touchid" type="uint32"/>
</event>
</interface>
<interface name="ei_stylus" version="1">
<description summary="stylus object">
Interface for stylus requests and events.
A stylus is an absolute pointing tool commonly used for writing, drawing,
and other tasks traditionally accomplished with a pen or pencil. This
interface represents the various semantics and properties common to these
kinds of tools. An ei_device may use this interface alone or in
combination with other interfaces to emulate tools with additional
features. For example, a tool with physical buttons may be emulated by
creating an ei_device that has both this and an ei_button interface.
When adding this interface to an ei_device, the server and client are
required to negotiate tool capabilities. Negotiation is performed by the
server first sending ei_stylus.tool_capabilities prior to ei_device.done
and then the client responding with ei_stylus.bind_tool_capabilities
prior to starting emulation for the first time.
This interface may be used on both ei_device.device_type.virtual and
ei_device.device_type.physical devices. As an interface with absolute
motion, virtual devices must declare an arbitrary set of regions that
are be valid for motion events. For physical devices, the server must
send an ei_device.dimensions that reflects the size of the active area
of the stylus' digitizer. This size **must not** include any
"[out-of-bounds][OOB]" margin that may exist (e.g. a digitizer with a
404 x 229 mm active area and 2mm margin on all sides should have a
declared size of only 400 x 225 mm).
Implementations are free to decide how they handle multiple styli. For
example, a sender that is capable of distinguishing two styli (e.g. by
serial number) may choose to use a unique ei_device object for each
stylus, making it possible for the receiver to keep track of which tool
is in use. Senders are not obligated to do this; it is equally valid for
them to discard any distinguishing information that they may (or may not)
have and use a single ei_device for all styli. Similarly, receivers are
free to choose whether to use or discard information about the source
ei_device after processing.
This interface is only provided once per device and where a client
requests ei_stylus.release the interface does not get re-initialized. An
EIS implementation may adjust the behavior of the device (including
removing the device) if the interface is released.
Note that for a client to receive objects of this type, it must announce
support for the interface in ei_handshake.interface_version.
### Protocol States
This protocol is stateful and relies on ei_device.frame to delimit state
changes. Only those properties, axes, and other values that have changed
from one frame to the next are required to be sent.
This documentation may use various phrases to describe the major
proximity states of the protocol. For clarity, these phrases are defined
below:
- **Out of proximity**: Initial state of an ei_stylus object. This state
indicates that a stylus tool is unable to be sensed by a digitizer.
Its location and other properties are unknown an invalid in this
state. This state can only be transitioned out of by sending a frame
containing ei_stylus.proximity_in.
- **Entering proximity**: State of the ei_stylus object for the duration
of the frame containing ei_stylus.proximity_in. This state indicates
that a stylus has just come near enough to a digitizer for its
location and other properties to become known and valid. This is a
transient state that automatically advances to "in proximity" once the
frame has finished processing.
- **In proximity**: State of the ei_stylus object during normal
operation. This state indicates that the stylus is remaining near
enough to its digitizer for location and other properties to remain
valid. This state is transitioned out of by sending a frame containing
ei_stylus.proximity_out.
- **Leaving proximity**: State of the ei_stylus for the duration of the
frame containing ei_stylus.proximity_out. This state indicates that
the stylus has just moved far enough from its digitizer for its
location and other properties to no longer be known or valid. This is
a transient state that automatically advances to "out of proximity"
once the frame has finished processing. Some stylus hardware/drivers
may provide a final update of the last-known valid location and other
properties as a stylus leaves proximity. This protocol allows such a
final update to be sent in this state.
### Logical Contact
This protocol uses the concept of "logical contact" to represent when a
stylus is making intentional contact with its digitizer. Logical
contact is a heuristic and often flagged by hardware drivers based on
things like the state of a hardware "tip switch", the level of pressure
compared to a threshold, etc. It is important to note that physical
contact does *not* necessarily imply logical contact; some physical
contacts are unintentional and some imperfect tools may indicate
physical contact even while hovering (e.g. by always sending some small
non-zero pressure value).
### Stylus Buttons
This protocol does not define notifications or events for the state of
buttons that exist on it. Instead, it is expected that implementations
ensure that the parent ei_device declares both this and an ei_button
interface. Button requests should be routed through the button interface
and be part of the same ei_device.frame that contains the coincident
stylus requests.
[OOB]: https://wayland.freedesktop.org/libinput/doc/latest/tablet-support.html#out-of-bounds-motion-events
</description>
<!-- ei_stylus client requests version 1 -->
<enum name="capability" since="1">
<description summary="capability type">
This enum denotes capability types for the ei_stylus.
A capability describes a particular type of data that may be reported
by a device or tool. Each capability is a bit flag that may be set in
a mask describing a full set of capabilities.
</description>
<entry name="erase" value="1" summary="the tool may act as an eraser"/>
<entry name="pressure" value="2" summary="pressure data may be reported"/>
<entry name="distance" value="4" summary="distance data may be reported"/>
<entry name="tilt" value="8" summary="tilt data may be reported"/>
<entry name="rotation" value="16" summary="rotation data may be reported"/>
<entry name="airbrush_flow" value="32" summary="airbrush_flow data may be reported"/>
</enum>
<request name="release" since="1">
<description summary="stylus removal request">
Notification that the client is no longer interested in this stylus
object. The EIS implementation will release any resources related to
this object and send the ei_stylus.destroyed event once complete.
</description>
</request>
<request name="bind_tool_capabilities" since="1" context-type="sender">
<description summary="stylus tool capability bind request">
Request to bind to a given set of tool capabilities. This is used
by clients to describe which subset of server-supported capabilities
may be sent by this particular stylus.
Clients are required to send this in response to the
ei_stylus.tool_capabilities event. It is required to be sent prior to
the first ei_device.start_emulating request. It is a protocol violation
to not provide this feedback back to the server. It is a protocol
violation to include capabilities that were not present in the server
event.
It is a protocol violation to send requests or events for unbound
capabilities.
</description>
<arg name="capabilities" type="uint32" summary="A mask of 'capability' flags that the client wishes to handle"/>
</request>
<request name="proximity_in" since="1" context-type="sender">
<description summary="sylus proximity in request">
Notification that a stylus is entering proximity of its digitizer. The
stylus object transitions from an "out of proximity" state to
"entering proximity" for the duration of the ei_device.frame that
contains this request. After the frame has been processed, the
object automatically transitions to an "in proximity" state.
The frame containing this request is required to also contain
ei_stylus.motion.
It is a protocol violation to send this request while the stylus object
is already in or entering proximity. It is a protocol violation to send
ei_stylus.proximity_in and ei_device.proximity_out in the same
frame.
</description>
</request>
<request name="proximity_out" since="1" context-type="sender">
<description summary="stylus proximity out request">
Notification that the the stylus is leaving proximity of its
digitizer. The stylus object transitions from an "in proximity" state
to "leaving proximity" for the duration of the ei_device.frame that
contains this request. After the frame has been processed, the object
automatically transitions to an "out of proximity" state.
Once a stylus is "out of proximity", all other state information
(e.g. tool type, up/down/erase state, axis values) is considered
invalid and must be cleared on both the sending and receiving side.
The frame containing this request may contain updates of the stylus
state (location, tilt, etc.) that represents its final known valid
properties. It should not contain events that attempt to explicitly
reset state.
It is a protocol violation to send this request while the stylus object
is already leaving or out of proximity. It is a protocol violation to
send ei_stylus.proximity_in and ei_device.proximity_out in the same
frame.
</description>
</request>
<request name="erase_start" since="1" context-type="sender">
<description summary="erase start request">
Notification that the stylus's eraser feature has been activated.
Physical styli often include some kind of eraser feature, activated by
flipping the tool over or holding a button down. These button-activated
tools in particular may start or stop erasing without ever leaving
proximity.
The default state for tools is "not erasing".
This request may only be sent while the stylus is entering or in
proximity. It is a protocol violation to send this request at any
other time. It is a protocol violation to for ei_stylus.erase_start and
ei_stylus.erase_stop to share the same ei_device.frame. It is a client
bug to send this request when the ei_stylus.capability.erase capability
is not bound. The EIS implementation may ignore unbound requests and/or
disconnect the client.
</description>
</request>
<request name="erase_stop" since="1" context-type="sender">
<description summary="erase stop request">
Notification that the stylus's eraser feature has been deactivated.
Physical styli often include some kind of eraser feature, activated by
flipping the tool over or holding a button down. These button-activated
tools in particular may start or stop erasing without ever leaving
proximity.
The default state for tools is "not erasing".
This request may only be sent while the stylus is in or leaving
proximity. It is a protocol violation to send this request at any
other time. It is a protocol violation to for ei_stylus.erase_start and
ei_stylus.erase_stop to share the same ei_device.frame. It is a client
bug to send this request when the ei_stylus.capability.erase capability
is not bound. The EIS implementation may ignore unbound requests and/or
disconnect the client.
</description>
</request>
<request name="down" since="1" context-type="sender">
<description summary="stylus down request">
Notification that the stylus has come into logical contact with its
digitizer.
Senders are required to use this request to signal logical contact.
Receivers are free to treat the stylus as "up" in the absence of this
event, regardless of other axis values that may be present (e.g.
pressure, distance).
This request may only be sent while the stylus is entering or in
proximity. It is a protocol violation to send this request at any other
time. It is a protocol violation to for ei_stylus.down and ei_stylus.up
to share the same ei_device.frame.
</description>
</request>
<request name="up" since="1" context-type="sender">
<description summary="stylus up request">
Notification that the stylus has left logical contact with its
digitizer.
Senders are required to generate this request to signal the loss of
logical contact. Receivers are free to treat the stylus as "down" in
the absence of this event, regardless of other axis values that may be
present (e.g. pressure, distance).
This request may only be sent while the stylus is in or leaving
proximity. It is a protocol violation to send this request at any other
time. It is a protocol violation to for ei_stylus.down and ei_stylus.up
to share the same ei_device.frame.
</description>
</request>
<request name="motion" since="1" context-type="sender">
<description summary="stylus motion request">
Notification that the stylus has been moved to the given absolute
coordinates. The interpretation of values depends on if the device is
physical (mm) or virtual (pixels). Fractional pixels are allowed.
Valid `(x, y)` locations are those that exist inside of the
ei_device.dimension (for physical devices) or one of the device's
ei_device.region (for virtual devices). It is a client bug to send a
location that exists outside of these locations. The EIS implementation
may clamp out-of-range values and/or disconnect the client.
This request is required to be in the same frame as
ei_stylus.proximity_in. It may also be sent while the stylus is in or
leaving proximity. It is a protocol violation to send this request at
any other time.
</description>
<arg name="x" type="float" summary="The x position of the stylus in mm or logical pixels."/>
<arg name="y" type="float" summary="The y position of the stylus in mm or logical pixels."/>
</request>
<request name="pressure" since="1" context-type="sender">
<description summary="stylus pressure request">
Notification of the relative amount of pressure exerted on the stylus.
The valid range for pressure is `0.0 &lt;= pressure &lt;= 1.0`. It is a
client bug to send values outside of this range. The EIS implementation
may clamp out-of-range values and/or disconnect the client.
The value `pressure = 0.0` indicates the stylus is experiencing
"minimum" or "no" pressure, and `pressure = 1.0` indicates the stylus
is measuring some arbitrary "maximum pressure".
This request may only be sent while the stylus is entering, in, or
leaving proximity. It is a protocol violation to send this request at
any other time.
> [!IMPORTANT]
> Non-zero pressure does not imply logical contact, but logical
> contact _does_ imply a non-zero pressure. If a client has bound the
> pressure capability, it is a bug to not ensure `pressure &gt; 0.0`
> when sending ei_stylus.down. The EIS implementation may correct
> illegal values and/or disconnect the client.
</description>
<arg name="pressure" type="float" summary="The tip pressure as a normalized value."/>
</request>
<request name="distance" since="1" context-type="sender">
<description summary="stylus distance request">
Notification of the relative distance between the stylus and its
digitizer.
The valid range for distance is `0.0 &lt;= distance &lt;= 1.0`. It is a
client bug to send values outside of this range. The EIS implementation
may clamp out-of-range values and/or disconnect the client.
The value `distance = 0.0` indicates the stylus is in physical contact
with the digitizer and `distance = 1.0` indicates the stylus is at some
arbitrary "maximum distance".
This request may only be sent while the stylus is entering, in, or
leaving proximity. It is a protocol violation to send this request at
any other time.
> [!IMPORTANT]
> Physical contact does not imply logical contact, but logical
> contact _does_ imply physical contact. If a client has bound the
> distance capability, it is a bug to not ensure that `d = 0.0` when
> sending ei_stylus.down. The EIS implementation may correct illegal
> values and/or disconnect the client.
</description>
<arg name="distance" type="float" summary="The tip-to-digitizer distance as a normalized value."/>
</request>
<request name="tilt" since="1" context-type="sender">
<description summary="stylus tilt request">
Notification of the stylus's tilt angles in degrees.
The valid range for each tilt angle is `-90.0 &lt;= tilt_[xy] &lt;=
+90.0`. It is a client bug to send values outside of this range. The
EIS implementation may clamp out-of-range values and/or disconnect
the client.
Each tilt angle is measured relative to the digitizer's Z axis. The pair
`(tilt_x = 0, tilt_y = 0)` indicates the stylus is being held parallel
to the Z axis, while `(tilt_x = 90, tilt_y = 0)` and `(tilt_x = 0,
tilt_y = 90)` indicate the stylus parallel to the +X and +Y axes,
respectively.
This request may only be sent while the stylus is entering, in, or
leaving proximity. It is a protocol violation to send this request at
any other time.
</description>
<arg name="tilt_x" type="float" summary="The angle in degrees of the stylus along the digitizer X axis."/>
<arg name="tilt_y" type="float" summary="The angle in degrees of the stylus along the digitizer Y axis."/>
</request>
<request name="rotation" since="1" context-type="sender">
<description summary="stylus rotation request">
Notification of the stylus's barrel rotation angle in degrees.
The valid range for barrel rotation is `0.0 &lt;= rotation &lt; 360.0`.
It is a client bug to send values outside of this range. The EIS
implementation may reduce out-of-range values modulo 360 and/or
disconnect the client.
The value `rotation = 0.0` indicates the stylus is being held at its
neutral rotation angle. Angles increase in value as the stylus is
rotated clockwise while held.
This request may only be sent while the stylus is entering, in, or
leaving proximity. It is a protocol violation to send this request at
any other time.
</description>
<arg name="rotation" type="float" summary="The angle in degrees of the stylus about its own Z axis."/>
</request>
<request name="airbrush_flow" since="1" context-type="sender">
<description summary="stylus airbrush flow request">
Notification of the relative position of an airbrush stylus's flow
control.
The valid range for airbrush flow is `0.0 &lt;= flow &lt;= 1.0`. It is
a client bug to send values outside of this range. The EIS
implementation may clamp out-of-range values and/or disconnect the
client.
The value `flow = 0.0` indicates the airbrush flow control is fully
"off" and `flow = 1.0` indicates the control is fully "on".
This request may only be sent while the stylus is entering, in, or
leaving proximity. It is a protocol violation to send this request at
any other time.
> [!IMPORTANT]
> Many platforms report the airbrush flow control through a generic
> "tangential pressure" or "slider" property. These generic properties
> may contain other types of data when non-airbrush tools are in use.
> Developers should take care to not report other types of data with
> this request.
</description>
<arg name="flow" type="float" summary="The position of an airbrush flow control present on the tool, as a normalized value."/>
</request>
<!-- ei_stylus events version 1 -->
<event name="destroyed" type="destructor" since="1">
<description summary="stylus removal notification">
This object has been removed and a client should release all associated
resources.
This object will be destroyed by the EIS implementation immediately
after this event is sent and as such the client must not attempt to use
it after that point.
</description>
<arg name="serial" type="uint32" summary="This event's serial number."/>
</event>
<event name="tool_capabilities" since="1">
<description summary="stylus tool capabilities event">
See the ei_stylus.bind_tool_capabilities request for more details.
The server sends this event after attaching the ei_stylus interface
to an ei_device. This event describes the set of capabilities that
the tool supports. For sender contexts, the client must confirm
(and optionally narrow down) the capabilities with the
ei_stylus.bind_tool_capabilities request before the device will
send events.
</description>
<arg name="capabilities" type="uint32" summary="A mask of 'capability' flags for the events available through this interface"/>
</event>
<event name="proximity_in" since="1" context-type="receiver">
<description summary="sylus proximity in event">
See the ei_stylus.proximity_in request for more details.
</description>
</event>
<event name="proximity_out" since="1" context-type="receiver">
<description summary="stylus proximity out event">
See the ei_stylus.proximity_out request for more details.
</description>
</event>
<event name="erase_start" since="1" context-type="receiver">
<description summary="erase start event">
See the ei_stylus.erase_start request for more details.
</description>
</event>
<event name="erase_stop" since="1" context-type="receiver">
<description summary="erase stop event">
See the ei_stylus.erase_stop request for more details.
</description>
</event>
<event name="down" since="1" context-type="receiver">
<description summary="stylus down event">
See the ei_stylus.down request for more details.
</description>
</event>
<event name="up" since="1" context-type="receiver">
<description summary="stylus up event">
See the ei_stylus.up request for more details.
</description>
</event>
<event name="motion" since="1" context-type="receiver">
<description summary="stylus motion event">
See the ei_stylus.motion request for more details.
</description>
<arg name="x" type="float" summary="The x position of the stylus in mm or logical pixels."/>
<arg name="y" type="float" summary="The y position of the stylus in mm or logical pixels."/>
</event>
<event name="pressure" since="1" context-type="receiver">
<description summary="stylus pressure event">
See the ei_stylus.pressure request for more details.
</description>
<arg name="pressure" type="float" summary="The tip pressure as a normalized value."/>
</event>
<event name="distance" since="1" context-type="receiver">
<description summary="stylus distance event">
See the ei_stylus.distance request for more details.
</description>
<arg name="distance" type="float" summary="The tip-to-digitizer distance as a normalized value."/>
</event>
<event name="tilt" since="1" context-type="receiver">
<description summary="stylus tilt event">
See the ei_stylus.tilt request for more details.
</description>
<arg name="tilt_x" type="float" summary="The angle in degrees of the stylus along the digitizer X axis."/>
<arg name="tilt_y" type="float" summary="The angle in degrees of the stylus along the digitizer Y axis."/>
</event>
<event name="rotation" since="1" context-type="receiver">
<description summary="stylus rotation event">
See the ei_stylus.rotation request for more details.
</description>
<arg name="rotation" type="float" summary="The angle in degrees of the stylus about its own Z axis."/>
</event>
<event name="airbrush_flow" since="1" context-type="receiver">
<description summary="stylus slider event">
See the ei_stylus.airbrush_flow request for more details.
</description>
<arg name="flow" type="float" summary="The position of an airbrush flow control present on the tool, as a normalized value."/>
</event>
</interface>
</protocol>

View file

@ -90,6 +90,7 @@ ei_device_destroy(struct ei_device *device)
ei_button_unref(device->button);
ei_touchscreen_unref(device->touchscreen);
ei_keyboard_unref(device->keyboard);
ei_stylus_unref(device->stylus);
ei_seat_unref(seat);
free(device->name);
free(device->pending_region_mapping_id);
@ -255,13 +256,16 @@ handle_msg_done(struct ei_device *device)
mask_add(device->capabilities, EI_DEVICE_CAP_KEYBOARD);
if (device->touchscreen)
mask_add(device->capabilities, EI_DEVICE_CAP_TOUCH);
if (device->stylus)
mask_add(device->capabilities, EI_DEVICE_CAP_STYLUS);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) &&
!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD) &&
!ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH) &&
!ei_device_has_capability(device, EI_DEVICE_CAP_BUTTON) &&
!ei_device_has_capability(device, EI_DEVICE_CAP_SCROLL)) {
!ei_device_has_capability(device, EI_DEVICE_CAP_SCROLL) &&
!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
log_debug(ei, "Rejecting device %#" PRIx64 " '%s' with no known capabilities",
ei_device_get_id(device), ei_device_get_name(device));
ei_device_close(device);
@ -273,7 +277,7 @@ handle_msg_done(struct ei_device *device)
ei_queue_device_added_event(device);
ei_device_done(device);
log_debug(ei,
"Added device %#" PRIx64 " '%s' caps: %s%s%s%s%s%s seat: %s",
"Added device %#" PRIx64 " '%s' caps: %s%s%s%s%s%s%s seat: %s",
ei_device_get_id(device), ei_device_get_name(device),
ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) ? "p" : "",
ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) ? "a" : "",
@ -281,6 +285,7 @@ handle_msg_done(struct ei_device *device)
ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH) ? "t" : "",
ei_device_has_capability(device, EI_DEVICE_CAP_BUTTON) ? "b" : "",
ei_device_has_capability(device, EI_DEVICE_CAP_SCROLL) ? "s" : "",
ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS) ? "y" : "",
ei_seat_get_name(ei_device_get_seat(device)));
return NULL;
}
@ -466,6 +471,13 @@ handle_msg_interface(struct ei_device *device, object_id_t id, const char *name,
"Duplicate ei_touchscreen interface object on device");
device->touchscreen = ei_touchscreen_new(device, id, version);
} else if (streq(name, EI_STYLUS_INTERFACE_NAME)) {
DISCONNECT_IF_INVALID_VERSION(ei, ei_stylus, id, version);
if (device->stylus)
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Duplicate ei_stylus interface object on device");
device->stylus = ei_stylus_new(device, id, version);
} else {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Unsupported interface '%s' on device", name);
@ -913,6 +925,286 @@ ei_device_get_touchscreen_interface(struct ei_device *device)
return &touchscreen_interface;
}
static struct brei_result *
handle_msg_stylus_tool_capabilities(struct ei_stylus *stylus, uint32_t capability)
{
struct ei_device *device = ei_stylus_get_device(stylus);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus tool capabilities event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_NEW) {
ei_queue_stylus_tool_capabilities_event(device, capability);
return NULL;
}
return maybe_error_on_device_state(device, "stylus tool capabilities");
}
static struct brei_result *
handle_msg_stylus_proximity_in(struct ei_stylus *stylus)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus proximity in event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_proximity_in_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus proximity in");
}
static struct brei_result *
handle_msg_stylus_proximity_out(struct ei_stylus *stylus)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus proximity out event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_proximity_out_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus proximity out");
}
static struct brei_result *
handle_msg_stylus_erase_start(struct ei_stylus *stylus)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus erase start event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_erase_start_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus erase start");
}
static struct brei_result *
handle_msg_stylus_erase_stop(struct ei_stylus *stylus)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus erase stop event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_erase_stop_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus erase stop");
}
static struct brei_result *
handle_msg_stylus_down(struct ei_stylus *stylus)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus down event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_down_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus down");
}
static struct brei_result *
handle_msg_stylus_up(struct ei_stylus *stylus)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus up event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_up_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus up");
}
static struct brei_result *
handle_msg_stylus_motion(struct ei_stylus *stylus, float x, float y)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus motion event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_motion_event(device, x, y);
return NULL;
}
return maybe_error_on_device_state(device, "stylus motion");
}
static struct brei_result *
handle_msg_stylus_pressure(struct ei_stylus *stylus, float pressure)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus pressure event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_pressure_event(device, pressure);
return NULL;
}
return maybe_error_on_device_state(device, "stylus pressure");
}
static struct brei_result *
handle_msg_stylus_distance(struct ei_stylus *stylus, float distance)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus distance event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_distance_event(device, distance);
return NULL;
}
return maybe_error_on_device_state(device, "stylus distance");
}
static struct brei_result *
handle_msg_stylus_tilt(struct ei_stylus *stylus, float tilt_x, float tilt_y)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus tilt event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_tilt_event(device, tilt_x, tilt_y);
return NULL;
}
return maybe_error_on_device_state(device, "stylus tilt");
}
static struct brei_result *
handle_msg_stylus_rotation(struct ei_stylus *stylus, float rotation)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus rotation event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_rotation_event(device, rotation);
return NULL;
}
return maybe_error_on_device_state(device, "stylus rotation");
}
static struct brei_result *
handle_msg_stylus_airbrush_flow(struct ei_stylus *stylus, float flow)
{
struct ei_device *device = ei_stylus_get_device(stylus);
DISCONNECT_IF_SENDER_CONTEXT(device);
if (!ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus airbrush_flow event for non-stylus device");
}
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_stylus_airbrush_flow_event(device, flow);
return NULL;
}
return maybe_error_on_device_state(device, "stylus airbrush_flow");
}
static struct brei_result *
handle_msg_stylus_destroy(struct ei_stylus *stylus, uint32_t serial)
{
struct ei *ei = ei_stylus_get_context(stylus);
ei_update_serial(ei, serial);
struct ei_device *device = ei_stylus_get_device(stylus);
ei_stylus_unref(steal(&device->stylus));
return NULL;
}
static const struct ei_stylus_interface stylus_interface = {
.destroyed = handle_msg_stylus_destroy,
.airbrush_flow = handle_msg_stylus_airbrush_flow,
.rotation = handle_msg_stylus_rotation,
.tilt = handle_msg_stylus_tilt,
.distance = handle_msg_stylus_distance,
.pressure = handle_msg_stylus_pressure,
.motion = handle_msg_stylus_motion,
.up = handle_msg_stylus_up,
.down = handle_msg_stylus_down,
.erase_start = handle_msg_stylus_erase_start,
.erase_stop = handle_msg_stylus_erase_stop,
.proximity_out = handle_msg_stylus_proximity_out,
.proximity_in = handle_msg_stylus_proximity_in,
.tool_capabilities = handle_msg_stylus_tool_capabilities,
};
const struct ei_stylus_interface *
ei_device_get_stylus_interface(struct ei_device *device)
{
return &stylus_interface;
}
struct ei_device *
ei_device_new(struct ei_seat *seat, object_id_t deviceid, uint32_t version)
{
@ -1058,6 +1350,8 @@ ei_device_send_release(struct ei_device *device)
ei_scroll_request_release(device->scroll);
if (device->button)
ei_button_request_release(device->button);
if (device->stylus)
ei_stylus_request_release(device->stylus);
int rc = ei_device_request_release(device);
if (rc)
@ -1118,6 +1412,7 @@ ei_device_removed_by_server(struct ei_device *device)
ei_touchscreen_unref(steal(&device->touchscreen));
ei_scroll_unref(steal(&device->scroll));
ei_button_unref(steal(&device->button));
ei_stylus_unref(steal(&device->stylus));
ei_unregister_object(ei, &device->proto_object);
ei_queue_device_removed_event(device);
@ -1194,6 +1489,7 @@ ei_device_has_capability(struct ei_device *device,
case EI_DEVICE_CAP_TOUCH:
case EI_DEVICE_CAP_BUTTON:
case EI_DEVICE_CAP_SCROLL:
case EI_DEVICE_CAP_STYLUS:
return mask_all(device->capabilities, cap);
}
return false;
@ -1812,6 +2108,376 @@ ei_touch_cancel(struct ei_touch *touch)
ei_send_touch_up(touch->device, touch->tracking_id);
}
static int
ei_send_stylus_bind_tool_capabilities(struct ei_device *device, uint32_t capabilities)
{
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 = false;
int rc = ei_stylus_request_bind_tool_capabilities(device->stylus, capabilities);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_bind_tool_capabilities(struct ei_device *device, uint32_t capabilities)
{
// TODO: If/when libei-device.c gets something equivalent to the
// EIS_DEVICE_STATE_AWAITING_READY state, this should be updated
// to match it.
if (device->state != EI_DEVICE_STATE_PAUSED) {
log_bug_client(ei_device_get_context(device),
"%s: device is not paused", __func__);
return;
}
ei_send_stylus_bind_tool_capabilities(device, capabilities);
}
static int
ei_send_stylus_motion(struct ei_device *device, float x, float y)
{
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_stylus_request_motion(device->stylus, x, y);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_motion(struct ei_device *device, float x, float y)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
if (!ei_device_in_region(device, x, y))
return;
ei_send_stylus_motion(device, x, y);
}
static int
ei_send_stylus_pressure(struct ei_device *device, float pressure)
{
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_stylus_request_pressure(device->stylus, pressure);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_pressure(struct ei_device *device, float pressure)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
ei_send_stylus_pressure(device, pressure);
}
static int
ei_send_stylus_distance(struct ei_device *device, float distance)
{
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_stylus_request_distance(device->stylus, distance);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_distance(struct ei_device *device, float distance)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
ei_send_stylus_distance(device, distance);
}
static int
ei_send_stylus_tilt(struct ei_device *device, float tilt_x, float tilt_y)
{
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_stylus_request_tilt(device->stylus, tilt_x, tilt_y);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_tilt(struct ei_device *device, float tilt_x, float tilt_y)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
ei_send_stylus_tilt(device, tilt_x, tilt_y);
}
static int
ei_send_stylus_rotation(struct ei_device *device, float rotation)
{
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_stylus_request_rotation(device->stylus, rotation);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_rotation(struct ei_device *device, float rotation)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
ei_send_stylus_rotation(device, rotation);
}
static int
ei_send_stylus_airbrush_flow(struct ei_device *device, float flow)
{
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_stylus_request_airbrush_flow(device->stylus, flow);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_airbrush_flow(struct ei_device *device, float flow)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
ei_send_stylus_airbrush_flow(device, flow);
}
static int
ei_send_stylus_erase_start(struct ei_device *device)
{
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_stylus_request_erase_start(device->stylus);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_erase_start(struct ei_device *device)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
ei_send_stylus_erase_start(device);
}
static int
ei_send_stylus_erase_stop(struct ei_device *device)
{
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_stylus_request_erase_stop(device->stylus);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_erase_stop(struct ei_device *device)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
ei_send_stylus_erase_stop(device);
}
static int
ei_send_stylus_up(struct ei_device *device)
{
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_stylus_request_up(device->stylus);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_up(struct ei_device *device)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
ei_send_stylus_up(device);
}
static int
ei_send_stylus_down(struct ei_device *device)
{
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_stylus_request_down(device->stylus);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_down(struct ei_device *device)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
ei_send_stylus_down(device);
}
static int
ei_send_stylus_proximity_out(struct ei_device *device)
{
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_stylus_request_proximity_out(device->stylus);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_proximity_out(struct ei_device *device)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
ei_send_stylus_proximity_out(device);
}
static int
ei_send_stylus_proximity_in(struct ei_device *device)
{
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_stylus_request_proximity_in(device->stylus);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ void
ei_device_stylus_proximity_in(struct ei_device *device)
{
if (device->state != EI_DEVICE_STATE_EMULATING) {
log_bug_client(ei_device_get_context(device),
"%s: device is not emulating", __func__);
return;
}
ei_send_stylus_proximity_in(device);
}
_public_ void
ei_device_frame(struct ei_device *device, uint64_t time)
{

View file

@ -32,6 +32,7 @@
#include "libei-pointer.h"
#include "libei-keyboard.h"
#include "libei-touchscreen.h"
#include "libei-stylus.h"
enum ei_device_state {
/* Before the DeviceAddedDone was received */
@ -65,6 +66,7 @@ struct ei_device {
struct ei_button *button;
struct ei_keyboard *keyboard;
struct ei_touchscreen *touchscreen;
struct ei_stylus *stylus;
struct list link;
enum ei_device_state state;
@ -123,6 +125,7 @@ OBJECT_DECLARE_GETTER(ei_device, scroll_interface, const struct ei_scroll_interf
OBJECT_DECLARE_GETTER(ei_device, button_interface, const struct ei_button_interface *);
OBJECT_DECLARE_GETTER(ei_device, keyboard_interface, const struct ei_keyboard_interface *);
OBJECT_DECLARE_GETTER(ei_device, touchscreen_interface, const struct ei_touchscreen_interface *);
OBJECT_DECLARE_GETTER(ei_device, stylus_interface, const struct ei_stylus_interface *);
OBJECT_DECLARE_SETTER(ei_device, type, enum ei_device_type);
OBJECT_DECLARE_SETTER(ei_device, name, const char*);
OBJECT_DECLARE_SETTER(ei_device, seat, const char*);

View file

@ -65,6 +65,19 @@ ei_event_type_to_string(enum ei_event_type type)
CASE_RETURN_STRING(EI_EVENT_TOUCH_DOWN);
CASE_RETURN_STRING(EI_EVENT_TOUCH_UP);
CASE_RETURN_STRING(EI_EVENT_TOUCH_MOTION);
CASE_RETURN_STRING(EI_EVENT_STYLUS_PROXIMITY_IN);
CASE_RETURN_STRING(EI_EVENT_STYLUS_PROXIMITY_OUT);
CASE_RETURN_STRING(EI_EVENT_STYLUS_DOWN);
CASE_RETURN_STRING(EI_EVENT_STYLUS_UP);
CASE_RETURN_STRING(EI_EVENT_STYLUS_MOTION);
CASE_RETURN_STRING(EI_EVENT_STYLUS_PRESSURE);
CASE_RETURN_STRING(EI_EVENT_STYLUS_DISTANCE);
CASE_RETURN_STRING(EI_EVENT_STYLUS_TILT);
CASE_RETURN_STRING(EI_EVENT_STYLUS_ROTATION);
CASE_RETURN_STRING(EI_EVENT_STYLUS_AIRBRUSH_FLOW);
CASE_RETURN_STRING(EI_EVENT_STYLUS_TOOL_CAPABILITIES);
CASE_RETURN_STRING(EI_EVENT_STYLUS_ERASE_START);
CASE_RETURN_STRING(EI_EVENT_STYLUS_ERASE_STOP);
}
return NULL;
@ -98,6 +111,19 @@ ei_event_destroy(struct ei_event *event)
case EI_EVENT_TOUCH_DOWN:
case EI_EVENT_TOUCH_UP:
case EI_EVENT_TOUCH_MOTION:
case EI_EVENT_STYLUS_PROXIMITY_IN:
case EI_EVENT_STYLUS_PROXIMITY_OUT:
case EI_EVENT_STYLUS_DOWN:
case EI_EVENT_STYLUS_UP:
case EI_EVENT_STYLUS_MOTION:
case EI_EVENT_STYLUS_PRESSURE:
case EI_EVENT_STYLUS_DISTANCE:
case EI_EVENT_STYLUS_TILT:
case EI_EVENT_STYLUS_ROTATION:
case EI_EVENT_STYLUS_AIRBRUSH_FLOW:
case EI_EVENT_STYLUS_TOOL_CAPABILITIES:
case EI_EVENT_STYLUS_ERASE_START:
case EI_EVENT_STYLUS_ERASE_STOP:
break;
case EI_EVENT_DEVICE_ADDED:
if (ei->interface_versions.ei_device >= EI_DEVICE_REQUEST_READY_SINCE_VERSION)
@ -377,6 +403,117 @@ ei_event_touch_get_is_cancel(struct ei_event *event)
return event->touch.is_cancel;
}
_public_ uint32_t
ei_event_stylus_get_capability(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_TOOL_CAPABILITIES);
return event->stylus.capability;
}
_public_ bool
ei_event_stylus_get_is_prox(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_PROXIMITY_IN,
EI_EVENT_STYLUS_PROXIMITY_OUT);
return event->stylus.is_prox;
}
_public_ bool
ei_event_stylus_get_is_down(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_DOWN,
EI_EVENT_STYLUS_UP);
return event->stylus.is_down;
}
_public_ bool
ei_event_stylus_get_is_erase(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_ERASE_START,
EI_EVENT_STYLUS_ERASE_STOP);
return event->stylus.is_erase;
}
_public_ float
ei_event_stylus_get_x(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_MOTION);
return event->stylus.x;
}
_public_ float
ei_event_stylus_get_y(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_MOTION);
return event->stylus.y;
}
_public_ float
ei_event_stylus_get_pressure(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_PRESSURE);
return event->stylus.pressure;
}
_public_ float
ei_event_stylus_get_distance(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_DISTANCE);
return event->stylus.distance;
}
_public_ float
ei_event_stylus_get_tilt_x(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_TILT);
return event->stylus.tilt_x;
}
_public_ float
ei_event_stylus_get_tilt_y(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_TILT);
return event->stylus.tilt_y;
}
_public_ float
ei_event_stylus_get_rotation(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_ROTATION);
return event->stylus.rotation;
}
_public_ float
ei_event_stylus_get_flow(struct ei_event *event)
{
require_event_type(event, 0.0,
EI_EVENT_STYLUS_AIRBRUSH_FLOW);
return event->stylus.flow;
}
_public_ uint64_t
ei_event_get_time(struct ei_event *event)
{
@ -392,6 +529,19 @@ ei_event_get_time(struct ei_event *event)
EI_EVENT_TOUCH_DOWN,
EI_EVENT_TOUCH_UP,
EI_EVENT_TOUCH_MOTION,
EI_EVENT_STYLUS_PROXIMITY_IN,
EI_EVENT_STYLUS_PROXIMITY_OUT,
EI_EVENT_STYLUS_DOWN,
EI_EVENT_STYLUS_UP,
EI_EVENT_STYLUS_MOTION,
EI_EVENT_STYLUS_PRESSURE,
EI_EVENT_STYLUS_DISTANCE,
EI_EVENT_STYLUS_TILT,
EI_EVENT_STYLUS_ROTATION,
EI_EVENT_STYLUS_AIRBRUSH_FLOW,
EI_EVENT_STYLUS_ERASE_STOP,
EI_EVENT_STYLUS_ERASE_START,
EI_EVENT_STYLUS_TOOL_CAPABILITIES,
EI_EVENT_FRAME);
return event->timestamp;

View file

@ -75,6 +75,12 @@ struct ei_event {
struct {
struct ei_pingpong *pingpong;
} sync;
struct {
uint32_t capability;
bool is_prox, is_down, is_erase;
float x, y, pressure, distance, rotation, flow;
float tilt_x, tilt_y;
} stylus;
};
};

View file

@ -98,6 +98,7 @@ ei_handshake_initialize(struct ei_handshake *setup, uint32_t version)
ei_handshake_request_interface_version(setup, EI_BUTTON_INTERFACE_NAME, v->ei_button);
ei_handshake_request_interface_version(setup, EI_KEYBOARD_INTERFACE_NAME, v->ei_keyboard);
ei_handshake_request_interface_version(setup, EI_TOUCHSCREEN_INTERFACE_NAME, v->ei_touchscreen);
ei_handshake_request_interface_version(setup, EI_STYLUS_INTERFACE_NAME, v->ei_stylus);
}
ei_handshake_request_finish(setup);

View file

@ -49,6 +49,7 @@
#include "libei-region.h"
#include "libei-scroll.h"
#include "libei-seat.h"
#include "libei-stylus.h"
#include "libei-touchscreen.h"
struct ei_backend_interface {
@ -77,6 +78,7 @@ struct ei_interface_versions {
uint32_t ei_button;
uint32_t ei_keyboard;
uint32_t ei_touchscreen;
uint32_t ei_stylus;
};
struct ei_unsent {
@ -246,6 +248,45 @@ ei_queue_touch_cancel_event(struct ei_device *device, uint32_t touchid);
void
ei_sync_event_send_done(struct ei_event *e);
void
ei_queue_stylus_tool_capabilities_event(struct ei_device *device, uint32_t capability);
void
ei_queue_stylus_proximity_in_event(struct ei_device *device);
void
ei_queue_stylus_proximity_out_event(struct ei_device *device);
void
ei_queue_stylus_down_event(struct ei_device *device);
void
ei_queue_stylus_up_event(struct ei_device *device);
void
ei_queue_stylus_motion_event(struct ei_device *device, float x, float y);
void
ei_queue_stylus_pressure_event(struct ei_device *device, float p);
void
ei_queue_stylus_distance_event(struct ei_device *device, float d);
void
ei_queue_stylus_tilt_event(struct ei_device *device, float tilt_x, float tilt_y);
void
ei_queue_stylus_rotation_event(struct ei_device *device, float r);
void
ei_queue_stylus_airbrush_flow_event(struct ei_device *device, float s);
void
ei_queue_stylus_erase_start_event(struct ei_device *device);
void
ei_queue_stylus_erase_stop_event(struct ei_device *device);
_printf_(6, 7) void
ei_log_msg(struct ei *ei,
enum ei_log_priority priority,

View file

@ -247,6 +247,8 @@ ei_seat_has_capability(struct ei_seat *seat,
return seat->capabilities.map[EI_SCROLL_INTERFACE_INDEX] != 0;
case EI_DEVICE_CAP_BUTTON:
return seat->capabilities.map[EI_BUTTON_INTERFACE_INDEX] != 0;
case EI_DEVICE_CAP_STYLUS:
return seat->capabilities.map[EI_STYLUS_INTERFACE_INDEX] != 0;
}
return false;
}
@ -281,6 +283,8 @@ ei_seat_cap_mask(struct ei_seat *seat, enum ei_device_capability cap)
return seat->capabilities.map[EI_BUTTON_INTERFACE_INDEX];
case EI_DEVICE_CAP_SCROLL:
return seat->capabilities.map[EI_SCROLL_INTERFACE_INDEX];
case EI_DEVICE_CAP_STYLUS:
return seat->capabilities.map[EI_STYLUS_INTERFACE_INDEX];
}
return 0;

87
src/libei-stylus.c Normal file
View file

@ -0,0 +1,87 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2023 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <errno.h>
#include <stdbool.h>
#include "util-bits.h"
#include "util-macros.h"
#include "util-mem.h"
#include "util-io.h"
#include "util-strings.h"
#include "util-version.h"
#include "libei-private.h"
#include "ei-proto.h"
static void
ei_stylus_destroy(struct ei_stylus *stylus)
{
struct ei *ei = ei_stylus_get_context(stylus);
ei_unregister_object(ei, &stylus->proto_object);
}
OBJECT_IMPLEMENT_REF(ei_stylus);
OBJECT_IMPLEMENT_UNREF_CLEANUP(ei_stylus);
static
OBJECT_IMPLEMENT_CREATE(ei_stylus);
static
OBJECT_IMPLEMENT_PARENT(ei_stylus, ei_device);
OBJECT_IMPLEMENT_GETTER_AS_REF(ei_stylus, proto_object, const struct brei_object*);
struct ei_device *
ei_stylus_get_device(struct ei_stylus *stylus)
{
return ei_stylus_parent(stylus);
}
struct ei*
ei_stylus_get_context(struct ei_stylus *stylus)
{
return ei_device_get_context(ei_stylus_get_device(stylus));
}
const struct ei_stylus_interface *
ei_stylus_get_interface(struct ei_stylus *stylus) {
struct ei_device *device = ei_stylus_get_device(stylus);
return ei_device_get_stylus_interface(device);
}
struct ei_stylus *
ei_stylus_new(struct ei_device *device, object_id_t id, uint32_t version)
{
struct ei_stylus *stylus = ei_stylus_create(&device->object);
struct ei *ei = ei_device_get_context(device);
stylus->proto_object.id = id;
stylus->proto_object.implementation = stylus;
stylus->proto_object.interface = &ei_stylus_proto_interface;
stylus->proto_object.version = version;
ei_register_object(ei, &stylus->proto_object);
return stylus; /* ref owned by caller */
}

49
src/libei-stylus.h Normal file
View file

@ -0,0 +1,49 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2023 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include "util-object.h"
#include "util-list.h"
#include "brei-shared.h"
struct ei;
struct ei_device;
struct ei_stylus;
/* This is a protocol-only object, not exposed in the API */
struct ei_stylus {
struct object object;
struct brei_object proto_object;
};
OBJECT_DECLARE_GETTER(ei_stylus, context, struct ei*);
OBJECT_DECLARE_GETTER(ei_stylus, device, struct ei_device*);
OBJECT_DECLARE_GETTER(ei_stylus, proto_object, const struct brei_object*);
OBJECT_DECLARE_GETTER(ei_stylus, interface, const struct ei_stylus_interface *);
OBJECT_DECLARE_REF(ei_stylus);
OBJECT_DECLARE_UNREF(ei_stylus);
struct ei_stylus *
ei_stylus_new(struct ei_device *device, object_id_t id, uint32_t version);

View file

@ -134,6 +134,7 @@ ei_create_context(bool is_sender, void *user_data)
.ei_button = VERSION_V(1),
.ei_keyboard = VERSION_V(1),
.ei_touchscreen = VERSION_V(2),
.ei_stylus = VERSION_V(1),
};
/* This must be v1 until the server tells us otherwise */
ei->handshake = ei_handshake_new(ei, VERSION_V(1));
@ -245,6 +246,19 @@ update_event_timestamp(struct ei_event *event, uint64_t time)
case EI_EVENT_TOUCH_DOWN:
case EI_EVENT_TOUCH_UP:
case EI_EVENT_TOUCH_MOTION:
case EI_EVENT_STYLUS_PROXIMITY_IN:
case EI_EVENT_STYLUS_PROXIMITY_OUT:
case EI_EVENT_STYLUS_DOWN:
case EI_EVENT_STYLUS_UP:
case EI_EVENT_STYLUS_MOTION:
case EI_EVENT_STYLUS_PRESSURE:
case EI_EVENT_STYLUS_DISTANCE:
case EI_EVENT_STYLUS_TILT:
case EI_EVENT_STYLUS_ROTATION:
case EI_EVENT_STYLUS_AIRBRUSH_FLOW:
case EI_EVENT_STYLUS_TOOL_CAPABILITIES:
case EI_EVENT_STYLUS_ERASE_START:
case EI_EVENT_STYLUS_ERASE_STOP:
if (event->timestamp != 0) {
log_bug(ei_event_get_context(event),
"Unexpected timestamp for event of type: %s",
@ -280,6 +294,19 @@ queue_event(struct ei *ei, struct ei_event *event)
case EI_EVENT_TOUCH_DOWN:
case EI_EVENT_TOUCH_UP:
case EI_EVENT_TOUCH_MOTION:
case EI_EVENT_STYLUS_PROXIMITY_IN:
case EI_EVENT_STYLUS_PROXIMITY_OUT:
case EI_EVENT_STYLUS_DOWN:
case EI_EVENT_STYLUS_UP:
case EI_EVENT_STYLUS_MOTION:
case EI_EVENT_STYLUS_PRESSURE:
case EI_EVENT_STYLUS_DISTANCE:
case EI_EVENT_STYLUS_TILT:
case EI_EVENT_STYLUS_ROTATION:
case EI_EVENT_STYLUS_AIRBRUSH_FLOW:
case EI_EVENT_STYLUS_TOOL_CAPABILITIES:
case EI_EVENT_STYLUS_ERASE_START:
case EI_EVENT_STYLUS_ERASE_STOP:
prefix = "pending ";
queue = &device->pending_event_queue;
break;
@ -626,6 +653,151 @@ ei_queue_touch_cancel_event(struct ei_device *device, uint32_t touchid)
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_tool_capabilities_event(struct ei_device *device, uint32_t capability)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_TOOL_CAPABILITIES;
e->stylus.capability = capability;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_proximity_in_event(struct ei_device *device)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_PROXIMITY_IN;
e->stylus.is_prox = true;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_proximity_out_event(struct ei_device *device)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_PROXIMITY_OUT;
e->stylus.is_prox = false;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_erase_start_event(struct ei_device *device)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_ERASE_START;
e->stylus.is_erase = true;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_erase_stop_event(struct ei_device *device)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_ERASE_STOP;
e->stylus.is_erase = false;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_down_event(struct ei_device *device)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_DOWN;
e->stylus.is_down = true;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_up_event(struct ei_device *device)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_UP;
e->stylus.is_down = false;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_motion_event(struct ei_device *device, float x, float y)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_MOTION;
e->stylus.x = x;
e->stylus.y = y;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_pressure_event(struct ei_device *device, float pressure)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_PRESSURE;
e->stylus.pressure = pressure;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_distance_event(struct ei_device *device, float distance)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_DISTANCE;
e->stylus.distance = distance;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_tilt_event(struct ei_device *device, float tilt_x, float tilt_y)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_TILT;
e->stylus.tilt_x = tilt_x;
e->stylus.tilt_y = tilt_y;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_rotation_event(struct ei_device *device, float rotation)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_ROTATION;
e->stylus.rotation = rotation;
queue_event(ei_device_get_context(device), e);
}
void
ei_queue_stylus_airbrush_flow_event(struct ei_device *device, float flow)
{
struct ei_event *e = ei_event_new_for_device(device);
e->type = EI_EVENT_STYLUS_AIRBRUSH_FLOW;
e->stylus.flow = flow;
queue_event(ei_device_get_context(device), e);
}
_public_ void
ei_disconnect(struct ei *ei)
{

View file

@ -215,6 +215,28 @@ struct ei_region;
*/
struct ei_touch;
/**
* @struct ei_stylus
*
* A single stylus initiated by a sender context.
*
* @see ei_device_stylus_new
* @see ei_stylus_bind_tool_capabilities
* @see ei_stylus_proximity_in
* @see ei_stylus_proximity_out
* @see ei_stylus_erase_start
* @see ei_stylus_erase_stop
* @see ei_stylus_down
* @see ei_stylus_up
* @see ei_stylus_motion
* @see ei_stylus_pressure
* @see ei_stylus_distance
* @see ei_stylus_tilt
* @see ei_stylus_rotation
* @see ei_stylus_airbrush_flow
*/
struct ei_stylus;
/**
* @struct ei_ping
*
@ -300,6 +322,10 @@ enum ei_device_capability {
* The device can send button events
*/
EI_DEVICE_CAP_BUTTON = (1 << 5),
/**
* The device can send stylus events
*/
EI_DEVICE_CAP_STYLUS = (1 << 7),
};
/**
@ -591,6 +617,59 @@ enum ei_event_type {
* See ei_device_touch_new() and ei_touch_motion() for the sender context API.
*/
EI_EVENT_TOUCH_MOTION,
/**
* TODO
*/
EI_EVENT_STYLUS_TOOL_CAPABILITIES = 1000,
/**
* TODO
*/
EI_EVENT_STYLUS_PROXIMITY_IN,
/**
* TODO
*/
EI_EVENT_STYLUS_PROXIMITY_OUT,
/**
* TODO
*/
EI_EVENT_STYLUS_ERASE_START,
/**
* TODO
*/
EI_EVENT_STYLUS_ERASE_STOP,
/**
* TODO
*/
EI_EVENT_STYLUS_DOWN,
/**
* TODO
*/
EI_EVENT_STYLUS_UP,
/**
* TODO
*/
EI_EVENT_STYLUS_MOTION,
/**
* TODO
*/
EI_EVENT_STYLUS_PRESSURE,
/**
* TODO
*/
EI_EVENT_STYLUS_DISTANCE,
/**
* TODO
*/
EI_EVENT_STYLUS_TILT,
/**
* TODO
*/
EI_EVENT_STYLUS_ROTATION,
/**
* TODO
*/
EI_EVENT_STYLUS_AIRBRUSH_FLOW,
};
/**
@ -1957,6 +2036,118 @@ ei_touch_get_user_data(struct ei_touch *touch);
struct ei_device *
ei_touch_get_device(struct ei_touch *touch);
/**
* @ingroup libei-sender
*
* TODO
*/
struct ei_stylus *
ei_device_stylus_new(struct ei_device *device);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_bind_tool_capabilities(struct ei_device *device, uint32_t capabilitiy);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_proximity_in(struct ei_device *device);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_proximity_out(struct ei_device *device);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_erase_start(struct ei_device *device);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_erase_stop(struct ei_device *device);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_down(struct ei_device *device);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_up(struct ei_device *device);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_motion(struct ei_device *device, float x, float y);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_pressure(struct ei_device *device, float p);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_distance(struct ei_device *device, float d);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_tilt(struct ei_device *device, float tilt_x, float tilt_y);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_rotation(struct ei_device *device, float r);
/**
* @ingroup libei-sender
*
* TODO
*/
void
ei_device_stylus_airbrush_flow(struct ei_device *device, float s);
/**
* Return the seat from this event.
*
@ -2220,6 +2411,110 @@ ei_event_touch_get_y(struct ei_event *event);
bool
ei_event_touch_get_is_cancel(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
bool
ei_event_stylus_get_is_prox(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
bool
ei_event_stylus_get_is_erase(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
uint32_t
ei_event_stylus_get_capability(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
uint32_t
ei_event_stylus_get_code(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
bool
ei_event_stylus_get_is_down(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
float
ei_event_stylus_get_x(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
float
ei_event_stylus_get_y(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
float
ei_event_stylus_get_pressure(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
float
ei_event_stylus_get_distance(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
float
ei_event_stylus_get_tilt_x(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
float
ei_event_stylus_get_tilt_y(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
float
ei_event_stylus_get_rotation(struct ei_event *event);
/**
* @ingroup libei-receiver
*
* TODO
*/
float
ei_event_stylus_get_flow(struct ei_event *event);
/**
* @}
*/

View file

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

View file

@ -52,6 +52,7 @@ struct eis_client_interface_versions {
uint32_t ei_button;
uint32_t ei_keyboard;
uint32_t ei_touchscreen;
uint32_t ei_stylus;
};
struct eis_client {

View file

@ -149,6 +149,7 @@ eis_device_destroy(struct eis_device *device)
eis_pointer_unref(device->pointer);
eis_touchscreen_unref(device->touchscreen);
eis_keyboard_unref(device->keyboard);
eis_stylus_unref(device->stylus);
free(device->name);
}
@ -739,6 +740,299 @@ eis_device_get_touchscreen_interface(struct eis_device *device)
return &touchscreen_interface;
}
static struct brei_result *
client_msg_stylus_bind_tool_capabilities(struct eis_stylus *stylus, uint32_t capability)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus tool capabilities event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_AWAITING_READY) {
eis_queue_stylus_bind_tool_capabilities_event(device, capability);
return NULL;
}
return maybe_error_on_device_state(device, "stylus tool capabilities");
}
static struct brei_result *
client_msg_stylus_proximity_in(struct eis_stylus *stylus)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus proximity in event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_proximity_in_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus proximity in");
}
static struct brei_result *
client_msg_stylus_proximity_out(struct eis_stylus *stylus)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus proximity out event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_proximity_out_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus proximity out");
}
static struct brei_result *
client_msg_stylus_erase_start(struct eis_stylus *stylus)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus erase start event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_erase_start_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus erase start");
}
static struct brei_result *
client_msg_stylus_erase_stop(struct eis_stylus *stylus)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus erase stop event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_erase_stop_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus erase stop");
}
static struct brei_result *
client_msg_stylus_down(struct eis_stylus *stylus)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus down event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_down_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus down");
}
static struct brei_result *
client_msg_stylus_up(struct eis_stylus *stylus)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus up event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_up_event(device);
return NULL;
}
return maybe_error_on_device_state(device, "stylus up");
}
static struct brei_result *
client_msg_stylus_motion(struct eis_stylus *stylus, float x, float y)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus motion event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_motion_event(device, x, y);
return NULL;
}
return maybe_error_on_device_state(device, "stylus motion");
}
static struct brei_result *
client_msg_stylus_pressure(struct eis_stylus *stylus, float pressure)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus pressure event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_pressure_event(device, pressure);
return NULL;
}
return maybe_error_on_device_state(device, "stylus pressure");
}
static struct brei_result *
client_msg_stylus_distance(struct eis_stylus *stylus, float distance)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus distance event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_distance_event(device, distance);
return NULL;
}
return maybe_error_on_device_state(device, "stylus distance");
}
static struct brei_result *
client_msg_stylus_tilt(struct eis_stylus *stylus, float tilt_x, float tilt_y)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus tilt event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_tilt_event(device, tilt_x, tilt_y);
return NULL;
}
return maybe_error_on_device_state(device, "stylus tilt");
}
static struct brei_result *
client_msg_stylus_rotation(struct eis_stylus *stylus, float rotation)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus rotation event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_rotation_event(device, rotation);
return NULL;
}
return maybe_error_on_device_state(device, "stylus rotation");
}
static struct brei_result *
client_msg_stylus_airbrush_flow(struct eis_stylus *stylus, float flow)
{
struct eis_device *device = eis_stylus_get_device(stylus);
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
"Stylus airbrush_flow event for non-stylus device");
}
if (device->state == EIS_DEVICE_STATE_EMULATING) {
eis_queue_stylus_airbrush_flow_event(device, flow);
return NULL;
}
return maybe_error_on_device_state(device, "stylus airbrush_flow");
}
static struct brei_result *
client_msg_stylus_release(struct eis_stylus *stylus)
{
struct eis_device *device = eis_stylus_get_device(stylus);
eis_stylus_event_destroyed(device->stylus,
eis_client_get_next_serial(eis_device_get_client(device)));
eis_stylus_unref(steal(&device->stylus));
return NULL;
}
static const struct eis_stylus_interface stylus_interface = {
.release = client_msg_stylus_release,
.airbrush_flow = client_msg_stylus_airbrush_flow,
.rotation = client_msg_stylus_rotation,
.tilt = client_msg_stylus_tilt,
.distance = client_msg_stylus_distance,
.pressure = client_msg_stylus_pressure,
.motion = client_msg_stylus_motion,
.up = client_msg_stylus_up,
.down = client_msg_stylus_down,
.erase_stop = client_msg_stylus_erase_stop,
.erase_start = client_msg_stylus_erase_start,
.proximity_out = client_msg_stylus_proximity_out,
.proximity_in = client_msg_stylus_proximity_in,
.bind_tool_capabilities = client_msg_stylus_bind_tool_capabilities,
};
const struct eis_stylus_interface *
eis_device_get_stylus_interface(struct eis_device *device)
{
return &stylus_interface;
}
_public_ struct eis_device *
eis_seat_new_device(struct eis_seat *seat)
{
@ -809,6 +1103,7 @@ eis_device_configure_capability(struct eis_device *device, enum eis_device_capab
case EIS_DEVICE_CAP_TOUCH:
case EIS_DEVICE_CAP_BUTTON:
case EIS_DEVICE_CAP_SCROLL:
case EIS_DEVICE_CAP_STYLUS:
mask_add(device->capabilities, cap);
break;
}
@ -936,6 +1231,19 @@ eis_device_add(struct eis_device *device)
goto out;
}
if (eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
device->stylus = eis_stylus_new(device);
rc = eis_device_event_interface(device, eis_stylus_get_id(device->stylus),
EIS_STYLUS_INTERFACE_NAME,
eis_stylus_get_version(device->stylus));
if (rc < 0)
goto out;
// FIXME: This should not use a hardcoded value -- it should offer
// whatever capabilities the server implementation wants!
eis_device_stylus_tool_capabilities(device, 63);
}
rc = eis_device_event_done(device);
if (rc < 0)
goto out;
@ -991,6 +1299,10 @@ eis_device_remove(struct eis_device *device)
eis_keyboard_event_destroyed(device->keyboard, eis_client_get_next_serial(client));
eis_keyboard_unref(steal(&device->keyboard));
}
if (device->stylus) {
eis_stylus_event_destroyed(device->stylus, eis_client_get_next_serial(client));
eis_stylus_unref(steal(&device->stylus));
}
if (device->state != EIS_DEVICE_STATE_NEW)
eis_device_event_destroyed(device, eis_client_get_next_serial(client));
@ -1018,6 +1330,7 @@ eis_device_has_capability(struct eis_device *device,
case EIS_DEVICE_CAP_TOUCH:
case EIS_DEVICE_CAP_BUTTON:
case EIS_DEVICE_CAP_SCROLL:
case EIS_DEVICE_CAP_STYLUS:
return mask_all(device->capabilities, cap);
}
return false;
@ -1476,3 +1789,235 @@ eis_device_keyboard_send_xkb_modifiers(struct eis_device *device, uint32_t depre
eis_client_get_next_serial(eis_device_get_client(device)),
depressed, locked, latched, group);
}
_public_ struct eis_stylus *
eis_device_stylus_new(struct eis_device *device)
{
struct eis_stylus *stylus = eis_stylus_new(device);
return stylus;
}
_public_ void
eis_device_stylus_tool_capabilities(struct eis_device *device, uint32_t capability)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_NEW)
return;
device->send_frame_event = false;
eis_stylus_event_tool_capabilities(device->stylus, capability);
}
_public_ void
eis_device_stylus_proximity_in(struct eis_device *device)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
eis_stylus_event_proximity_in(device->stylus);
}
_public_ void
eis_device_stylus_proximity_out(struct eis_device *device)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
eis_stylus_event_proximity_out(device->stylus);
}
_public_ void
eis_device_stylus_erase_start(struct eis_device *device)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
eis_stylus_event_erase_start(device->stylus);
}
_public_ void
eis_device_stylus_erase_stop(struct eis_device *device)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
eis_stylus_event_erase_stop(device->stylus);
}
_public_ void
eis_device_stylus_down(struct eis_device *device)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
eis_stylus_event_down(device->stylus);
}
_public_ void
eis_device_stylus_up(struct eis_device *device)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
eis_stylus_event_up(device->stylus);
}
_public_ void
eis_device_stylus_motion(struct eis_device *device, float x, float y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
if (!eis_device_in_region(device, x, y))
return;
device->send_frame_event = true;
eis_stylus_event_motion(device->stylus, x, y);
}
_public_ void
eis_device_stylus_pressure(struct eis_device *device, float pressure)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
eis_stylus_event_pressure(device->stylus, pressure);
}
_public_ void
eis_device_stylus_distance(struct eis_device *device, float distance)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
eis_stylus_event_distance(device->stylus, distance);
}
_public_ void
eis_device_stylus_tilt(struct eis_device *device, float tilt_x, float tilt_y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
eis_stylus_event_tilt(device->stylus, tilt_x, tilt_y);
}
_public_ void
eis_device_stylus_rotation(struct eis_device *device, float rotation)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
eis_stylus_event_rotation(device->stylus, rotation);
}
_public_ void
eis_device_stylus_airbrush_flow(struct eis_device *device, float flow)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_STYLUS)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a stylus device", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
eis_stylus_event_airbrush_flow(device->stylus, flow);
}

View file

@ -52,6 +52,7 @@ struct eis_device {
struct eis_button *button;
struct eis_keyboard *keyboard;
struct eis_touchscreen *touchscreen;
struct eis_stylus *stylus;
char *name;
enum eis_device_state state;
@ -117,6 +118,7 @@ OBJECT_DECLARE_GETTER(eis_device, scroll_interface, const struct eis_scroll_inte
OBJECT_DECLARE_GETTER(eis_device, button_interface, const struct eis_button_interface *);
OBJECT_DECLARE_GETTER(eis_device, keyboard_interface, const struct eis_keyboard_interface *);
OBJECT_DECLARE_GETTER(eis_device, touchscreen_interface, const struct eis_touchscreen_interface *);
OBJECT_DECLARE_GETTER(eis_device, stylus_interface, const struct eis_stylus_interface *);
void
eis_device_set_client_keymap(struct eis_device *device,

View file

@ -57,6 +57,19 @@ eis_event_destroy(struct eis_event *event)
case EIS_EVENT_TOUCH_MOTION:
case EIS_EVENT_TOUCH_UP:
case EIS_EVENT_FRAME:
case EIS_EVENT_STYLUS_BIND_TOOL_CAPABILITIES:
case EIS_EVENT_STYLUS_PROXIMITY_IN:
case EIS_EVENT_STYLUS_PROXIMITY_OUT:
case EIS_EVENT_STYLUS_ERASE_START:
case EIS_EVENT_STYLUS_ERASE_STOP:
case EIS_EVENT_STYLUS_DOWN:
case EIS_EVENT_STYLUS_UP:
case EIS_EVENT_STYLUS_MOTION:
case EIS_EVENT_STYLUS_PRESSURE:
case EIS_EVENT_STYLUS_DISTANCE:
case EIS_EVENT_STYLUS_TILT:
case EIS_EVENT_STYLUS_ROTATION:
case EIS_EVENT_STYLUS_AIRBRUSH_FLOW:
handled = true;
break;
case EIS_EVENT_PONG:
@ -192,6 +205,19 @@ eis_event_get_time(struct eis_event *event)
EIS_EVENT_TOUCH_DOWN,
EIS_EVENT_TOUCH_UP,
EIS_EVENT_TOUCH_MOTION,
EIS_EVENT_STYLUS_BIND_TOOL_CAPABILITIES,
EIS_EVENT_STYLUS_PROXIMITY_IN,
EIS_EVENT_STYLUS_PROXIMITY_OUT,
EIS_EVENT_STYLUS_ERASE_START,
EIS_EVENT_STYLUS_ERASE_STOP,
EIS_EVENT_STYLUS_DOWN,
EIS_EVENT_STYLUS_UP,
EIS_EVENT_STYLUS_MOTION,
EIS_EVENT_STYLUS_PRESSURE,
EIS_EVENT_STYLUS_DISTANCE,
EIS_EVENT_STYLUS_TILT,
EIS_EVENT_STYLUS_ROTATION,
EIS_EVENT_STYLUS_AIRBRUSH_FLOW,
EIS_EVENT_FRAME);
return event->timestamp;
@ -217,6 +243,7 @@ eis_event_seat_has_capability(struct eis_event *event, enum eis_device_capabilit
case EIS_DEVICE_CAP_TOUCH:
case EIS_DEVICE_CAP_BUTTON:
case EIS_DEVICE_CAP_SCROLL:
case EIS_DEVICE_CAP_STYLUS:
return mask_all(event->bind.capabilities, cap);
}
return false;
@ -430,3 +457,114 @@ eis_event_touch_get_is_cancel(struct eis_event *event)
return event->touch.is_cancel;
}
_public_ bool
eis_event_stylus_get_is_prox(struct eis_event *event)
{
require_event_type(event, 0,
EIS_EVENT_STYLUS_PROXIMITY_IN,
EIS_EVENT_STYLUS_PROXIMITY_OUT);
return event->stylus.is_prox;
}
_public_ bool
eis_event_stylus_get_is_erase(struct eis_event *event)
{
require_event_type(event, 0,
EIS_EVENT_STYLUS_ERASE_START,
EIS_EVENT_STYLUS_ERASE_STOP);
return event->stylus.is_erase;
}
_public_ uint32_t
eis_event_stylus_get_capability(struct eis_event *event)
{
require_event_type(event, 0,
EIS_EVENT_STYLUS_BIND_TOOL_CAPABILITIES);
return event->stylus.capability;
}
_public_ bool
eis_event_stylus_get_is_down(struct eis_event *event)
{
require_event_type(event, 0,
EIS_EVENT_STYLUS_DOWN,
EIS_EVENT_STYLUS_UP);
return event->stylus.is_down;
}
_public_ float
eis_event_stylus_get_x(struct eis_event *event)
{
require_event_type(event, 0.0,
EIS_EVENT_STYLUS_MOTION);
return event->stylus.x;
}
_public_ float
eis_event_stylus_get_y(struct eis_event *event)
{
require_event_type(event, 0.0,
EIS_EVENT_STYLUS_MOTION);
return event->stylus.y;
}
_public_ float
eis_event_stylus_get_pressure(struct eis_event *event)
{
require_event_type(event, 0.0,
EIS_EVENT_STYLUS_PRESSURE);
return event->stylus.pressure;
}
_public_ float
eis_event_stylus_get_distance(struct eis_event *event)
{
require_event_type(event, 0.0,
EIS_EVENT_STYLUS_DISTANCE);
return event->stylus.distance;
}
_public_ float
eis_event_stylus_get_tilt_x(struct eis_event *event)
{
require_event_type(event, 0.0,
EIS_EVENT_STYLUS_TILT);
return event->stylus.tilt_x;
}
_public_ float
eis_event_stylus_get_tilt_y(struct eis_event *event)
{
require_event_type(event, 0.0,
EIS_EVENT_STYLUS_TILT);
return event->stylus.tilt_y;
}
_public_ float
eis_event_stylus_get_rotation(struct eis_event *event)
{
require_event_type(event, 0.0,
EIS_EVENT_STYLUS_ROTATION);
return event->stylus.rotation;
}
_public_ float
eis_event_stylus_get_flow(struct eis_event *event)
{
require_event_type(event, 0.0,
EIS_EVENT_STYLUS_AIRBRUSH_FLOW);
return event->stylus.flow;
}

View file

@ -69,6 +69,12 @@ struct eis_event {
struct {
struct eis_callback *callback;
} sync;
struct {
uint32_t capability;
float x, y, pressure, distance, rotation, flow;
float tilt_x, tilt_y;
bool is_prox, is_down, is_erase;
} stylus;
};
};

View file

@ -137,6 +137,7 @@ client_msg_finish(struct eis_handshake *setup)
SEND_INTERFACE_VERSION(EIS_SCROLL, ei_scroll);
SEND_INTERFACE_VERSION(EIS_KEYBOARD, ei_keyboard);
SEND_INTERFACE_VERSION(EIS_TOUCHSCREEN, ei_touchscreen);
SEND_INTERFACE_VERSION(EIS_STYLUS, ei_stylus);
#undef SEND_INTERFACE_VERSION
@ -237,6 +238,7 @@ client_msg_interface_version(struct eis_handshake *setup, const char *name, uint
VERSION_ENTRY(ei_scroll),
VERSION_ENTRY(ei_keyboard),
VERSION_ENTRY(ei_touchscreen),
VERSION_ENTRY(ei_stylus),
#undef VERSION_ENTRY
};

View file

@ -51,6 +51,7 @@
#include "libeis-region.h"
#include "libeis-scroll.h"
#include "libeis-seat.h"
#include "libeis-stylus.h"
#include "libeis-touchscreen.h"
struct eis_backend_interface {
@ -163,6 +164,45 @@ eis_queue_touch_cancel_event(struct eis_device *device, uint32_t touchid);
void
eis_sync_event_send_done(struct eis_event *e);
void
eis_queue_stylus_bind_tool_capabilities_event(struct eis_device *device, uint32_t capability);
void
eis_queue_stylus_proximity_in_event(struct eis_device *device);
void
eis_queue_stylus_proximity_out_event(struct eis_device *device);
void
eis_queue_stylus_erase_start_event(struct eis_device *device);
void
eis_queue_stylus_erase_stop_event(struct eis_device *device);
void
eis_queue_stylus_down_event(struct eis_device *device);
void
eis_queue_stylus_up_event(struct eis_device *device);
void
eis_queue_stylus_motion_event(struct eis_device *device, float x, float y);
void
eis_queue_stylus_pressure_event(struct eis_device *device, float p);
void
eis_queue_stylus_distance_event(struct eis_device *device, float d);
void
eis_queue_stylus_tilt_event(struct eis_device *device, float tilt_x, float tilt_y);
void
eis_queue_stylus_rotation_event(struct eis_device *device, float r);
void
eis_queue_stylus_airbrush_flow_event(struct eis_device *device, float s);
_printf_(6, 7) void
eis_log_msg(struct eis *eis,
enum eis_log_priority priority,

View file

@ -108,6 +108,8 @@ client_msg_bind(struct eis_seat *seat, uint64_t caps)
capabilities |= EIS_DEVICE_CAP_BUTTON;
if (caps & bit(EIS_SCROLL_INTERFACE_INDEX))
capabilities |= EIS_DEVICE_CAP_SCROLL;
if (caps & bit(EIS_STYLUS_INTERFACE_INDEX))
capabilities |= EIS_DEVICE_CAP_STYLUS;
eis_seat_bind(seat, capabilities);
@ -221,6 +223,14 @@ eis_seat_add(struct eis_seat *seat)
mask_add(seat->capabilities.proto_mask, mask);
}
if (seat->capabilities.c_mask & EIS_DEVICE_CAP_STYLUS &&
client->interface_versions.ei_stylus > 0) {
uint64_t mask = bit(EIS_STYLUS_INTERFACE_INDEX);
eis_seat_event_capability(seat, mask,
EIS_STYLUS_INTERFACE_NAME);
mask_add(seat->capabilities.proto_mask, mask);
}
eis_seat_event_done(seat);
}
@ -313,6 +323,7 @@ eis_seat_configure_capability(struct eis_seat *seat,
case EIS_DEVICE_CAP_TOUCH:
case EIS_DEVICE_CAP_BUTTON:
case EIS_DEVICE_CAP_SCROLL:
case EIS_DEVICE_CAP_STYLUS:
mask_add(seat->capabilities.c_mask, cap);
break;
}
@ -329,6 +340,7 @@ eis_seat_has_capability(struct eis_seat *seat,
case EIS_DEVICE_CAP_TOUCH:
case EIS_DEVICE_CAP_BUTTON:
case EIS_DEVICE_CAP_SCROLL:
case EIS_DEVICE_CAP_STYLUS:
return mask_all(seat->capabilities.c_mask, cap);
}
return false;

107
src/libeis-stylus.c Normal file
View file

@ -0,0 +1,107 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2023 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <errno.h>
#include <stdbool.h>
#include "util-bits.h"
#include "util-macros.h"
#include "util-mem.h"
#include "util-io.h"
#include "util-strings.h"
#include "util-version.h"
#include "libeis-private.h"
#include "eis-proto.h"
static void
eis_stylus_destroy(struct eis_stylus *stylus)
{
struct eis_client * client = eis_stylus_get_client(stylus);
eis_client_unregister_object(client, &stylus->proto_object);
}
OBJECT_IMPLEMENT_REF(eis_stylus);
OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_stylus);
OBJECT_IMPLEMENT_GETTER_AS_REF(eis_stylus, proto_object, const struct brei_object *);
static
OBJECT_IMPLEMENT_CREATE(eis_stylus);
static
OBJECT_IMPLEMENT_PARENT(eis_stylus, eis_device);
uint32_t
eis_stylus_get_version(struct eis_stylus *stylus)
{
return stylus->proto_object.version;
}
object_id_t
eis_stylus_get_id(struct eis_stylus *stylus)
{
return stylus->proto_object.id;
}
struct eis_device *
eis_stylus_get_device(struct eis_stylus *stylus)
{
return eis_stylus_parent(stylus);
}
struct eis_client*
eis_stylus_get_client(struct eis_stylus *stylus)
{
return eis_device_get_client(eis_stylus_get_device(stylus));
}
struct eis*
eis_stylus_get_context(struct eis_stylus *stylus)
{
struct eis_client *client = eis_stylus_get_client(stylus);
return eis_client_get_context(client);
}
const struct eis_stylus_interface *
eis_stylus_get_interface(struct eis_stylus *stylus) {
return eis_device_get_stylus_interface(eis_stylus_get_device(stylus));
}
struct eis_stylus *
eis_stylus_new(struct eis_device *device)
{
struct eis_stylus *stylus = eis_stylus_create(&device->object);
struct eis_client *client = eis_device_get_client(device);
stylus->proto_object.id = eis_client_get_new_id(client);
stylus->proto_object.implementation = stylus;
stylus->proto_object.interface = &eis_stylus_proto_interface;
stylus->proto_object.version = client->interface_versions.ei_stylus;
list_init(&stylus->proto_object.link);
eis_client_register_object(client, &stylus->proto_object);
return stylus; /* ref owned by caller */
}

51
src/libeis-stylus.h Normal file
View file

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2020 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include "util-object.h"
#include "brei-shared.h"
#include "libeis-client.h"
struct eis;
struct eis_client;
/* This is a protocol-only object, not exposed in the API */
struct eis_stylus {
struct object object;
struct brei_object proto_object;
};
OBJECT_DECLARE_GETTER(eis_stylus, context, struct eis *);
OBJECT_DECLARE_GETTER(eis_stylus, device, struct eis_device *);
OBJECT_DECLARE_GETTER(eis_stylus, client, struct eis_client *);
OBJECT_DECLARE_GETTER(eis_stylus, id, object_id_t);
OBJECT_DECLARE_GETTER(eis_stylus, version, uint32_t);
OBJECT_DECLARE_GETTER(eis_stylus, proto_object, const struct brei_object *);
OBJECT_DECLARE_GETTER(eis_stylus, interface, const struct eis_stylus_interface *);
OBJECT_DECLARE_REF(eis_stylus);
OBJECT_DECLARE_UNREF(eis_stylus);
struct eis_stylus *
eis_stylus_new(struct eis_device *device);

View file

@ -154,6 +154,19 @@ eis_event_type_to_string(enum eis_event_type type)
CASE_RETURN_STRING(EIS_EVENT_TOUCH_DOWN);
CASE_RETURN_STRING(EIS_EVENT_TOUCH_UP);
CASE_RETURN_STRING(EIS_EVENT_TOUCH_MOTION);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_BIND_TOOL_CAPABILITIES);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_PROXIMITY_IN);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_PROXIMITY_OUT);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_ERASE_START);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_ERASE_STOP);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_DOWN);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_UP);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_MOTION);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_PRESSURE);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_DISTANCE);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_TILT);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_ROTATION);
CASE_RETURN_STRING(EIS_EVENT_STYLUS_AIRBRUSH_FLOW);
CASE_RETURN_STRING(EIS_EVENT_FRAME);
}
@ -175,6 +188,19 @@ update_event_timestamp(struct eis_event *event, uint64_t time)
case EIS_EVENT_TOUCH_DOWN:
case EIS_EVENT_TOUCH_UP:
case EIS_EVENT_TOUCH_MOTION:
case EIS_EVENT_STYLUS_BIND_TOOL_CAPABILITIES:
case EIS_EVENT_STYLUS_PROXIMITY_IN:
case EIS_EVENT_STYLUS_PROXIMITY_OUT:
case EIS_EVENT_STYLUS_ERASE_START:
case EIS_EVENT_STYLUS_ERASE_STOP:
case EIS_EVENT_STYLUS_DOWN:
case EIS_EVENT_STYLUS_UP:
case EIS_EVENT_STYLUS_MOTION:
case EIS_EVENT_STYLUS_PRESSURE:
case EIS_EVENT_STYLUS_DISTANCE:
case EIS_EVENT_STYLUS_TILT:
case EIS_EVENT_STYLUS_ROTATION:
case EIS_EVENT_STYLUS_AIRBRUSH_FLOW:
if (event->timestamp != 0) {
log_bug(eis_event_get_context(event),
"Unexpected timestamp for event of type: %s",
@ -211,6 +237,19 @@ eis_queue_event(struct eis_event *event)
case EIS_EVENT_TOUCH_DOWN:
case EIS_EVENT_TOUCH_UP:
case EIS_EVENT_TOUCH_MOTION:
case EIS_EVENT_STYLUS_BIND_TOOL_CAPABILITIES:
case EIS_EVENT_STYLUS_PROXIMITY_IN:
case EIS_EVENT_STYLUS_PROXIMITY_OUT:
case EIS_EVENT_STYLUS_ERASE_START:
case EIS_EVENT_STYLUS_ERASE_STOP:
case EIS_EVENT_STYLUS_DOWN:
case EIS_EVENT_STYLUS_UP:
case EIS_EVENT_STYLUS_MOTION:
case EIS_EVENT_STYLUS_PRESSURE:
case EIS_EVENT_STYLUS_DISTANCE:
case EIS_EVENT_STYLUS_TILT:
case EIS_EVENT_STYLUS_ROTATION:
case EIS_EVENT_STYLUS_AIRBRUSH_FLOW:
prefix = "pending ";
queue = &device->pending_event_queue;
break;
@ -474,6 +513,125 @@ eis_queue_touch_cancel_event(struct eis_device *device, uint32_t touchid)
eis_queue_event(e);
}
void
eis_queue_stylus_bind_tool_capabilities_event(struct eis_device *device, uint32_t capability)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_BIND_TOOL_CAPABILITIES;
e->stylus.capability = capability;
eis_queue_event(e);
}
void
eis_queue_stylus_proximity_in_event(struct eis_device *device)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_PROXIMITY_IN;
e->stylus.is_prox = true;
eis_queue_event(e);
}
void
eis_queue_stylus_proximity_out_event(struct eis_device *device)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_PROXIMITY_OUT;
e->stylus.is_prox = false;
eis_queue_event(e);
}
void
eis_queue_stylus_erase_start_event(struct eis_device *device)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_ERASE_START;
e->stylus.is_erase = true;
eis_queue_event(e);
}
void
eis_queue_stylus_erase_stop_event(struct eis_device *device)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_ERASE_STOP;
e->stylus.is_erase = false;
eis_queue_event(e);
}
void
eis_queue_stylus_down_event(struct eis_device *device)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_DOWN;
e->stylus.is_down = true;
eis_queue_event(e);
}
void
eis_queue_stylus_up_event(struct eis_device *device)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_UP;
e->stylus.is_down = false;
eis_queue_event(e);
}
void
eis_queue_stylus_motion_event(struct eis_device *device, float x, float y)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_MOTION;
e->stylus.x = x;
e->stylus.y = y;
eis_queue_event(e);
}
void
eis_queue_stylus_pressure_event(struct eis_device *device, float pressure)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_PRESSURE;
e->stylus.pressure = pressure;
eis_queue_event(e);
}
void
eis_queue_stylus_distance_event(struct eis_device *device, float distance)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_DISTANCE;
e->stylus.distance = distance;
eis_queue_event(e);
}
void
eis_queue_stylus_tilt_event(struct eis_device *device, float tilt_x, float tilt_y)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_TILT;
e->stylus.tilt_x = tilt_x;
e->stylus.tilt_y = tilt_y;
eis_queue_event(e);
}
void
eis_queue_stylus_rotation_event(struct eis_device *device, float rotation)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_ROTATION;
e->stylus.rotation = rotation;
eis_queue_event(e);
}
void
eis_queue_stylus_airbrush_flow_event(struct eis_device *device, float flow)
{
struct eis_event *e = eis_event_new_for_device(device);
e->type = EIS_EVENT_STYLUS_AIRBRUSH_FLOW;
e->stylus.flow = flow;
eis_queue_event(e);
}
_public_ struct eis_event*
eis_get_event(struct eis *eis)
{

View file

@ -226,6 +226,7 @@ enum eis_device_capability {
EIS_DEVICE_CAP_TOUCH = (1 << 3),
EIS_DEVICE_CAP_SCROLL = (1 << 4),
EIS_DEVICE_CAP_BUTTON = (1 << 5),
EIS_DEVICE_CAP_STYLUS = (1 << 7),
};
/**
@ -396,6 +397,59 @@ enum eis_event_type {
* properties).
*/
EIS_EVENT_TOUCH_MOTION,
/**
* TODO
*/
EIS_EVENT_STYLUS_BIND_TOOL_CAPABILITIES = 1000,
/**
* TODO
*/
EIS_EVENT_STYLUS_PROXIMITY_IN,
/**
* TODO
*/
EIS_EVENT_STYLUS_PROXIMITY_OUT,
/**
* TODO
*/
EIS_EVENT_STYLUS_ERASE_START,
/**
* TODO
*/
EIS_EVENT_STYLUS_ERASE_STOP,
/**
* TODO
*/
EIS_EVENT_STYLUS_DOWN,
/**
* TODO
*/
EIS_EVENT_STYLUS_UP,
/**
* TODO
*/
EIS_EVENT_STYLUS_MOTION,
/**
* TODO
*/
EIS_EVENT_STYLUS_PRESSURE,
/**
* TODO
*/
EIS_EVENT_STYLUS_DISTANCE,
/**
* TODO
*/
EIS_EVENT_STYLUS_TILT,
/**
* TODO
*/
EIS_EVENT_STYLUS_ROTATION,
/**
* TODO
*/
EIS_EVENT_STYLUS_AIRBRUSH_FLOW,
};
/**
@ -1678,6 +1732,104 @@ eis_touch_get_user_data(struct eis_touch *touch);
struct eis_device *
eis_touch_get_device(struct eis_touch *touch);
/**
* @ingroup libeis-receiver
* TODO
*/
struct eis_stylus *
eis_device_stylus_new(struct eis_device *device);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_tool_capabilities(struct eis_device *device, uint32_t capability);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_proximity_in(struct eis_device *device);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_proximity_out(struct eis_device *device);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_erase_start(struct eis_device *device);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_erase_stop(struct eis_device *device);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_down(struct eis_device *device);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_up(struct eis_device *device);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_motion(struct eis_device *device, float x, float y);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_pressure(struct eis_device *device, float p);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_distance(struct eis_device *device, float d);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_tilt(struct eis_device *device, float tilt_x, float tilt_y);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_rotation(struct eis_device *device, float r);
/**
* @ingroup libeis-receiver
* TODO
*/
void
eis_device_stylus_airbrush_flow(struct eis_device *device, float s);
/**
* @ingroup libeis-sender
*
@ -1869,6 +2021,110 @@ eis_event_touch_get_y(struct eis_event *event);
bool
eis_event_touch_get_is_cancel(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
bool
eis_event_stylus_get_is_prox(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
bool
eis_event_stylus_get_is_erase(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
uint32_t
eis_event_stylus_get_code(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
uint32_t
eis_event_stylus_get_capability(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
bool
eis_event_stylus_get_is_down(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
float
eis_event_stylus_get_x(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
float
eis_event_stylus_get_y(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
float
eis_event_stylus_get_pressure(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
float
eis_event_stylus_get_distance(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
float
eis_event_stylus_get_tilt_x(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
float
eis_event_stylus_get_tilt_y(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
float
eis_event_stylus_get_rotation(struct eis_event *event);
/**
* @ingroup libeis-sender
*
* TODO
*/
float
eis_event_stylus_get_flow(struct eis_event *event);
/**
* @returns a timestamp for the current time to pass into
* eis_device_frame().

View file

@ -58,6 +58,7 @@ if build_libei
'libei-scroll.c',
'libei-seat.c',
'libei-socket.c',
'libei-stylus.c',
'libei-touchscreen.c',
) + [brei_proto_headers, ei_proto_headers, ei_proto_sources]
@ -121,6 +122,7 @@ if build_libeis
'libeis-scroll.c',
'libeis-seat.c',
'libeis-socket.c',
'libeis-stylus.c',
'libeis-touchscreen.c',
'libeis.c',
) + [brei_proto_headers, eis_proto_headers, eis_proto_sources]

View file

@ -75,6 +75,7 @@ struct peck {
struct eis_device *eis_button;
struct eis_device *eis_scroll;
struct eis_device *eis_touch;
struct eis_device *eis_stylus;
struct ei_seat *ei_seat;
struct ei_device *ei_pointer;
@ -83,6 +84,7 @@ struct peck {
struct ei_device *ei_button;
struct ei_device *ei_scroll;
struct ei_device *ei_touch;
struct ei_device *ei_stylus;
uint64_t now;
@ -174,6 +176,7 @@ peck_destroy(struct peck *peck)
eis_device_unref(peck->eis_touch);
eis_device_unref(peck->eis_button);
eis_device_unref(peck->eis_scroll);
eis_device_unref(peck->eis_stylus);
eis_seat_unref(peck->eis_seat);
ei_device_unref(peck->ei_pointer);
@ -182,6 +185,7 @@ peck_destroy(struct peck *peck)
ei_device_unref(peck->ei_touch);
ei_device_unref(peck->ei_button);
ei_device_unref(peck->ei_scroll);
ei_device_unref(peck->ei_stylus);
ei_seat_unref(peck->ei_seat);
ei_unref(peck->ei);
@ -351,6 +355,13 @@ peck_eis_get_default_touch(struct peck *peck)
return peck->eis_touch;
};
struct eis_device *
peck_eis_get_default_stylus(struct peck *peck)
{
munit_assert_ptr_not_null(peck->eis_stylus);
return peck->eis_stylus;
};
struct ei_seat *
peck_ei_get_default_seat(struct peck *peck)
{
@ -400,6 +411,13 @@ peck_ei_get_default_touch(struct peck *peck)
return peck->ei_touch;
};
struct ei_device *
peck_ei_get_default_stylus(struct peck *peck)
{
munit_assert_ptr_not_null(peck->ei_stylus);
return peck->ei_stylus;
};
/* Ensures that device frames in tests always have an ascending and fixed
* interval. Every time this is called it adds 10ms to the time offset.
*/
@ -708,12 +726,14 @@ peck_enable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior)
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_START_EMULATING);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_STOP_EMULATING);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_STYLUS_CAPABILITIES);
break;
case PECK_EIS_BEHAVIOR_ADD_DEVICES:
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_POINTER);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_KEYBOARD);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_TOUCH);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_STYLUS);
break;
case PECK_EIS_BEHAVIOR_HANDLE_BIND_SEAT:
case PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE:
@ -725,7 +745,9 @@ peck_enable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior)
case PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE:
case PECK_EIS_BEHAVIOR_ADD_KEYBOARD:
case PECK_EIS_BEHAVIOR_ADD_TOUCH:
case PECK_EIS_BEHAVIOR_ADD_STYLUS:
case PECK_EIS_BEHAVIOR_HANDLE_DEVICE_READY:
case PECK_EIS_BEHAVIOR_HANDLE_STYLUS_CAPABILITIES:
flag_set(peck->eis_behavior, behavior);
break;
case PECK_EIS_BEHAVIOR_REJECT_CLIENT:
@ -766,12 +788,14 @@ peck_disable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior)
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_START_EMULATING);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_STOP_EMULATING);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_STYLUS_CAPABILITIES);
break;
case PECK_EIS_BEHAVIOR_ADD_DEVICES:
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_POINTER);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_KEYBOARD);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_TOUCH);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_STYLUS);
break;
case PECK_EIS_BEHAVIOR_HANDLE_BIND_SEAT:
case PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE:
@ -783,11 +807,13 @@ peck_disable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior)
case PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE:
case PECK_EIS_BEHAVIOR_ADD_KEYBOARD:
case PECK_EIS_BEHAVIOR_ADD_TOUCH:
case PECK_EIS_BEHAVIOR_ADD_STYLUS:
case PECK_EIS_BEHAVIOR_HANDLE_DEVICE_READY:
case PECK_EIS_BEHAVIOR_REJECT_CLIENT:
case PECK_EIS_BEHAVIOR_ACCEPT_CLIENT:
case PECK_EIS_BEHAVIOR_RESUME_DEVICE:
case PECK_EIS_BEHAVIOR_SUSPEND_DEVICE:
case PECK_EIS_BEHAVIOR_HANDLE_STYLUS_CAPABILITIES:
flag_clear(peck->eis_behavior, behavior);
break;
}
@ -817,6 +843,7 @@ peck_enable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior)
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_STYLUS);
break;
case PECK_EI_BEHAVIOR_AUTOSTART:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER:
@ -825,10 +852,12 @@ peck_enable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior)
case PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_STYLUS:
case PECK_EI_BEHAVIOR_HANDLE_FRAME:
case PECK_EI_BEHAVIOR_HANDLE_SYNC:
case PECK_EI_BEHAVIOR_HANDLE_RESUMED:
case PECK_EI_BEHAVIOR_HANDLE_PAUSED:
case PECK_EI_BEHAVIOR_HANDLE_STYLUS_CAPABILITIES:
flag_set(peck->ei_behavior, behavior);
break;
}
@ -845,6 +874,7 @@ peck_disable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior)
case PECK_EI_BEHAVIOR_AUTOSEAT:
flag_clear(peck->ei_behavior, behavior);
break;
//FIXME: Shouldn't these be /disable/ ?
case PECK_EI_BEHAVIOR_AUTODEVICES:
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOSEAT);
@ -858,6 +888,7 @@ peck_disable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior)
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_STYLUS);
break;
case PECK_EI_BEHAVIOR_AUTOSTART:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER:
@ -866,10 +897,12 @@ peck_disable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior)
case PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_STYLUS:
case PECK_EI_BEHAVIOR_HANDLE_FRAME:
case PECK_EI_BEHAVIOR_HANDLE_SYNC:
case PECK_EI_BEHAVIOR_HANDLE_RESUMED:
case PECK_EI_BEHAVIOR_HANDLE_PAUSED:
case PECK_EI_BEHAVIOR_HANDLE_STYLUS_CAPABILITIES:
flag_clear(peck->ei_behavior, behavior);
break;
}
@ -886,6 +919,7 @@ peck_create_eis_seat(struct peck *peck, struct eis_client *client)
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_TOUCH);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_BUTTON);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_SCROLL);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_STYLUS);
log_debug(peck, "EIS adding seat: '%s'\n", eis_seat_get_name(seat));
eis_seat_add(seat);
@ -972,6 +1006,27 @@ peck_eis_create_touch(struct peck *peck, struct eis_seat *seat, const char *name
return device;
}
static inline struct eis_device *
peck_eis_create_stylus(struct peck *peck, struct eis_seat *seat, const char *name)
{
struct eis_device *device = eis_seat_new_device(seat);
_unref_(eis_region) *region = eis_device_new_region(device);
eis_region_set_offset(region, 0, 0);
eis_region_set_size(region, 1920, 1080);
eis_device_configure_name(device, name);
eis_device_configure_capability(device, EIS_DEVICE_CAP_STYLUS);
eis_device_configure_capability(device, EIS_DEVICE_CAP_BUTTON);
eis_region_add(region);
eis_device_add(device);
// FIXME: Configure capabilities from PECK_EIS_BEHAVIOR_*
// once eis_device_add is updated with a method to do so.
return device;
}
static inline void
peck_handle_eis_seat_bind(struct peck *peck, struct eis_event *e)
{
@ -1055,13 +1110,35 @@ peck_handle_eis_seat_bind(struct peck *peck, struct eis_event *e)
}
}
if (eis_event_seat_has_capability(e, EIS_DEVICE_CAP_STYLUS)) {
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ADD_STYLUS) && !peck->eis_stylus) {
// FIXME: This may need to be updated once the ability to announce
// and bind capabilities is available
log_debug(peck, "EIS creating default stylus\n");
_unref_(eis_device) *stylus = peck_eis_create_stylus(peck, seat, "default stylus");
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_RESUME_DEVICE))
eis_device_resume(stylus);
peck->eis_stylus = eis_device_ref(stylus);
}
} else {
if (peck->eis_stylus) {
log_debug(peck, "EIS removing default stylus\n");
_unref_(eis_device) *stylus = steal(&peck->eis_stylus);
eis_device_remove(stylus);
}
}
/* Removing all caps means removing the seat */
if (!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_POINTER) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_POINTER_ABSOLUTE) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_KEYBOARD) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_BUTTON) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_SCROLL) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TOUCH))
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TOUCH) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_STYLUS))
eis_seat_remove(seat);
}
@ -1081,8 +1158,12 @@ peck_eis_device_remove(struct peck *peck, struct eis_device *device)
peck->eis_button = eis_device_unref(device);
if (device == peck->eis_scroll)
peck->eis_scroll = eis_device_unref(device);
if (device == peck->eis_stylus)
peck->eis_stylus = eis_device_unref(device);
}
static uint32_t offered_caps;
bool
_peck_dispatch_eis(struct peck *peck, int lineno)
{
@ -1148,6 +1229,10 @@ _peck_dispatch_eis(struct peck *peck, int lineno)
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_STOP_EMULATING))
process_event = tristate_yes;
break;
case EIS_EVENT_STYLUS_BIND_TOOL_CAPABILITIES:
if (flag_is_set(peck->eis_behavior, PECK_EI_BEHAVIOR_HANDLE_STYLUS_CAPABILITIES))
process_event = tristate_yes;
break;
case EIS_EVENT_POINTER_MOTION:
case EIS_EVENT_POINTER_MOTION_ABSOLUTE:
case EIS_EVENT_BUTTON_BUTTON:
@ -1159,6 +1244,18 @@ _peck_dispatch_eis(struct peck *peck, int lineno)
case EIS_EVENT_TOUCH_DOWN:
case EIS_EVENT_TOUCH_UP:
case EIS_EVENT_TOUCH_MOTION:
case EIS_EVENT_STYLUS_PROXIMITY_IN:
case EIS_EVENT_STYLUS_PROXIMITY_OUT:
case EIS_EVENT_STYLUS_ERASE_START:
case EIS_EVENT_STYLUS_ERASE_STOP:
case EIS_EVENT_STYLUS_DOWN:
case EIS_EVENT_STYLUS_UP:
case EIS_EVENT_STYLUS_MOTION:
case EIS_EVENT_STYLUS_PRESSURE:
case EIS_EVENT_STYLUS_DISTANCE:
case EIS_EVENT_STYLUS_TILT:
case EIS_EVENT_STYLUS_ROTATION:
case EIS_EVENT_STYLUS_AIRBRUSH_FLOW:
need_frame = true;
break;
}
@ -1202,6 +1299,9 @@ _peck_dispatch_eis(struct peck *peck, int lineno)
last_timestamp = timestamp;
break;
}
case EIS_EVENT_STYLUS_BIND_TOOL_CAPABILITIES:
uint32_t bound_caps = eis_event_stylus_get_capability(e);
munit_assert_int(offered_caps & bound_caps, ==, bound_caps);
default:
break;
}
@ -1241,6 +1341,10 @@ peck_check_ei_added(struct peck *peck, struct ei_event *e)
flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL))
return tristate_yes;
if (ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS) &
flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_STYLUS))
return tristate_yes;
return tristate_unset;
}
@ -1308,6 +1412,10 @@ _peck_dispatch_ei(struct peck *peck, int lineno)
case EI_EVENT_DEVICE_START_EMULATING:
case EI_EVENT_DEVICE_STOP_EMULATING:
break;
case EI_EVENT_STYLUS_TOOL_CAPABILITIES:
if (flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_STYLUS_CAPABILITIES))
process_event = tristate_yes;
break;
case EI_EVENT_POINTER_MOTION:
case EI_EVENT_POINTER_MOTION_ABSOLUTE:
case EI_EVENT_BUTTON_BUTTON:
@ -1320,6 +1428,18 @@ _peck_dispatch_ei(struct peck *peck, int lineno)
case EI_EVENT_TOUCH_DOWN:
case EI_EVENT_TOUCH_UP:
case EI_EVENT_TOUCH_MOTION:
case EIS_EVENT_STYLUS_PROXIMITY_IN:
case EIS_EVENT_STYLUS_PROXIMITY_OUT:
case EIS_EVENT_STYLUS_ERASE_START:
case EIS_EVENT_STYLUS_ERASE_STOP:
case EIS_EVENT_STYLUS_DOWN:
case EIS_EVENT_STYLUS_UP:
case EIS_EVENT_STYLUS_MOTION:
case EIS_EVENT_STYLUS_PRESSURE:
case EIS_EVENT_STYLUS_DISTANCE:
case EIS_EVENT_STYLUS_TILT:
case EIS_EVENT_STYLUS_ROTATION:
case EIS_EVENT_STYLUS_AIRBRUSH_FLOW:
need_frame = true;
break;
}
@ -1355,7 +1475,8 @@ _peck_dispatch_ei(struct peck *peck, int lineno)
EI_DEVICE_CAP_KEYBOARD,
EI_DEVICE_CAP_TOUCH,
EI_DEVICE_CAP_BUTTON,
EI_DEVICE_CAP_SCROLL, NULL);
EI_DEVICE_CAP_SCROLL,
EI_DEVICE_CAP_STYLUS, NULL);
break;
}
case EI_EVENT_DEVICE_ADDED:
@ -1373,6 +1494,8 @@ _peck_dispatch_ei(struct peck *peck, int lineno)
peck->ei_button = ei_device_ref(device);
if (!peck->ei_scroll && ei_device_has_capability(device, EI_DEVICE_CAP_SCROLL))
peck->ei_scroll = ei_device_ref(device);
if (!peck->ei_scroll && ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS))
peck->ei_stylus = ei_device_ref(device);
break;
}
case EI_EVENT_DEVICE_RESUMED:
@ -1386,6 +1509,9 @@ _peck_dispatch_ei(struct peck *peck, int lineno)
case EI_EVENT_DEVICE_PAUSED:
/* Nothing to do here */
break;
case EI_EVENT_STYLUS_TOOL_CAPABILITIES:
offered_caps = ei_event_stylus_get_capability(e);
break;
default:
break;
}
@ -1656,6 +1782,19 @@ peck_ei_event_type_name(enum ei_event_type type)
CASE_STRING(TOUCH_DOWN);
CASE_STRING(TOUCH_UP);
CASE_STRING(TOUCH_MOTION);
CASE_STRING(STYLUS_TOOL_CAPABILITIES);
CASE_STRING(STYLUS_PROXIMITY_IN);
CASE_STRING(STYLUS_PROXIMITY_OUT);
CASE_STRING(STYLUS_ERASE_START);
CASE_STRING(STYLUS_ERASE_STOP);
CASE_STRING(STYLUS_DOWN);
CASE_STRING(STYLUS_UP);
CASE_STRING(STYLUS_MOTION);
CASE_STRING(STYLUS_PRESSURE);
CASE_STRING(STYLUS_DISTANCE);
CASE_STRING(STYLUS_TILT);
CASE_STRING(STYLUS_ROTATION);
CASE_STRING(STYLUS_AIRBRUSH_FLOW);
}
#undef CASE_STRING
assert(!"Unhandled ei event type");
@ -1695,6 +1834,19 @@ peck_eis_event_type_name(enum eis_event_type type)
CASE_STRING(TOUCH_UP);
CASE_STRING(TOUCH_MOTION);
CASE_STRING(FRAME);
CASE_STRING(STYLUS_BIND_TOOL_CAPABILITIES);
CASE_STRING(STYLUS_PROXIMITY_IN);
CASE_STRING(STYLUS_PROXIMITY_OUT);
CASE_STRING(STYLUS_ERASE_START);
CASE_STRING(STYLUS_ERASE_STOP);
CASE_STRING(STYLUS_DOWN);
CASE_STRING(STYLUS_UP);
CASE_STRING(STYLUS_MOTION);
CASE_STRING(STYLUS_PRESSURE);
CASE_STRING(STYLUS_DISTANCE);
CASE_STRING(STYLUS_TILT);
CASE_STRING(STYLUS_ROTATION);
CASE_STRING(STYLUS_AIRBRUSH_FLOW);
}
#undef CASE_STRING
assert(!"Unhandled EIS event type");

View file

@ -96,6 +96,10 @@ enum peck_eis_behavior {
*/
PECK_EIS_BEHAVIOR_HANDLE_START_EMULATING,
PECK_EIS_BEHAVIOR_HANDLE_STOP_EMULATING,
/**
* Handle stylus capability events. This behavior is enabled by default.
*/
PECK_EIS_BEHAVIOR_HANDLE_STYLUS_CAPABILITIES,
/**
* Create default devices
@ -105,6 +109,7 @@ enum peck_eis_behavior {
PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE,
PECK_EIS_BEHAVIOR_ADD_KEYBOARD,
PECK_EIS_BEHAVIOR_ADD_TOUCH,
PECK_EIS_BEHAVIOR_ADD_STYLUS,
PECK_EIS_BEHAVIOR_RESUME_DEVICE,
PECK_EIS_BEHAVIOR_SUSPEND_DEVICE,
@ -136,6 +141,7 @@ enum peck_ei_behavior {
PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH,
PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON,
PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL,
PECK_EI_BEHAVIOR_HANDLE_ADDED_STYLUS,
PECK_EI_BEHAVIOR_HANDLE_RESUMED,
PECK_EI_BEHAVIOR_HANDLE_PAUSED,
@ -148,6 +154,10 @@ enum peck_ei_behavior {
* Handle sync events. This behavior is enabled by default.
*/
PECK_EI_BEHAVIOR_HANDLE_SYNC,
/**
* Handle stylus capability events.
*/
PECK_EI_BEHAVIOR_HANDLE_STYLUS_CAPABILITIES,
};
struct peck;
@ -252,6 +262,9 @@ peck_eis_get_default_button(struct peck *peck);
struct eis_device *
peck_eis_get_default_scroll(struct peck *peck);
struct eis_device *
peck_eis_get_default_stylus(struct peck *peck);
struct ei_seat *
peck_ei_get_default_seat(struct peck *peck);
@ -273,6 +286,9 @@ peck_ei_get_default_pointer_absolute(struct peck *peck);
struct ei_device *
peck_ei_get_default_touch(struct peck *peck);
struct ei_device *
peck_ei_get_default_stylus(struct peck *peck);
uint64_t
peck_ei_now(struct peck *peck);

View file

@ -1503,6 +1503,143 @@ MUNIT_TEST(test_ei_device_multitouch)
return MUNIT_OK;
}
// FIXME: This may need to be updated once the ability to announce
// and bind capabilities is available.
MUNIT_TEST(test_ei_device_stylus)
{
_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_STYLUS);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
peck_dispatch_until_stable(peck);
with_client(peck) {
device = peck_ei_get_default_stylus(peck);
ei_device_stylus_proximity_in(device);
ei_device_frame(device, peck_ei_now(peck));
ei_device_stylus_motion(device, 0.1 , 0.2);
ei_device_frame(device, peck_ei_now(peck));
ei_device_stylus_pressure(device, 0.3);
ei_device_frame(device, peck_ei_now(peck));
ei_device_stylus_distance(device, 0.0);
ei_device_frame(device, peck_ei_now(peck));
ei_device_stylus_tilt(device, 0.4, 0.5);
ei_device_frame(device, peck_ei_now(peck));
ei_device_stylus_rotation(device, 0.6);
ei_device_frame(device, peck_ei_now(peck));
ei_device_stylus_airbrush_flow(device, 0.7);
ei_device_frame(device, peck_ei_now(peck));
ei_device_stylus_erase_start(device);
ei_device_frame(device, peck_ei_now(peck));
ei_device_stylus_erase_stop(device);
ei_device_frame(device, peck_ei_now(peck));
ei_device_stylus_proximity_out(device);
ei_device_frame(device, peck_ei_now(peck));
}
peck_dispatch_until_stable(peck);
with_server(peck) {
_unref_(eis_event) *proximity_in =
peck_eis_next_event(eis, EIS_EVENT_STYLUS_PROXIMITY_IN);
munit_assert_true(eis_event_stylus_get_is_prox(proximity_in));
_unref_(eis_event) *motion =
peck_eis_next_event(eis, EIS_EVENT_STYLUS_MOTION);
munit_assert_double_equal(eis_event_stylus_get_x(motion), 0.1, 2 /* precision */);
munit_assert_double_equal(eis_event_stylus_get_y(motion), 0.2, 2 /* precision */);
_unref_(eis_event) *pressure =
peck_eis_next_event(eis, EIS_EVENT_STYLUS_PRESSURE);
munit_assert_double_equal(eis_event_stylus_get_pressure(pressure), 0.3, 2 /* precision */);
_unref_(eis_event) *distance =
peck_eis_next_event(eis, EIS_EVENT_STYLUS_DISTANCE);
munit_assert_double_equal(eis_event_stylus_get_distance(distance), 0.0, 2 /* precision */);
_unref_(eis_event) *tilt =
peck_eis_next_event(eis, EIS_EVENT_STYLUS_TILT);
munit_assert_double_equal(eis_event_stylus_get_tilt_x(tilt), 0.4, 2 /* precision */);
munit_assert_double_equal(eis_event_stylus_get_tilt_y(tilt), 0.5, 2 /* precision */);
_unref_(eis_event) *rotation =
peck_eis_next_event(eis, EIS_EVENT_STYLUS_ROTATION);
munit_assert_double_equal(eis_event_stylus_get_rotation(rotation), 0.6, 2 /* precision */);
_unref_(eis_event) *airbrush_flow =
peck_eis_next_event(eis, EIS_EVENT_STYLUS_AIRBRUSH_FLOW);
munit_assert_double_equal(eis_event_stylus_get_flow(airbrush_flow), 0.7, 2 /* precision */);
_unref_(eis_event) *erase_start =
peck_eis_next_event(eis, EIS_EVENT_STYLUS_ERASE_START);
munit_assert_true(eis_event_stylus_get_is_erase(erase_start));
_unref_(eis_event) *erase_stop =
peck_eis_next_event(eis, EIS_EVENT_STYLUS_ERASE_STOP);
munit_assert_false(eis_event_stylus_get_is_erase(erase_stop));
_unref_(eis_event) *proximity_out =
peck_eis_next_event(eis, EIS_EVENT_STYLUS_PROXIMITY_OUT);
munit_assert_false(eis_event_stylus_get_is_prox(proximity_out));
peck_assert_no_eis_events(eis);
}
with_client(peck) {
/* We know our default device has one region */
struct ei_region *r = ei_device_get_region(device, 0);
uint32_t maxx = ei_region_get_x(r) + ei_region_get_width(r);
uint32_t maxy = ei_region_get_y(r) + ei_region_get_height(r);
/* outside of stylus range, expect to be discarded */
ei_device_stylus_motion(device, maxx + 1, maxy/2);
ei_device_frame(device, peck_ei_now(peck));
ei_device_stylus_motion(device, maxx/2 , maxy + 1);
ei_device_frame(device, peck_ei_now(peck));
}
peck_dispatch_until_stable(peck);
with_server(peck) {
peck_assert_no_eis_events(eis);
/* Don't auto-handle the DEVICE_CLOSED event */
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_SYNC);
}
with_client(peck) {
ei_device_close(device);
/* motion after remove must not trigger an event */
with_nonfatal_ei_bug(peck)
ei_device_stylus_motion(device, 100, 200);
}
peck_dispatch_until_stable(peck);
with_server(peck) {
_unref_(eis_event) *stop =
peck_eis_next_event(eis, EIS_EVENT_DEVICE_STOP_EMULATING);
_unref_(eis_event) *closed =
peck_eis_next_event(eis, EIS_EVENT_DEVICE_CLOSED);
peck_assert_no_eis_events(eis);
}
return MUNIT_OK;
}
#if HAVE_MEMFD_CREATE
MUNIT_TEST(test_ei_keymap_invalid)
{
@ -2893,6 +3030,153 @@ MUNIT_TEST(test_passive_ei_device_multitouch)
return MUNIT_OK;
}
// FIXME: This may need to be updated once the ability to announce
// and bind capabilities is available.
MUNIT_TEST(test_passive_ei_device_stylus)
{
_unref_(peck) *peck = peck_new_context("mode", 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_STYLUS);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES);
peck_dispatch_until_stable(peck);
uint32_t sequence = 123;
with_server(peck) {
struct eis_device *device = peck_eis_get_default_stylus(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_stylus(peck);
eis_device_stylus_proximity_in(device);
eis_device_frame(device, peck_eis_now(peck));
eis_device_stylus_motion(device, 0.1 , 0.2);
eis_device_frame(device, peck_eis_now(peck));
eis_device_stylus_pressure(device, 0.3);
eis_device_frame(device, peck_eis_now(peck));
eis_device_stylus_distance(device, 0.0);
eis_device_frame(device, peck_eis_now(peck));
eis_device_stylus_tilt(device, 0.4, 0.5);
eis_device_frame(device, peck_eis_now(peck));
eis_device_stylus_rotation(device, 0.6);
eis_device_frame(device, peck_eis_now(peck));
eis_device_stylus_airbrush_flow(device, 0.7);
eis_device_frame(device, peck_eis_now(peck));
eis_device_stylus_erase_start(device);
eis_device_frame(device, peck_eis_now(peck));
eis_device_stylus_erase_stop(device);
eis_device_frame(device, peck_eis_now(peck));
eis_device_stylus_proximity_out(device);
eis_device_frame(device, peck_eis_now(peck));
}
peck_dispatch_until_stable(peck);
with_client(peck) {
_unref_(ei_event) *proximity_in =
peck_ei_next_event(ei, EI_EVENT_STYLUS_PROXIMITY_IN);
munit_assert_true(ei_event_stylus_get_is_prox(proximity_in));
_unref_(ei_event) *motion =
peck_ei_next_event(ei, EI_EVENT_STYLUS_MOTION);
munit_assert_double_equal(ei_event_stylus_get_x(motion), 0.1, 2 /* precision */);
munit_assert_double_equal(ei_event_stylus_get_y(motion), 0.2, 2 /* precision */);
_unref_(ei_event) *pressure =
peck_ei_next_event(ei, EI_EVENT_STYLUS_PRESSURE);
munit_assert_double_equal(ei_event_stylus_get_pressure(pressure), 0.3, 2 /* precision */);
_unref_(ei_event) *distance =
peck_ei_next_event(ei, EI_EVENT_STYLUS_DISTANCE);
munit_assert_double_equal(ei_event_stylus_get_distance(distance), 0.0, 2 /* precision */);
_unref_(ei_event) *tilt =
peck_ei_next_event(ei, EI_EVENT_STYLUS_TILT);
munit_assert_double_equal(ei_event_stylus_get_tilt_x(tilt), 0.4, 2 /* precision */);
munit_assert_double_equal(ei_event_stylus_get_tilt_y(tilt), 0.5, 2 /* precision */);
_unref_(ei_event) *rotation =
peck_ei_next_event(ei, EI_EVENT_STYLUS_ROTATION);
munit_assert_double_equal(ei_event_stylus_get_rotation(rotation), 0.6, 2 /* precision */);
_unref_(ei_event) *airbrush_flow =
peck_ei_next_event(ei, EI_EVENT_STYLUS_AIRBRUSH_FLOW);
munit_assert_double_equal(ei_event_stylus_get_flow(airbrush_flow), 0.7, 2 /* precision */);
_unref_(ei_event) *erase_start =
peck_ei_next_event(ei, EI_EVENT_STYLUS_ERASE_START);
munit_assert_true(ei_event_stylus_get_is_erase(erase_start));
_unref_(ei_event) *erase_stop =
peck_ei_next_event(ei, EI_EVENT_STYLUS_ERASE_STOP);
munit_assert_false(ei_event_stylus_get_is_erase(erase_stop));
_unref_(ei_event) *proximity_out =
peck_ei_next_event(ei, EI_EVENT_STYLUS_PROXIMITY_OUT);
munit_assert_false(ei_event_stylus_get_is_prox(proximity_out));
peck_assert_no_ei_events(ei);
}
with_server(peck) {
/* We know our default device has one region */
struct eis_region *r = eis_device_get_region(device, 0);
uint32_t maxx = eis_region_get_x(r) + eis_region_get_width(r);
uint32_t maxy = eis_region_get_y(r) + eis_region_get_height(r);
/* outside of stylus range, expect to be discarded */
eis_device_stylus_motion(device, maxx + 1, maxy/2);
eis_device_frame(device, peck_eis_now(peck));
eis_device_stylus_motion(device, maxx/2 , maxy + 1);
eis_device_frame(device, peck_eis_now(peck));
}
peck_dispatch_until_stable(peck);
with_client(peck) {
peck_assert_no_ei_events(ei);
}
with_server(peck) {
eis_device_remove(device);
/* motion after remove must not trigger an event */
eis_device_stylus_motion(device, 100, 200);
}
peck_dispatch_until_stable(peck);
with_client(peck) {
_unref_(ei_event) *stop =
peck_ei_next_event(ei, EI_EVENT_DEVICE_STOP_EMULATING);
_unref_(ei_event) *closed =
peck_ei_next_event(ei, EI_EVENT_DEVICE_REMOVED);
peck_assert_no_ei_events(ei);
}
return MUNIT_OK;
}
MUNIT_TEST(test_passive_ei_frame_timestamp)
{
_unref_(peck) *peck = peck_new_context("mode", PECK_EI_RECEIVER);

View file

@ -295,6 +295,29 @@ print_pong_event(struct ei_event *event)
printf(" id: %#" PRIx64 "\n", ei_ping_get_id(ping));
}
static void
print_stylus_event(struct ei_event *event)
{
print_device(event);
bool down = ei_event_stylus_get_is_down(event);
bool prox = ei_event_stylus_get_is_prox(event);
bool erase = ei_event_stylus_get_is_erase(event);
uint32_t capability = ei_event_stylus_get_capability(event);
float x = ei_event_stylus_get_x(event);
float y = ei_event_stylus_get_y(event);
float p = ei_event_stylus_get_pressure(event);
float d = ei_event_stylus_get_distance(event);
float r = ei_event_stylus_get_rotation(event);
float s = ei_event_stylus_get_flow(event);
float tilt_x = ei_event_stylus_get_tilt_x(event);
float tilt_y = ei_event_stylus_get_tilt_y(event);
printf(" stylus: [down - %s, prox - %s, erase - %s]\n[x - %f, y - %f, p - %f, d - %f, r - %f, s - %f, tilt_x - %f, tilt_y - %f]\n[Capability - %u]", truefalse(down), truefalse(prox), truefalse(erase), x, y, p, d, r, s, tilt_x, tilt_y, capability);
}
int main(int argc, char **argv)
{
enum {
@ -455,6 +478,21 @@ int main(int argc, char **argv)
case EI_EVENT_PONG:
print_pong_event(e);
break;
case EI_EVENT_STYLUS_TOOL_CAPABILITIES:
case EI_EVENT_STYLUS_PROXIMITY_IN:
case EI_EVENT_STYLUS_PROXIMITY_OUT:
case EI_EVENT_STYLUS_ERASE_START:
case EI_EVENT_STYLUS_ERASE_STOP:
case EI_EVENT_STYLUS_DOWN:
case EI_EVENT_STYLUS_UP:
case EI_EVENT_STYLUS_MOTION:
case EI_EVENT_STYLUS_PRESSURE:
case EI_EVENT_STYLUS_DISTANCE:
case EI_EVENT_STYLUS_TILT:
case EI_EVENT_STYLUS_ROTATION:
case EI_EVENT_STYLUS_AIRBRUSH_FLOW:
print_stylus_event(e);
break;
case EI_EVENT_SYNC:
break;
}

View file

@ -300,12 +300,14 @@ int main(int argc, char **argv)
_unref_(ei_device) *kbd = NULL;
_unref_(ei_device) *abs = NULL;
_unref_(ei_device) *touch = NULL;
_unref_(ei_device) *stylus = NULL;
bool stop = false;
bool have_ptr = false;
bool have_kbd = false;
bool have_abs = false;
bool have_touch = false;
bool have_stylus = false;
struct ei_seat *default_seat = NULL;
uint32_t sequence = 0;
@ -344,7 +346,8 @@ int main(int argc, char **argv)
EI_DEVICE_CAP_POINTER_ABSOLUTE,
EI_DEVICE_CAP_TOUCH,
EI_DEVICE_CAP_BUTTON,
EI_DEVICE_CAP_SCROLL, NULL);
EI_DEVICE_CAP_SCROLL,
EI_DEVICE_CAP_STYLUS, NULL);
break;
}
case EI_EVENT_SEAT_REMOVED:
@ -376,6 +379,11 @@ int main(int argc, char **argv)
touch = ei_device_ref(device);
handle_regions(device);
}
if (ei_device_has_capability(device, EI_DEVICE_CAP_STYLUS)) {
colorprint("New stylus device: %s\n", ei_device_get_name(device));
stylus = ei_device_ref(device);
handle_regions(device);
}
}
break;
case EI_EVENT_DEVICE_RESUMED:
@ -403,6 +411,12 @@ int main(int argc, char **argv)
colorprint("Touch device was resumed\n");
have_touch = true;
}
if (ei_event_get_device(e) == stylus) {
if (!receiver)
ei_device_start_emulating(stylus, ++sequence);
colorprint("Stylus device was resumed\n");
have_stylus = true;
}
break;
case EI_EVENT_DEVICE_PAUSED:
if (ei_event_get_device(e) == ptr) {
@ -421,6 +435,10 @@ int main(int argc, char **argv)
colorprint("Touch device was paused\n");
have_touch = false;
}
if (ei_event_get_device(e) == stylus) {
colorprint("Stylus device was paused\n");
have_stylus = false;
}
break;
case EI_EVENT_DEVICE_REMOVED:
{
@ -498,6 +516,71 @@ int main(int argc, char **argv)
colorprint("touch up %u\n", ei_event_touch_get_id(e));
}
break;
case EI_EVENT_STYLUS_PROXIMITY_IN:
case EI_EVENT_STYLUS_PROXIMITY_OUT:
{
colorprint("stylus proximity %s\n",
ei_event_get_type(e) == EI_EVENT_STYLUS_PROXIMITY_OUT ? "out" : "in");
}
break;
case EI_EVENT_STYLUS_DOWN:
case EI_EVENT_STYLUS_UP:
{
colorprint("stylus %s\n",
ei_event_get_type(e) == EI_EVENT_STYLUS_DOWN ? "down" : "up");
}
break;
case EI_EVENT_STYLUS_TOOL_CAPABILITIES:
{
colorprint("stylus capability 0x%08x\n",
ei_event_stylus_get_capability(e));
}
break;
case EI_EVENT_STYLUS_ERASE_START:
case EI_EVENT_STYLUS_ERASE_STOP:
{
colorprint("stylus erase %s\n",
ei_event_get_type(e) == EI_EVENT_STYLUS_ERASE_STOP ? "stop" : "start");
}
break;
case EI_EVENT_STYLUS_PRESSURE:
{
colorprint("stylus pressure %.2f\n",
ei_event_stylus_get_pressure(e));
}
break;
case EI_EVENT_STYLUS_DISTANCE:
{
colorprint("stylus distance %.2f\n",
ei_event_stylus_get_distance(e));
}
break;
case EI_EVENT_STYLUS_ROTATION:
{
colorprint("stylus rotation %.2f\n",
ei_event_stylus_get_rotation(e));
}
break;
case EI_EVENT_STYLUS_AIRBRUSH_FLOW:
{
colorprint("stylus airbrush %.2f\n",
ei_event_stylus_get_flow(e));
}
break;
case EI_EVENT_STYLUS_TILT:
{
colorprint("stylus tilt | x - %.2f y - %.2f\n",
ei_event_stylus_get_tilt_x(e),
ei_event_stylus_get_tilt_y(e));
}
break;
case EI_EVENT_STYLUS_MOTION:
{
colorprint("stylus motion | x - %.2f y - %.2f\n",
ei_event_stylus_get_x(e),
ei_event_stylus_get_y(e));
}
break;
case EI_EVENT_SYNC:
{
colorprint("sync\n");
@ -585,6 +668,78 @@ int main(int argc, char **argv)
}
}
if (have_stylus) {
static int counter = 0;
switch (counter++ % 13) {
case 0:
colorprint("sending stylus prox in event\n");
ei_device_stylus_proximity_in(stylus);
ei_device_frame(stylus, now);
break;
case 1:
break;
case 2:
colorprint("sending stylus down event\n");
ei_device_stylus_down(stylus);
ei_device_frame(stylus, now);
break;
case 3:
colorprint("sending stylus motion event\n");
ei_device_stylus_motion(stylus, 10, 10);
ei_device_frame(stylus, now);
break;
case 4:
colorprint("sending stylus tilt event\n");
ei_device_stylus_tilt(stylus, 15, 15);
ei_device_frame(stylus, now);
break;
case 5:
colorprint("sending stylus pressure event\n");
ei_device_stylus_pressure(stylus, 20);
ei_device_frame(stylus, now);
break;
case 6:
colorprint("sending stylus distance event\n");
ei_device_stylus_distance(stylus, 25);
ei_device_frame(stylus, now);
break;
case 7:
colorprint("sending stylus rotation event\n");
ei_device_stylus_rotation(stylus, 5);
ei_device_frame(stylus, now);
break;
case 8:
colorprint("sending stylus airbrush event\n");
ei_device_stylus_airbrush_flow(stylus, 30);
ei_device_frame(stylus, now);
break;
case 9:
colorprint("sending stylus erase start event\n");
ei_device_stylus_erase_start(stylus);
ei_device_frame(stylus, now);
break;
case 10:
colorprint("sending stylus erase stop event\n");
ei_device_stylus_erase_stop(stylus);
ei_device_frame(stylus, now);
break;
case 11:
colorprint("sending stylus up event\n");
ei_device_stylus_up(stylus);
ei_device_frame(stylus, now);
break;
case 12:
colorprint("sending stylus prox out event\n");
ei_device_stylus_proximity_out(stylus);
ei_device_frame(stylus, now);
break;
default:
break;
}
}
}
}
@ -597,13 +752,16 @@ int main(int argc, char **argv)
ei_device_close(abs);
if (touch)
ei_device_close(touch);
if (stylus)
ei_device_close(stylus);
if (default_seat) {
ei_seat_bind_capabilities(default_seat, EI_DEVICE_CAP_POINTER,
EI_DEVICE_CAP_KEYBOARD,
EI_DEVICE_CAP_POINTER_ABSOLUTE,
EI_DEVICE_CAP_TOUCH,
EI_DEVICE_CAP_BUTTON,
EI_DEVICE_CAP_SCROLL, NULL);
EI_DEVICE_CAP_SCROLL,
EI_DEVICE_CAP_STYLUS, NULL);
ei_seat_unref(default_seat);
}

View file

@ -38,6 +38,7 @@
*/
#include "config.h"
#include "libeis.h"
#include <assert.h>
#include <errno.h>
@ -124,6 +125,7 @@ eis_demo_client_destroy(struct eis_demo_client *democlient)
eis_device_unref(democlient->abs);
eis_device_unref(democlient->kbd);
eis_device_unref(democlient->touchscreen);
eis_device_unref(democlient->stylus);
}
static
@ -315,6 +317,22 @@ add_device(struct eis_demo_server *server, struct eis_client *client,
device = steal(&touchscreen);
break;
}
case EIS_DEVICE_CAP_STYLUS:
{
struct eis_device *stylus = eis_seat_new_device(seat);
eis_device_configure_name(stylus, "test stylus");
eis_device_configure_capability(stylus, EIS_DEVICE_CAP_STYLUS);
_unref_(eis_region) *region = eis_device_new_region(stylus);
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);
colorprint("Creating stylus device %s for %s\n", eis_device_get_name(stylus),
eis_client_get_name(client));
eis_device_add(stylus);
device = steal(&stylus);
break;
}
case EIS_DEVICE_CAP_BUTTON:
case EIS_DEVICE_CAP_SCROLL:
/* Mixed in with pointer/abs - good enough for a demo server */
@ -371,6 +389,7 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server,
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_TOUCH);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_BUTTON);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_SCROLL);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_STYLUS);
eis_seat_add(seat);
/* Note: we don't have a ref to this seat ourselves anywhere */
break;
@ -436,12 +455,23 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server,
}
}
if (eis_event_seat_has_capability(e, EIS_DEVICE_CAP_STYLUS)) {
if (!democlient->stylus)
democlient->stylus = add_device(server, client, seat, EIS_DEVICE_CAP_STYLUS);
} else {
if (democlient->stylus) {
eis_device_remove(democlient->stylus);
democlient->stylus = eis_device_unref(democlient->stylus);
}
}
/* Special "Feature", if all caps are unbound remove the seat.
* This is a demo server after all, so let's demo this. */
if (!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_POINTER) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_POINTER_ABSOLUTE) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_KEYBOARD) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TOUCH))
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TOUCH) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_STYLUS))
eis_seat_remove(seat);
break;
@ -479,6 +509,9 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server,
if (democlient->touchscreen == device)
democlient->touchscreen = NULL;
if (democlient->stylus == device)
democlient->stylus = NULL;
eis_device_unref(device);
}
break;
@ -551,6 +584,71 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server,
colorprint("touch up %u\n", eis_event_touch_get_id(e));
}
break;
case EIS_EVENT_STYLUS_PROXIMITY_IN:
case EIS_EVENT_STYLUS_PROXIMITY_OUT:
{
colorprint("stylus proximity %s\n",
eis_event_get_type(e) == EIS_EVENT_STYLUS_PROXIMITY_OUT ? "out" : "in");
}
break;
case EIS_EVENT_STYLUS_DOWN:
case EIS_EVENT_STYLUS_UP:
{
colorprint("stylus %s\n",
eis_event_get_type(e) == EIS_EVENT_STYLUS_DOWN ? "down" : "up");
}
break;
case EIS_EVENT_STYLUS_BIND_TOOL_CAPABILITIES:
{
colorprint("stylus bind capability 0x%08x\n",
eis_event_stylus_get_capability(e));
}
break;
case EIS_EVENT_STYLUS_ERASE_START:
case EIS_EVENT_STYLUS_ERASE_STOP:
{
colorprint("stylus erase %s\n",
eis_event_get_type(e) == EIS_EVENT_STYLUS_ERASE_STOP ? "stop" : "start");
}
break;
case EIS_EVENT_STYLUS_PRESSURE:
{
colorprint("stylus pressure %.2f\n",
eis_event_stylus_get_pressure(e));
}
break;
case EIS_EVENT_STYLUS_DISTANCE:
{
colorprint("stylus distance %.2f\n",
eis_event_stylus_get_distance(e));
}
break;
case EIS_EVENT_STYLUS_ROTATION:
{
colorprint("stylus rotation %.2f\n",
eis_event_stylus_get_rotation(e));
}
break;
case EIS_EVENT_STYLUS_AIRBRUSH_FLOW:
{
colorprint("stylus airbrush %.2f\n",
eis_event_stylus_get_flow(e));
}
break;
case EIS_EVENT_STYLUS_TILT:
{
colorprint("stylus tilt | x - %.2f y - %.2f\n",
eis_event_stylus_get_tilt_x(e),
eis_event_stylus_get_tilt_y(e));
}
break;
case EIS_EVENT_STYLUS_MOTION:
{
colorprint("stylus motion | x - %.2f y - %.2f\n",
eis_event_stylus_get_x(e),
eis_event_stylus_get_y(e));
}
break;
case EIS_EVENT_FRAME:
{
colorprint("frame timestamp: %" PRIu64 "\n",
@ -744,6 +842,7 @@ int main(int argc, char **argv)
struct eis_device *kbd = democlient->kbd;
struct eis_device *abs = democlient->abs;
struct eis_device *touchscreen = democlient->touchscreen;
struct eis_device *stylus = democlient->stylus;
if (ptr) {
colorprint("sending motion event\n");
eis_device_pointer_motion(ptr, -1, 1);
@ -816,6 +915,102 @@ int main(int argc, char **argv)
}
}
if (stylus) {
static int counter = 0;
switch (counter++ % 13) {
case 0:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus prox in\n");
eis_device_stylus_proximity_in(stylus);
eis_device_frame(stylus, now);
}
break;
case 1:
break;
case 2:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus down\n");
eis_device_stylus_down(stylus);
eis_device_frame(stylus, now);
}
break;
case 3:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus motion\n");
eis_device_stylus_motion(stylus, 10, 10);
eis_device_frame(stylus, now);
}
break;
case 4:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus tilt\n");
eis_device_stylus_tilt(stylus, 15, 15);
eis_device_frame(stylus, now);
}
break;
case 5:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus pressure\n");
eis_device_stylus_pressure(stylus, 5);
eis_device_frame(stylus, now);
}
break;
case 6:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus distance\n");
eis_device_stylus_distance(stylus, 20);
eis_device_frame(stylus, now);
}
break;
case 7:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus rotation\n");
eis_device_stylus_rotation(stylus, 25);
eis_device_frame(stylus, now);
}
break;
case 8:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus airbrush\n");
eis_device_stylus_airbrush_flow(stylus, 30);
eis_device_frame(stylus, now);
}
break;
case 9:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus erase start\n");
eis_device_stylus_erase_start(stylus);
eis_device_frame(stylus, now);
}
break;
case 10:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus erase stop\n");
eis_device_stylus_erase_stop(stylus);
eis_device_frame(stylus, now);
}
break;
case 11:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus up\n");
eis_device_stylus_up(stylus);
eis_device_frame(stylus, now);
}
break;
case 12:
if (stylus) { /* NULL if client was disconnected internally already */
colorprint("sending stylus prox out\n");
eis_device_stylus_proximity_out(stylus);
eis_device_frame(stylus, now);
}
break;
default:
break;
}
}
}
}

View file

@ -40,6 +40,7 @@ struct eis_demo_client {
struct eis_device *abs;
struct eis_device *touchscreen;
struct eis_touch *touch;
struct eis_device *stylus;
};
struct eis_demo_server {