diff --git a/proto/protocol.xml b/proto/protocol.xml
index 7266aa4..caf6f39 100644
--- a/proto/protocol.xml
+++ b/proto/protocol.xml
@@ -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 @@
+
+
+
+ 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
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+ Notification of the relative amount of pressure exerted on the stylus.
+
+ The valid range for pressure is `0.0 <= pressure <= 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 > 0.0`
+ > when sending ei_stylus.down. The EIS implementation may correct
+ > illegal values and/or disconnect the client.
+
+
+
+
+
+
+ Notification of the relative distance between the stylus and its
+ digitizer.
+
+ The valid range for distance is `0.0 <= distance <= 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.
+
+
+
+
+
+
+ Notification of the stylus's tilt angles in degrees.
+
+ The valid range for each tilt angle is `-90.0 <= tilt_[xy] <=
+ +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.
+
+
+
+
+
+
+
+ Notification of the stylus's barrel rotation angle in degrees.
+
+ The valid range for barrel rotation is `0.0 <= rotation < 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.
+
+
+
+
+
+
+ Notification of the relative position of an airbrush stylus's flow
+ control.
+
+ The valid range for airbrush flow is `0.0 <= flow <= 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.
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ See the ei_stylus.proximity_in request for more details.
+
+
+
+
+
+ See the ei_stylus.proximity_out request for more details.
+
+
+
+
+
+ See the ei_stylus.erase_start request for more details.
+
+
+
+
+
+ See the ei_stylus.erase_stop request for more details.
+
+
+
+
+
+ See the ei_stylus.down request for more details.
+
+
+
+
+
+ See the ei_stylus.up request for more details.
+
+
+
+
+
+ See the ei_stylus.motion request for more details.
+
+
+
+
+
+
+
+ See the ei_stylus.pressure request for more details.
+
+
+
+
+
+
+ See the ei_stylus.distance request for more details.
+
+
+
+
+
+
+ See the ei_stylus.tilt request for more details.
+
+
+
+
+
+
+
+ See the ei_stylus.rotation request for more details.
+
+
+
+
+
+
+ See the ei_stylus.airbrush_flow request for more details.
+
+
+
+
diff --git a/src/libei-device.c b/src/libei-device.c
index 607160b..6721cdb 100644
--- a/src/libei-device.c
+++ b/src/libei-device.c
@@ -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)
{
diff --git a/src/libei-device.h b/src/libei-device.h
index 0679a12..73c622f 100644
--- a/src/libei-device.h
+++ b/src/libei-device.h
@@ -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*);
diff --git a/src/libei-event.c b/src/libei-event.c
index c3cc53d..bf8cf27 100644
--- a/src/libei-event.c
+++ b/src/libei-event.c
@@ -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;
diff --git a/src/libei-event.h b/src/libei-event.h
index 8f011f7..ef3fc98 100644
--- a/src/libei-event.h
+++ b/src/libei-event.h
@@ -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;
};
};
diff --git a/src/libei-handshake.c b/src/libei-handshake.c
index f157690..c76acde 100644
--- a/src/libei-handshake.c
+++ b/src/libei-handshake.c
@@ -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);
diff --git a/src/libei-private.h b/src/libei-private.h
index 9fec25b..55d97ee 100644
--- a/src/libei-private.h
+++ b/src/libei-private.h
@@ -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,
diff --git a/src/libei-seat.c b/src/libei-seat.c
index 9d2e49b..23356ac 100644
--- a/src/libei-seat.c
+++ b/src/libei-seat.c
@@ -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;
diff --git a/src/libei-stylus.c b/src/libei-stylus.c
new file mode 100644
index 0000000..91a112d
--- /dev/null
+++ b/src/libei-stylus.c
@@ -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
+#include
+
+#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 */
+}
diff --git a/src/libei-stylus.h b/src/libei-stylus.h
new file mode 100644
index 0000000..85cdc71
--- /dev/null
+++ b/src/libei-stylus.h
@@ -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);
diff --git a/src/libei.c b/src/libei.c
index 6dcbfa7..3582439 100644
--- a/src/libei.c
+++ b/src/libei.c
@@ -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)
{
diff --git a/src/libei.h b/src/libei.h
index 151b24e..8b3cd74 100644
--- a/src/libei.h
+++ b/src/libei.h
@@ -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);
+
/**
* @}
*/
diff --git a/src/libeis-client.c b/src/libeis-client.c
index f0f7a67..b35a3c4 100644
--- a/src/libeis-client.c
+++ b/src/libeis-client.c
@@ -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);
diff --git a/src/libeis-client.h b/src/libeis-client.h
index 14b4833..0c37b1a 100644
--- a/src/libeis-client.h
+++ b/src/libeis-client.h
@@ -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 {
diff --git a/src/libeis-device.c b/src/libeis-device.c
index 514ef12..d440a40 100644
--- a/src/libeis-device.c
+++ b/src/libeis-device.c
@@ -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);
+}
diff --git a/src/libeis-device.h b/src/libeis-device.h
index 5f6f2a1..0369c23 100644
--- a/src/libeis-device.h
+++ b/src/libeis-device.h
@@ -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,
diff --git a/src/libeis-event.c b/src/libeis-event.c
index 125dc51..fd628d1 100644
--- a/src/libeis-event.c
+++ b/src/libeis-event.c
@@ -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;
+}
diff --git a/src/libeis-event.h b/src/libeis-event.h
index 9754d22..73bfc6d 100644
--- a/src/libeis-event.h
+++ b/src/libeis-event.h
@@ -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;
};
};
diff --git a/src/libeis-handshake.c b/src/libeis-handshake.c
index 39367b6..875190a 100644
--- a/src/libeis-handshake.c
+++ b/src/libeis-handshake.c
@@ -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
};
diff --git a/src/libeis-private.h b/src/libeis-private.h
index b3634ef..ed067f0 100644
--- a/src/libeis-private.h
+++ b/src/libeis-private.h
@@ -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,
diff --git a/src/libeis-seat.c b/src/libeis-seat.c
index bca387e..3f27422 100644
--- a/src/libeis-seat.c
+++ b/src/libeis-seat.c
@@ -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;
diff --git a/src/libeis-stylus.c b/src/libeis-stylus.c
new file mode 100644
index 0000000..39111ac
--- /dev/null
+++ b/src/libeis-stylus.c
@@ -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
+#include
+
+#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 */
+}
diff --git a/src/libeis-stylus.h b/src/libeis-stylus.h
new file mode 100644
index 0000000..af16500
--- /dev/null
+++ b/src/libeis-stylus.h
@@ -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);
diff --git a/src/libeis.c b/src/libeis.c
index a83d465..0c7ed93 100644
--- a/src/libeis.c
+++ b/src/libeis.c
@@ -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)
{
diff --git a/src/libeis.h b/src/libeis.h
index 17b2619..54e159c 100644
--- a/src/libeis.h
+++ b/src/libeis.h
@@ -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().
diff --git a/src/meson.build b/src/meson.build
index 583a2fb..0f191e1 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -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]
diff --git a/test/eierpecken.c b/test/eierpecken.c
index e8fdd8d..172a119 100644
--- a/test/eierpecken.c
+++ b/test/eierpecken.c
@@ -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");
diff --git a/test/eierpecken.h b/test/eierpecken.h
index ad34fac..3b7a98c 100644
--- a/test/eierpecken.h
+++ b/test/eierpecken.h
@@ -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);
diff --git a/test/test-ei-device.c b/test/test-ei-device.c
index 261de35..dacf8fd 100644
--- a/test/test-ei-device.c
+++ b/test/test-ei-device.c
@@ -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);
diff --git a/tools/ei-debug-events.c b/tools/ei-debug-events.c
index 02bfeb8..c1f0d5d 100644
--- a/tools/ei-debug-events.c
+++ b/tools/ei-debug-events.c
@@ -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;
}
diff --git a/tools/ei-demo-client.c b/tools/ei-demo-client.c
index 45114cc..d76c737 100644
--- a/tools/ei-demo-client.c
+++ b/tools/ei-demo-client.c
@@ -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);
}
diff --git a/tools/eis-demo-server.c b/tools/eis-demo-server.c
index 5f19c5f..4777f5d 100644
--- a/tools/eis-demo-server.c
+++ b/tools/eis-demo-server.c
@@ -38,6 +38,7 @@
*/
#include "config.h"
+#include "libeis.h"
#include
#include
@@ -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;
+ }
+
+ }
}
}
diff --git a/tools/eis-demo-server.h b/tools/eis-demo-server.h
index 18a1bac..1ff8147 100644
--- a/tools/eis-demo-server.h
+++ b/tools/eis-demo-server.h
@@ -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 {