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 {