mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2025-12-30 10:40:09 +01:00
Merge branch 'wip/keysyms' into 'main'
Draft: RFC: Add a new text interface for sending keysyms and utf8 text Closes #73 and #83 See merge request libinput/libei!355
This commit is contained in:
commit
582ec4ef98
32 changed files with 1232 additions and 8 deletions
|
|
@ -781,6 +781,7 @@
|
|||
- "ei_button"
|
||||
- "ei_keyboard"
|
||||
- "ei_touchscreen"
|
||||
- "ei_text"
|
||||
The interface version is equal or less to the client-supported
|
||||
version in ei_handshake.interface_version for the respective interface.
|
||||
|
||||
|
|
@ -1584,4 +1585,109 @@
|
|||
<arg name="touchid" type="uint32"/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="ei_text" version="1">
|
||||
<description summary="text object">
|
||||
Interface for text-based requests and events.
|
||||
|
||||
This interface is only provided once per device and where a client
|
||||
requests ei_text.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 this interface in ei_handshake.interface_version.
|
||||
</description>
|
||||
|
||||
<!-- ei_text client requests version 1 -->
|
||||
|
||||
<request name="release" since="1">
|
||||
<description summary="text removal request">
|
||||
Notification that the client is no longer interested in this text interface object.
|
||||
The EIS implementation will release any resources related to this object and
|
||||
send the ei_text.destroyed event once complete.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="keysym" since="1" context-type="sender">
|
||||
<description summary="Keysym state change request">
|
||||
Generate an XKB key sym event.
|
||||
|
||||
It is a client bug to send more than one key request for the same keysym
|
||||
within the same ei_device.frame and the EIS implementation
|
||||
may ignore either or all keysym state changes and/or disconnect the client.
|
||||
|
||||
It is a protocol violation to send this request for a client
|
||||
of an ei_handshake.context_type other than sender.
|
||||
|
||||
It is a protocol violation to send this request in the same frame
|
||||
as a ei_keyboard.key.
|
||||
</description>
|
||||
<arg name="keysym" type="uint32" summary="the key sym"/>
|
||||
<arg name="state" type="uint32" enum="ei_keyboard.key_state" summary="logical state of the keysym"/>
|
||||
</request>
|
||||
|
||||
<request name="utf8" since="1" context-type="sender">
|
||||
<description summary="UTF8 text">
|
||||
Generate a UTF-8 compatible text string.
|
||||
|
||||
It is a protocol violation to send this request for a client
|
||||
of an ei_handshake.context_type other than sender.
|
||||
|
||||
It is a protocol violation to send more than one utf8 request in the same
|
||||
frame.
|
||||
|
||||
The order of ei_keyboard.key or ei_text.keysym and ei_text.utf8 if sent
|
||||
within the same frame is undefined.
|
||||
</description>
|
||||
<arg name="text" type="string" summary="the utf-8 compatible text"/>
|
||||
</request>
|
||||
|
||||
<!-- ei_text events version 1 -->
|
||||
|
||||
<event name="destroyed" type="destructor" since="1">
|
||||
<description summary="Text interface object removal notification">
|
||||
This text interface object has been removed and a client should release all
|
||||
associated resources.
|
||||
|
||||
This ei_text object will be destroyed by the EIS implementation immediately after
|
||||
after this event is sent and as such the client must not attempt to use
|
||||
it after that point.
|
||||
</description>
|
||||
<arg name="serial" type="uint32" summary="this event's serial number"/>
|
||||
</event>
|
||||
|
||||
<event name="keysym" since="1" context-type="receiver">
|
||||
<description summary="Keysym state change event">
|
||||
See the ei_text.keysym request for details.
|
||||
|
||||
It is a protocol violation to send this request for a client
|
||||
of an ei_handshake.context_type other than receiver.
|
||||
|
||||
It is a protocol violation to send a keysym down event in the same
|
||||
frame as a key up event for the same keysym in the same frame.
|
||||
|
||||
It is a protocol violation to send this event in the same frame
|
||||
as a ei_keyboard.key event.
|
||||
</description>
|
||||
<arg name="keysym" type="uint32"/>
|
||||
<arg name="state" type="uint32" enum="ei_keyboard.key_state"/>
|
||||
</event>
|
||||
|
||||
<event name="utf8" since="1" context-type="receiver">
|
||||
<description summary="UTF8 text event">
|
||||
See the ei_text.utf8 request for details.
|
||||
|
||||
It is a protocol violation to send this request for a client
|
||||
of an ei_handshake.context_type other than receiver.
|
||||
|
||||
It is a protocol violation to send more than one utf8 event in the same
|
||||
frame.
|
||||
|
||||
The order of ei_keyboard.key, ei_text.keysym, and ei_text.utf8 if sent
|
||||
within the same frame is undefined.
|
||||
</description>
|
||||
<arg name="text" type="string" summary="the utf-8 compatible text"/>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
||||
|
|
|
|||
|
|
@ -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_text_unref(device->text);
|
||||
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->text)
|
||||
mask_add(device->capabilities, EI_DEVICE_CAP_TEXT);
|
||||
|
||||
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_TEXT)) {
|
||||
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_TEXT) ? "x" : "",
|
||||
ei_seat_get_name(ei_device_get_seat(device)));
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -466,6 +471,12 @@ 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_TEXT_INTERFACE_NAME)) {
|
||||
DISCONNECT_IF_INVALID_VERSION(ei, ei_text, id, version);
|
||||
if (device->text)
|
||||
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
|
||||
"Duplicate ei_text interface object on device");
|
||||
device->text = ei_text_new(device, id, version);
|
||||
} else {
|
||||
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
|
||||
"Unsupported interface '%s' on device", name);
|
||||
|
|
@ -875,7 +886,8 @@ handle_msg_touch_cancel(struct ei_touchscreen *touchscreen, uint32_t touchid)
|
|||
}
|
||||
|
||||
struct ei *ei = ei_device_get_context(device);
|
||||
if (ei->interface_versions.ei_touchscreen < EI_TOUCHSCREEN_EVENT_CANCEL_SINCE_VERSION) {
|
||||
if (ei->interface_versions.ei_touchscreen <
|
||||
EI_TOUCHSCREEN_EVENT_CANCEL_SINCE_VERSION) {
|
||||
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
|
||||
"Touch cancel event for touchscreen version v1");
|
||||
}
|
||||
|
|
@ -913,6 +925,75 @@ ei_device_get_touchscreen_interface(struct ei_device *device)
|
|||
return &touchscreen_interface;
|
||||
}
|
||||
|
||||
static struct brei_result *
|
||||
handle_msg_text_destroy(struct ei_text *text, uint32_t serial)
|
||||
{
|
||||
struct ei *ei = ei_text_get_context(text);
|
||||
ei_update_serial(ei, serial);
|
||||
|
||||
struct ei_device *device = ei_text_get_device(text);
|
||||
ei_text_unref(steal(&device->text));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct brei_result *
|
||||
handle_msg_text_keysym(struct ei_text *text, uint32_t keysym, uint32_t state)
|
||||
{
|
||||
struct ei_device *device = ei_text_get_device(text);
|
||||
|
||||
DISCONNECT_IF_SENDER_CONTEXT(device);
|
||||
|
||||
if (!ei_device_has_capability(device, EI_DEVICE_CAP_TEXT)) {
|
||||
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
|
||||
"keysym event for non-text device");
|
||||
}
|
||||
|
||||
if (device->state == EI_DEVICE_STATE_EMULATING) {
|
||||
ei_queue_text_keysym_event(device, keysym, !!state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return maybe_error_on_device_state(device, "text keysym");
|
||||
}
|
||||
|
||||
static struct brei_result *
|
||||
handle_msg_text_utf8(struct ei_text *text, const char *utf8)
|
||||
{
|
||||
struct ei_device *device = ei_text_get_device(text);
|
||||
|
||||
DISCONNECT_IF_SENDER_CONTEXT(device);
|
||||
|
||||
if (!ei_device_has_capability(device, EI_DEVICE_CAP_TEXT)) {
|
||||
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
|
||||
"utf8 event for non-text device");
|
||||
}
|
||||
|
||||
if (!utf8) {
|
||||
return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
|
||||
"utf8 text is NULL");
|
||||
}
|
||||
|
||||
if (device->state == EI_DEVICE_STATE_EMULATING) {
|
||||
ei_queue_text_utf8_event(device, utf8);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return maybe_error_on_device_state(device, "text utf8");
|
||||
}
|
||||
|
||||
static const struct ei_text_interface text_interface = {
|
||||
.destroyed = handle_msg_text_destroy,
|
||||
.keysym = handle_msg_text_keysym,
|
||||
.utf8 = handle_msg_text_utf8,
|
||||
};
|
||||
|
||||
const struct ei_text_interface *
|
||||
ei_device_get_text_interface(struct ei_device *device)
|
||||
{
|
||||
return &text_interface;
|
||||
}
|
||||
|
||||
struct ei_device *
|
||||
ei_device_new(struct ei_seat *seat, object_id_t deviceid, uint32_t version)
|
||||
{
|
||||
|
|
@ -1194,6 +1275,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_TEXT:
|
||||
return mask_all(device->capabilities, cap);
|
||||
}
|
||||
return false;
|
||||
|
|
@ -1812,6 +1894,89 @@ ei_touch_cancel(struct ei_touch *touch)
|
|||
ei_send_touch_up(touch->device, touch->tracking_id);
|
||||
}
|
||||
|
||||
static int
|
||||
ei_send_text_keysym(struct ei_device *device, uint32_t keysym, bool is_press)
|
||||
{
|
||||
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_text_request_keysym(device->text, keysym, is_press);
|
||||
if (rc)
|
||||
ei_disconnect(ei);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
ei_send_text_utf8(struct ei_device *device, const char *utf8)
|
||||
{
|
||||
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_text_request_utf8(device->text, utf8);
|
||||
if (rc)
|
||||
ei_disconnect(ei);
|
||||
return rc;
|
||||
}
|
||||
|
||||
_public_ void
|
||||
ei_device_text_keysym(struct ei_device *device,
|
||||
uint32_t keysym, bool is_press)
|
||||
{
|
||||
if (!ei_device_has_capability(device, EI_DEVICE_CAP_TEXT)) {
|
||||
log_bug_client(ei_device_get_context(device),
|
||||
"%s: device is not a text device", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
||||
log_bug_client(ei_device_get_context(device),
|
||||
"%s: device is not emulating", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ei_send_text_keysym(device, keysym, is_press);
|
||||
}
|
||||
|
||||
_public_ void
|
||||
ei_device_text_utf8(struct ei_device *device, const char *utf8)
|
||||
{
|
||||
ei_device_text_utf8_with_length(device, utf8, utf8 ? strlen(utf8) : 0);
|
||||
}
|
||||
|
||||
_public_ void
|
||||
ei_device_text_utf8_with_length(struct ei_device *device, const char *utf8, size_t length)
|
||||
{
|
||||
if (!ei_device_has_capability(device, EI_DEVICE_CAP_TEXT)) {
|
||||
log_bug_client(ei_device_get_context(device),
|
||||
"%s: device is not a text device", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
||||
log_bug_client(ei_device_get_context(device),
|
||||
"%s: device is not emulating", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
log_bug_client(ei_device_get_context(device),
|
||||
"%s: empty utf8 string", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[4096] = {0};
|
||||
memcpy(buf, utf8, min(length, sizeof(buf) - 1));
|
||||
ei_send_text_utf8(device, buf);
|
||||
}
|
||||
|
||||
_public_ void
|
||||
ei_device_frame(struct ei_device *device, uint64_t time)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "brei-shared.h"
|
||||
#include "libei-pointer.h"
|
||||
#include "libei-keyboard.h"
|
||||
#include "libei-text.h"
|
||||
#include "libei-touchscreen.h"
|
||||
|
||||
enum ei_device_state {
|
||||
|
|
@ -65,6 +66,7 @@ struct ei_device {
|
|||
struct ei_button *button;
|
||||
struct ei_keyboard *keyboard;
|
||||
struct ei_touchscreen *touchscreen;
|
||||
struct ei_text *text;
|
||||
|
||||
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, text_interface, const struct ei_text_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*);
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ 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_TEXT_KEYSYM);
|
||||
CASE_RETURN_STRING(EI_EVENT_TEXT_UTF8);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
@ -98,6 +100,10 @@ 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_TEXT_KEYSYM:
|
||||
break;
|
||||
case EI_EVENT_TEXT_UTF8:
|
||||
free(steal(&event->text.utf8));
|
||||
break;
|
||||
case EI_EVENT_DEVICE_ADDED:
|
||||
if (ei->interface_versions.ei_device >= EI_DEVICE_REQUEST_READY_SINCE_VERSION)
|
||||
|
|
@ -377,6 +383,30 @@ ei_event_touch_get_is_cancel(struct ei_event *event)
|
|||
return event->touch.is_cancel;
|
||||
}
|
||||
|
||||
_public_ uint32_t
|
||||
ei_event_text_get_keysym(struct ei_event *event)
|
||||
{
|
||||
require_event_type(event, 0, EI_EVENT_TEXT_KEYSYM);
|
||||
|
||||
return event->text.keysym;
|
||||
}
|
||||
|
||||
_public_ bool
|
||||
ei_event_text_get_keysym_is_press(struct ei_event *event)
|
||||
{
|
||||
require_event_type(event, false, EI_EVENT_TEXT_KEYSYM);
|
||||
|
||||
return event->text.is_press;
|
||||
}
|
||||
|
||||
_public_ const char *
|
||||
ei_event_text_get_utf8(struct ei_event *event)
|
||||
{
|
||||
require_event_type(event, NULL, EI_EVENT_TEXT_UTF8);
|
||||
|
||||
return event->text.utf8;
|
||||
}
|
||||
|
||||
_public_ uint64_t
|
||||
ei_event_get_time(struct ei_event *event)
|
||||
{
|
||||
|
|
@ -392,6 +422,8 @@ ei_event_get_time(struct ei_event *event)
|
|||
EI_EVENT_TOUCH_DOWN,
|
||||
EI_EVENT_TOUCH_UP,
|
||||
EI_EVENT_TOUCH_MOTION,
|
||||
EI_EVENT_TEXT_KEYSYM,
|
||||
EI_EVENT_TEXT_UTF8,
|
||||
EI_EVENT_FRAME);
|
||||
|
||||
return event->timestamp;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,11 @@ struct ei_event {
|
|||
double x, y;
|
||||
bool is_cancel;
|
||||
} touch;
|
||||
struct {
|
||||
uint32_t keysym;
|
||||
bool is_press;
|
||||
char *utf8;
|
||||
} text;
|
||||
struct {
|
||||
uint32_t sequence;
|
||||
} start_emulating;
|
||||
|
|
|
|||
|
|
@ -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_TEXT_INTERFACE_NAME, v->ei_text);
|
||||
}
|
||||
|
||||
ei_handshake_request_finish(setup);
|
||||
|
|
@ -144,6 +145,7 @@ handle_msg_interface_version(struct ei_handshake *setup, const char *name, uint3
|
|||
else VERSION_UPDATE(ei_button)
|
||||
else VERSION_UPDATE(ei_keyboard)
|
||||
else VERSION_UPDATE(ei_touchscreen)
|
||||
else VERSION_UPDATE(ei_text)
|
||||
|
||||
#undef VERSION_UPDATE
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#include "libei-region.h"
|
||||
#include "libei-scroll.h"
|
||||
#include "libei-seat.h"
|
||||
#include "libei-text.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_text;
|
||||
};
|
||||
|
||||
struct ei_unsent {
|
||||
|
|
@ -243,6 +245,13 @@ ei_queue_touch_up_event(struct ei_device *device, uint32_t touchid);
|
|||
void
|
||||
ei_queue_touch_cancel_event(struct ei_device *device, uint32_t touchid);
|
||||
|
||||
void
|
||||
ei_queue_text_keysym_event(struct ei_device *device, uint32_t keysym,
|
||||
bool is_press);
|
||||
|
||||
void
|
||||
ei_queue_text_utf8_event(struct ei_device *device, const char *utf8);
|
||||
|
||||
void
|
||||
ei_sync_event_send_done(struct ei_event *e);
|
||||
|
||||
|
|
|
|||
|
|
@ -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_TEXT:
|
||||
return seat->capabilities.map[EI_TEXT_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_TEXT:
|
||||
return seat->capabilities.map[EI_TEXT_INTERFACE_INDEX];
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
87
src/libei-text.c
Normal file
87
src/libei-text.c
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2025 Red Hat, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "util-bits.h"
|
||||
#include "util-macros.h"
|
||||
#include "util-mem.h"
|
||||
#include "util-io.h"
|
||||
#include "util-strings.h"
|
||||
#include "util-version.h"
|
||||
|
||||
#include "libei-private.h"
|
||||
#include "ei-proto.h"
|
||||
|
||||
static void
|
||||
ei_text_destroy(struct ei_text *text)
|
||||
{
|
||||
struct ei *ei = ei_text_get_context(text);
|
||||
ei_unregister_object(ei, &text->proto_object);
|
||||
}
|
||||
|
||||
OBJECT_IMPLEMENT_REF(ei_text);
|
||||
OBJECT_IMPLEMENT_UNREF_CLEANUP(ei_text);
|
||||
|
||||
static
|
||||
OBJECT_IMPLEMENT_CREATE(ei_text);
|
||||
static
|
||||
OBJECT_IMPLEMENT_PARENT(ei_text, ei_device);
|
||||
OBJECT_IMPLEMENT_GETTER_AS_REF(ei_text, proto_object, const struct brei_object*);
|
||||
|
||||
struct ei_device *
|
||||
ei_text_get_device(struct ei_text *text)
|
||||
{
|
||||
return ei_text_parent(text);
|
||||
}
|
||||
|
||||
struct ei*
|
||||
ei_text_get_context(struct ei_text *text)
|
||||
{
|
||||
return ei_device_get_context(ei_text_get_device(text));
|
||||
}
|
||||
|
||||
const struct ei_text_interface *
|
||||
ei_text_get_interface(struct ei_text *text) {
|
||||
struct ei_device *device = ei_text_get_device(text);
|
||||
return ei_device_get_text_interface(device);
|
||||
}
|
||||
|
||||
struct ei_text *
|
||||
ei_text_new(struct ei_device *device, object_id_t id, uint32_t version)
|
||||
{
|
||||
struct ei_text *text = ei_text_create(&device->object);
|
||||
struct ei *ei = ei_device_get_context(device);
|
||||
|
||||
text->proto_object.id = id;
|
||||
text->proto_object.implementation = text;
|
||||
text->proto_object.interface = &ei_text_proto_interface;
|
||||
text->proto_object.version = version;
|
||||
ei_register_object(ei, &text->proto_object);
|
||||
|
||||
return text; /* ref owned by caller */
|
||||
}
|
||||
49
src/libei-text.h
Normal file
49
src/libei-text.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2025 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_text;
|
||||
|
||||
/* This is a protocol-only object, not exposed in the API */
|
||||
struct ei_text {
|
||||
struct object object;
|
||||
struct brei_object proto_object;
|
||||
};
|
||||
|
||||
OBJECT_DECLARE_GETTER(ei_text, context, struct ei*);
|
||||
OBJECT_DECLARE_GETTER(ei_text, device, struct ei_device*);
|
||||
OBJECT_DECLARE_GETTER(ei_text, proto_object, const struct brei_object*);
|
||||
OBJECT_DECLARE_GETTER(ei_text, interface, const struct ei_text_interface *);
|
||||
OBJECT_DECLARE_REF(ei_text);
|
||||
OBJECT_DECLARE_UNREF(ei_text);
|
||||
|
||||
struct ei_text *
|
||||
ei_text_new(struct ei_device *device, object_id_t id, uint32_t version);
|
||||
29
src/libei.c
29
src/libei.c
|
|
@ -133,6 +133,7 @@ ei_create_context(bool is_sender, void *user_data)
|
|||
.ei_scroll = VERSION_V(1),
|
||||
.ei_button = VERSION_V(1),
|
||||
.ei_keyboard = VERSION_V(1),
|
||||
.ei_text = VERSION_V(1),
|
||||
.ei_touchscreen = VERSION_V(2),
|
||||
};
|
||||
/* This must be v1 until the server tells us otherwise */
|
||||
|
|
@ -245,6 +246,8 @@ 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_TEXT_KEYSYM:
|
||||
case EI_EVENT_TEXT_UTF8:
|
||||
if (event->timestamp != 0) {
|
||||
log_bug(ei_event_get_context(event),
|
||||
"Unexpected timestamp for event of type: %s",
|
||||
|
|
@ -280,6 +283,8 @@ 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_TEXT_KEYSYM:
|
||||
case EI_EVENT_TEXT_UTF8:
|
||||
prefix = "pending ";
|
||||
queue = &device->pending_event_queue;
|
||||
break;
|
||||
|
|
@ -626,6 +631,30 @@ ei_queue_touch_cancel_event(struct ei_device *device, uint32_t touchid)
|
|||
queue_event(ei_device_get_context(device), e);
|
||||
}
|
||||
|
||||
void
|
||||
ei_queue_text_keysym_event(struct ei_device *device, uint32_t keysym,
|
||||
bool is_press)
|
||||
{
|
||||
struct ei_event *e = ei_event_new_for_device(device);
|
||||
|
||||
e->type = EI_EVENT_TEXT_KEYSYM;
|
||||
e->text.keysym = keysym;
|
||||
e->text.is_press = is_press;
|
||||
|
||||
queue_event(ei_device_get_context(device), e);
|
||||
}
|
||||
|
||||
void
|
||||
ei_queue_text_utf8_event(struct ei_device *device, const char *utf8)
|
||||
{
|
||||
struct ei_event *e = ei_event_new_for_device(device);
|
||||
|
||||
e->type = EI_EVENT_TEXT_UTF8;
|
||||
e->text.utf8 = xstrdup(utf8);
|
||||
|
||||
queue_event(ei_device_get_context(device), e);
|
||||
}
|
||||
|
||||
_public_ void
|
||||
ei_disconnect(struct ei *ei)
|
||||
{
|
||||
|
|
|
|||
112
src/libei.h
112
src/libei.h
|
|
@ -300,6 +300,10 @@ enum ei_device_capability {
|
|||
* The device can send button events
|
||||
*/
|
||||
EI_DEVICE_CAP_BUTTON = (1 << 5),
|
||||
/**
|
||||
* The device can send text-like data
|
||||
*/
|
||||
EI_DEVICE_CAP_TEXT = (1 << 6),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -591,6 +595,24 @@ enum ei_event_type {
|
|||
* See ei_device_touch_new() and ei_touch_motion() for the sender context API.
|
||||
*/
|
||||
EI_EVENT_TOUCH_MOTION,
|
||||
|
||||
/**
|
||||
* Event for a single keysym logically pressed/released on this device.
|
||||
* The keysym is an XKB-compatible keysym (not key code!) and may not be
|
||||
* present on any keymap currently active on any device.
|
||||
*
|
||||
* @note This event is only generated on a receiver ei context.
|
||||
* See ei_device_text_keysym() for the sender context API.
|
||||
*/
|
||||
EI_EVENT_TEXT_KEYSYM = 900,
|
||||
|
||||
/**
|
||||
* Event for a UTF-8 compatible text sequence sent by this device.
|
||||
*
|
||||
* @note This event is only generated on a receiver ei context.
|
||||
* See ei_device_text_utf8() for the sender context API.
|
||||
*/
|
||||
EI_EVENT_TEXT_UTF8,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1845,6 +1867,68 @@ ei_device_scroll_cancel(struct ei_device *device, bool cancel_x, bool cancel_y);
|
|||
void
|
||||
ei_device_keyboard_key(struct ei_device *device, uint32_t keycode, bool is_press);
|
||||
|
||||
/**
|
||||
* @ingroup libei-sender
|
||||
*
|
||||
* Generate a key event on a device with
|
||||
* the @ref EI_DEVICE_CAP_TEXT capability.
|
||||
*
|
||||
* Keysyms use the XKB-compatible keysyms, see e.g.
|
||||
* [/usr/include/xkbcommon/xkbcommon-keysyms.h](https://xkbcommon.org/doc/current/xkbcommon-keysyms_8h.html)
|
||||
* for a list of key syms
|
||||
*
|
||||
* Note that this keysym is independent of any keymap and the keysym
|
||||
* may not exist on any active keymap on any device. For example, a device
|
||||
* with both @ref EI_DEVICE_CAP_TEXT and @ref EI_DEVICE_CAP_KEYBOARD is not
|
||||
* limited to sending keysyms only present on the keymap.
|
||||
*
|
||||
* This method is only available on an ei sender context.
|
||||
*
|
||||
* @param device The EI device
|
||||
* @param keysym The keysym
|
||||
* @param is_press true for key down, false for key up
|
||||
*/
|
||||
void
|
||||
ei_device_text_keysym(struct ei_device *device, uint32_t keysym, bool is_press);
|
||||
|
||||
/**
|
||||
* @ingroup libei-sender
|
||||
*
|
||||
* Generate a UTF-8 text event on a device with
|
||||
* the @ref EI_DEVICE_CAP_TEXT capability.
|
||||
*
|
||||
* This method is only available on an ei sender context.
|
||||
* This method is identical to ei_device_text_utf8_with_length()
|
||||
* but calculates the length of the string.
|
||||
*
|
||||
* @param device The EI device
|
||||
* @param text The zero-terminated UTF-8 compatible text
|
||||
*
|
||||
* @see ei_device_text_utf8_with_length
|
||||
*/
|
||||
void
|
||||
ei_device_text_utf8(struct ei_device *device, const char *text);
|
||||
|
||||
/**
|
||||
* @ingroup libei-sender
|
||||
*
|
||||
* Generate a UTF-8 text event on a device with
|
||||
* the @ref EI_DEVICE_CAP_TEXT capability.
|
||||
*
|
||||
* This method is only available on an ei sender context.
|
||||
*
|
||||
* This method is identical to ei_device_text_utf8()
|
||||
* but takes an additional length argument.
|
||||
*
|
||||
* @param device The EI device
|
||||
* @param text The zero-terminated UTF-8 compatible text
|
||||
* @param length The length of text in bytes, excluding the zero bytes
|
||||
*
|
||||
* @see ei_device_text_utf8
|
||||
*/
|
||||
void
|
||||
ei_device_text_utf8_with_length(struct ei_device *device, const char *text, size_t length);
|
||||
|
||||
/**
|
||||
* @ingroup libei-sender
|
||||
*
|
||||
|
|
@ -2220,6 +2304,34 @@ ei_event_touch_get_y(struct ei_event *event);
|
|||
bool
|
||||
ei_event_touch_get_is_cancel(struct ei_event *event);
|
||||
|
||||
/**
|
||||
* @ingroup libei-receiver
|
||||
*
|
||||
* For an event of type @ref EI_EVENT_TEXT_KEYSYM
|
||||
* return the XKB-compatible keysym.
|
||||
*/
|
||||
uint32_t
|
||||
ei_event_text_get_keysym(struct ei_event *event);
|
||||
|
||||
/**
|
||||
* @ingroup libei-receiver
|
||||
*
|
||||
* For an event of type @ref EI_EVENT_TEXT_KEYSYM
|
||||
* return true if the event is a logical key down for the
|
||||
* keysym.
|
||||
*/
|
||||
bool
|
||||
ei_event_text_get_keysym_is_press(struct ei_event *event);
|
||||
|
||||
/**
|
||||
* @ingroup libei-receiver
|
||||
*
|
||||
* For an event of type @ref EI_EVENT_TEXT_UTF8
|
||||
* return the zero-terminated UTF8 string.
|
||||
*/
|
||||
const char *
|
||||
ei_event_text_get_utf8(struct ei_event *event);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -528,6 +528,7 @@ eis_client_new(struct eis *eis, int fd)
|
|||
.ei_scroll = VERSION_V(1),
|
||||
.ei_button = VERSION_V(1),
|
||||
.ei_keyboard = VERSION_V(1),
|
||||
.ei_text = VERSION_V(1),
|
||||
.ei_touchscreen = VERSION_V(2),
|
||||
};
|
||||
struct source *s = source_new(fd, client_dispatch, client);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ struct eis_client_interface_versions {
|
|||
uint32_t ei_button;
|
||||
uint32_t ei_keyboard;
|
||||
uint32_t ei_touchscreen;
|
||||
uint32_t ei_text;
|
||||
};
|
||||
|
||||
struct eis_client {
|
||||
|
|
|
|||
|
|
@ -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_text_unref(device->text);
|
||||
|
||||
free(device->name);
|
||||
}
|
||||
|
|
@ -739,6 +740,73 @@ eis_device_get_touchscreen_interface(struct eis_device *device)
|
|||
return &touchscreen_interface;
|
||||
}
|
||||
|
||||
static struct brei_result *
|
||||
client_msg_text_release(struct eis_text *text)
|
||||
{
|
||||
struct eis_device *device = eis_text_get_device(text);
|
||||
eis_text_event_destroyed(device->text,
|
||||
eis_client_get_next_serial(eis_device_get_client(device)));
|
||||
eis_text_unref(steal(&device->text));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct brei_result *
|
||||
client_msg_text_keysym(struct eis_text *text, uint32_t keysym, uint32_t state)
|
||||
{
|
||||
struct eis_device *device = eis_text_get_device(text);
|
||||
|
||||
DISCONNECT_IF_RECEIVER_CONTEXT(device);
|
||||
|
||||
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_TEXT)) {
|
||||
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
|
||||
"keysym event for non-text device");
|
||||
}
|
||||
|
||||
if (device->state == EIS_DEVICE_STATE_EMULATING) {
|
||||
eis_queue_text_keysym_event(device, keysym, !!state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return maybe_error_on_device_state(device, "text keysym");
|
||||
}
|
||||
|
||||
static struct brei_result *
|
||||
client_msg_text_utf8(struct eis_text *text, const char *utf8)
|
||||
{
|
||||
struct eis_device *device = eis_text_get_device(text);
|
||||
|
||||
DISCONNECT_IF_RECEIVER_CONTEXT(device);
|
||||
|
||||
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_TEXT)) {
|
||||
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
|
||||
"utf8 event for non-text device");
|
||||
}
|
||||
|
||||
if (!utf8) {
|
||||
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL,
|
||||
"utf8 text is NULL");
|
||||
}
|
||||
|
||||
if (device->state == EIS_DEVICE_STATE_EMULATING) {
|
||||
eis_queue_text_utf8_event(device, utf8);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return maybe_error_on_device_state(device, "text utf8");
|
||||
}
|
||||
|
||||
static const struct eis_text_interface text_interface = {
|
||||
.release = client_msg_text_release,
|
||||
.keysym = client_msg_text_keysym,
|
||||
.utf8 = client_msg_text_utf8,
|
||||
};
|
||||
|
||||
const struct eis_text_interface *
|
||||
eis_device_get_text_interface(struct eis_device *device)
|
||||
{
|
||||
return &text_interface;
|
||||
}
|
||||
|
||||
_public_ struct eis_device *
|
||||
eis_seat_new_device(struct eis_seat *seat)
|
||||
{
|
||||
|
|
@ -809,6 +877,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_TEXT:
|
||||
mask_add(device->capabilities, cap);
|
||||
break;
|
||||
}
|
||||
|
|
@ -935,6 +1004,14 @@ eis_device_add(struct eis_device *device)
|
|||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
if (eis_device_has_capability(device, EIS_DEVICE_CAP_TEXT)) {
|
||||
device->text = eis_text_new(device);
|
||||
rc = eis_device_event_interface(device, eis_text_get_id(device->text),
|
||||
EIS_TEXT_INTERFACE_NAME,
|
||||
eis_text_get_version(device->text));
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = eis_device_event_done(device);
|
||||
if (rc < 0)
|
||||
|
|
@ -991,6 +1068,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->text) {
|
||||
eis_text_event_destroyed(device->text, eis_client_get_next_serial(client));
|
||||
eis_text_unref(steal(&device->text));
|
||||
}
|
||||
|
||||
if (device->state != EIS_DEVICE_STATE_NEW)
|
||||
eis_device_event_destroyed(device, eis_client_get_next_serial(client));
|
||||
|
|
@ -1018,6 +1099,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_TEXT:
|
||||
return mask_all(device->capabilities, cap);
|
||||
}
|
||||
return false;
|
||||
|
|
@ -1391,6 +1473,49 @@ eis_touch_cancel(struct eis_touch *touch)
|
|||
eis_touchscreen_event_up(device->touchscreen, touch->tracking_id);
|
||||
}
|
||||
|
||||
_public_ void
|
||||
eis_device_text_keysym(struct eis_device *device,
|
||||
uint32_t keysym, bool is_press)
|
||||
{
|
||||
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_TEXT)) {
|
||||
log_bug_client(eis_device_get_context(device),
|
||||
"%s: device is not a text device", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device->state != EIS_DEVICE_STATE_EMULATING)
|
||||
return;
|
||||
|
||||
device->send_frame_event = true;
|
||||
|
||||
eis_text_event_keysym(device->text, keysym, is_press);
|
||||
}
|
||||
|
||||
_public_ void
|
||||
eis_device_text_utf8(struct eis_device *device, const char *utf8)
|
||||
{
|
||||
return eis_device_text_utf8_with_length(device, utf8, utf8 ? strlen(utf8) : 0);
|
||||
}
|
||||
|
||||
_public_ void
|
||||
eis_device_text_utf8_with_length(struct eis_device *device, const char *text, size_t length)
|
||||
{
|
||||
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_TEXT)) {
|
||||
log_bug_client(eis_device_get_context(device),
|
||||
"%s: device is not a text device", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device->state != EIS_DEVICE_STATE_EMULATING)
|
||||
return;
|
||||
|
||||
device->send_frame_event = true;
|
||||
|
||||
char buf[4096] = {0};
|
||||
memcpy(buf, text, min(length, sizeof(buf) - 1));
|
||||
eis_text_event_utf8(device->text, buf);
|
||||
}
|
||||
|
||||
_public_ void
|
||||
eis_device_frame(struct eis_device *device, uint64_t time)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ struct eis_device {
|
|||
struct eis_button *button;
|
||||
struct eis_keyboard *keyboard;
|
||||
struct eis_touchscreen *touchscreen;
|
||||
struct eis_text *text;
|
||||
|
||||
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, text_interface, const struct eis_text_interface *);
|
||||
|
||||
void
|
||||
eis_device_set_client_keymap(struct eis_device *device,
|
||||
|
|
|
|||
|
|
@ -57,6 +57,11 @@ eis_event_destroy(struct eis_event *event)
|
|||
case EIS_EVENT_TOUCH_MOTION:
|
||||
case EIS_EVENT_TOUCH_UP:
|
||||
case EIS_EVENT_FRAME:
|
||||
case EIS_EVENT_TEXT_KEYSYM:
|
||||
handled = true;
|
||||
break;
|
||||
case EIS_EVENT_TEXT_UTF8:
|
||||
free(steal(&event->text.utf8));
|
||||
handled = true;
|
||||
break;
|
||||
case EIS_EVENT_PONG:
|
||||
|
|
@ -192,6 +197,8 @@ eis_event_get_time(struct eis_event *event)
|
|||
EIS_EVENT_TOUCH_DOWN,
|
||||
EIS_EVENT_TOUCH_UP,
|
||||
EIS_EVENT_TOUCH_MOTION,
|
||||
EIS_EVENT_TEXT_KEYSYM,
|
||||
EIS_EVENT_TEXT_UTF8,
|
||||
EIS_EVENT_FRAME);
|
||||
|
||||
return event->timestamp;
|
||||
|
|
@ -217,6 +224,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_TEXT:
|
||||
return mask_all(event->bind.capabilities, cap);
|
||||
}
|
||||
return false;
|
||||
|
|
@ -430,3 +438,30 @@ eis_event_touch_get_is_cancel(struct eis_event *event)
|
|||
|
||||
return event->touch.is_cancel;
|
||||
}
|
||||
|
||||
_public_ uint32_t
|
||||
eis_event_text_get_keysym(struct eis_event *event)
|
||||
{
|
||||
require_event_type(event, 0,
|
||||
EIS_EVENT_TEXT_KEYSYM);
|
||||
|
||||
return event->text.keysym;
|
||||
}
|
||||
|
||||
_public_ bool
|
||||
eis_event_text_get_keysym_is_press(struct eis_event *event)
|
||||
{
|
||||
require_event_type(event, false,
|
||||
EIS_EVENT_TEXT_KEYSYM);
|
||||
|
||||
return event->text.is_press;
|
||||
}
|
||||
|
||||
_public_ const char *
|
||||
eis_event_text_get_utf8(struct eis_event *event)
|
||||
{
|
||||
require_event_type(event, NULL,
|
||||
EIS_EVENT_TEXT_UTF8);
|
||||
|
||||
return event->text.utf8;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,11 @@ struct eis_event {
|
|||
double x, y;
|
||||
bool is_cancel;
|
||||
} touch;
|
||||
struct {
|
||||
uint32_t keysym;
|
||||
bool is_press;
|
||||
char *utf8;
|
||||
} text;
|
||||
struct {
|
||||
uint32_t sequence;
|
||||
} start_emulating;
|
||||
|
|
|
|||
|
|
@ -237,6 +237,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_text),
|
||||
#undef VERSION_ENTRY
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
#include "libeis-region.h"
|
||||
#include "libeis-scroll.h"
|
||||
#include "libeis-seat.h"
|
||||
#include "libeis-text.h"
|
||||
#include "libeis-touchscreen.h"
|
||||
|
||||
struct eis_backend_interface {
|
||||
|
|
@ -160,6 +161,12 @@ eis_queue_touch_up_event(struct eis_device *device, uint32_t touchid);
|
|||
void
|
||||
eis_queue_touch_cancel_event(struct eis_device *device, uint32_t touchid);
|
||||
|
||||
void
|
||||
eis_queue_text_keysym_event(struct eis_device *device, uint32_t keysym, bool is_press);
|
||||
|
||||
void
|
||||
eis_queue_text_utf8_event(struct eis_device *device, const char *utf8);
|
||||
|
||||
void
|
||||
eis_sync_event_send_done(struct eis_event *e);
|
||||
|
||||
|
|
|
|||
|
|
@ -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_TEXT_INTERFACE_INDEX))
|
||||
capabilities |= EIS_DEVICE_CAP_TEXT;
|
||||
|
||||
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_TEXT &&
|
||||
client->interface_versions.ei_text > 0) {
|
||||
uint64_t mask = bit(EIS_TEXT_INTERFACE_INDEX);
|
||||
eis_seat_event_capability(seat, mask,
|
||||
EIS_TEXT_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_TEXT:
|
||||
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_TEXT:
|
||||
return mask_all(seat->capabilities.c_mask, cap);
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
107
src/libeis-text.c
Normal file
107
src/libeis-text.c
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2025 Red Hat, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "util-bits.h"
|
||||
#include "util-macros.h"
|
||||
#include "util-mem.h"
|
||||
#include "util-io.h"
|
||||
#include "util-strings.h"
|
||||
#include "util-version.h"
|
||||
|
||||
#include "libeis-private.h"
|
||||
#include "eis-proto.h"
|
||||
|
||||
static void
|
||||
eis_text_destroy(struct eis_text *text)
|
||||
{
|
||||
struct eis_client * client = eis_text_get_client(text);
|
||||
eis_client_unregister_object(client, &text->proto_object);
|
||||
}
|
||||
|
||||
OBJECT_IMPLEMENT_REF(eis_text);
|
||||
OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_text);
|
||||
OBJECT_IMPLEMENT_GETTER_AS_REF(eis_text, proto_object, const struct brei_object *);
|
||||
|
||||
static
|
||||
OBJECT_IMPLEMENT_CREATE(eis_text);
|
||||
static
|
||||
OBJECT_IMPLEMENT_PARENT(eis_text, eis_device);
|
||||
|
||||
uint32_t
|
||||
eis_text_get_version(struct eis_text *text)
|
||||
{
|
||||
return text->proto_object.version;
|
||||
}
|
||||
|
||||
object_id_t
|
||||
eis_text_get_id(struct eis_text *text)
|
||||
{
|
||||
return text->proto_object.id;
|
||||
}
|
||||
|
||||
struct eis_device *
|
||||
eis_text_get_device(struct eis_text *text)
|
||||
{
|
||||
return eis_text_parent(text);
|
||||
}
|
||||
|
||||
struct eis_client*
|
||||
eis_text_get_client(struct eis_text *text)
|
||||
{
|
||||
return eis_device_get_client(eis_text_get_device(text));
|
||||
}
|
||||
|
||||
struct eis*
|
||||
eis_text_get_context(struct eis_text *text)
|
||||
{
|
||||
struct eis_client *client = eis_text_get_client(text);
|
||||
return eis_client_get_context(client);
|
||||
}
|
||||
|
||||
const struct eis_text_interface *
|
||||
eis_text_get_interface(struct eis_text *text) {
|
||||
return eis_device_get_text_interface(eis_text_get_device(text));
|
||||
}
|
||||
|
||||
struct eis_text *
|
||||
eis_text_new(struct eis_device *device)
|
||||
{
|
||||
struct eis_text *text = eis_text_create(&device->object);
|
||||
struct eis_client *client = eis_device_get_client(device);
|
||||
|
||||
text->proto_object.id = eis_client_get_new_id(client);
|
||||
text->proto_object.implementation = text;
|
||||
text->proto_object.interface = &eis_text_proto_interface;
|
||||
text->proto_object.version = client->interface_versions.ei_text;
|
||||
list_init(&text->proto_object.link);
|
||||
|
||||
eis_client_register_object(client, &text->proto_object);
|
||||
|
||||
return text; /* ref owned by caller */
|
||||
}
|
||||
51
src/libeis-text.h
Normal file
51
src/libeis-text.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2025 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_text {
|
||||
struct object object;
|
||||
struct brei_object proto_object;
|
||||
};
|
||||
|
||||
OBJECT_DECLARE_GETTER(eis_text, context, struct eis *);
|
||||
OBJECT_DECLARE_GETTER(eis_text, device, struct eis_device *);
|
||||
OBJECT_DECLARE_GETTER(eis_text, client, struct eis_client *);
|
||||
OBJECT_DECLARE_GETTER(eis_text, id, object_id_t);
|
||||
OBJECT_DECLARE_GETTER(eis_text, version, uint32_t);
|
||||
OBJECT_DECLARE_GETTER(eis_text, proto_object, const struct brei_object *);
|
||||
OBJECT_DECLARE_GETTER(eis_text, interface, const struct eis_text_interface *);
|
||||
OBJECT_DECLARE_REF(eis_text);
|
||||
OBJECT_DECLARE_UNREF(eis_text);
|
||||
|
||||
struct eis_text *
|
||||
eis_text_new(struct eis_device *device);
|
||||
26
src/libeis.c
26
src/libeis.c
|
|
@ -155,6 +155,8 @@ eis_event_type_to_string(enum eis_event_type type)
|
|||
CASE_RETURN_STRING(EIS_EVENT_TOUCH_UP);
|
||||
CASE_RETURN_STRING(EIS_EVENT_TOUCH_MOTION);
|
||||
CASE_RETURN_STRING(EIS_EVENT_FRAME);
|
||||
CASE_RETURN_STRING(EIS_EVENT_TEXT_KEYSYM);
|
||||
CASE_RETURN_STRING(EIS_EVENT_TEXT_UTF8);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
@ -175,6 +177,8 @@ 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_TEXT_KEYSYM:
|
||||
case EIS_EVENT_TEXT_UTF8:
|
||||
if (event->timestamp != 0) {
|
||||
log_bug(eis_event_get_context(event),
|
||||
"Unexpected timestamp for event of type: %s",
|
||||
|
|
@ -211,6 +215,8 @@ 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_TEXT_KEYSYM:
|
||||
case EIS_EVENT_TEXT_UTF8:
|
||||
prefix = "pending ";
|
||||
queue = &device->pending_event_queue;
|
||||
break;
|
||||
|
|
@ -474,6 +480,26 @@ eis_queue_touch_cancel_event(struct eis_device *device, uint32_t touchid)
|
|||
eis_queue_event(e);
|
||||
}
|
||||
|
||||
void
|
||||
eis_queue_text_keysym_event(struct eis_device *device, uint32_t keysym,
|
||||
bool is_press)
|
||||
{
|
||||
struct eis_event *e = eis_event_new_for_device(device);
|
||||
e->type = EIS_EVENT_TEXT_KEYSYM;
|
||||
e->text.keysym = keysym;
|
||||
e->text.is_press = is_press;
|
||||
eis_queue_event(e);
|
||||
}
|
||||
|
||||
void
|
||||
eis_queue_text_utf8_event(struct eis_device *device, const char *text)
|
||||
{
|
||||
struct eis_event *e = eis_event_new_for_device(device);
|
||||
e->type = EIS_EVENT_TEXT_UTF8;
|
||||
e->text.utf8 = xstrdup(text);
|
||||
eis_queue_event(e);
|
||||
}
|
||||
|
||||
_public_ struct eis_event*
|
||||
eis_get_event(struct eis *eis)
|
||||
{
|
||||
|
|
|
|||
63
src/libeis.h
63
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_TEXT = (1 << 6),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -396,6 +397,16 @@ enum eis_event_type {
|
|||
* properties).
|
||||
*/
|
||||
EIS_EVENT_TOUCH_MOTION,
|
||||
|
||||
/**
|
||||
* A key sym logical press or release event
|
||||
*/
|
||||
EIS_EVENT_TEXT_KEYSYM = 900,
|
||||
|
||||
/**
|
||||
* A text event event
|
||||
*/
|
||||
EIS_EVENT_TEXT_UTF8,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1508,6 +1519,30 @@ eis_device_keyboard_send_xkb_modifiers(struct eis_device *device,
|
|||
uint32_t locked,
|
||||
uint32_t group);
|
||||
|
||||
/**
|
||||
* @ingroup libeis-receiver
|
||||
*
|
||||
* see @ref ei_device_text_keysym
|
||||
*/
|
||||
void
|
||||
eis_device_text_keysym(struct eis_device *device, uint32_t keysym, bool is_press);
|
||||
|
||||
/**
|
||||
* @ingroup libeis-receiver
|
||||
*
|
||||
* see @ref ei_device_text_utf8
|
||||
*/
|
||||
void
|
||||
eis_device_text_utf8(struct eis_device *device, const char *text);
|
||||
|
||||
/**
|
||||
* @ingroup libeis-receiver
|
||||
*
|
||||
* see @ref ei_device_text_utf8_with_length
|
||||
*/
|
||||
void
|
||||
eis_device_text_utf8_with_length(struct eis_device *device, const char *text, size_t length);
|
||||
|
||||
/**
|
||||
* @ingroup libeis-receiver
|
||||
*
|
||||
|
|
@ -1869,6 +1904,34 @@ eis_event_touch_get_y(struct eis_event *event);
|
|||
bool
|
||||
eis_event_touch_get_is_cancel(struct eis_event *event);
|
||||
|
||||
/**
|
||||
* @ingroup libeis-sender
|
||||
*
|
||||
* For an event of type @ref EIS_EVENT_TEXT_KEYSYM
|
||||
* return the XKB-compatible keysym.
|
||||
*/
|
||||
uint32_t
|
||||
eis_event_text_get_keysym(struct eis_event *event);
|
||||
|
||||
/**
|
||||
* @ingroup libeis-sender
|
||||
*
|
||||
* For an event of type @ref EIS_EVENT_TEXT_KEYSYM
|
||||
* return true if the event is a logical key down for the
|
||||
* keysym.
|
||||
*/
|
||||
bool
|
||||
eis_event_text_get_keysym_is_press(struct eis_event *event);
|
||||
|
||||
/**
|
||||
* @ingroup libeis-sender
|
||||
*
|
||||
* For an event of type @ref EIS_EVENT_TEXT_UTF8
|
||||
* return the zero-terminated UTF8 string.
|
||||
*/
|
||||
const char *
|
||||
eis_event_text_get_utf8(struct eis_event *event);
|
||||
|
||||
/**
|
||||
* @returns a timestamp for the current time to pass into
|
||||
* eis_device_frame().
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ if build_libei
|
|||
'libei-scroll.c',
|
||||
'libei-seat.c',
|
||||
'libei-socket.c',
|
||||
'libei-text.c',
|
||||
'libei-touchscreen.c',
|
||||
) + [brei_proto_headers, ei_proto_headers, ei_proto_sources]
|
||||
|
||||
|
|
@ -157,6 +158,7 @@ if build_libeis
|
|||
'libeis-scroll.c',
|
||||
'libeis-seat.c',
|
||||
'libeis-socket.c',
|
||||
'libeis-text.c',
|
||||
'libeis-touchscreen.c',
|
||||
'libeis.c',
|
||||
) + [brei_proto_headers, eis_proto_headers, eis_proto_sources]
|
||||
|
|
|
|||
|
|
@ -1159,6 +1159,8 @@ _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_TEXT_KEYSYM:
|
||||
case EIS_EVENT_TEXT_UTF8:
|
||||
need_frame = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1320,6 +1322,8 @@ _peck_dispatch_ei(struct peck *peck, int lineno)
|
|||
case EI_EVENT_TOUCH_DOWN:
|
||||
case EI_EVENT_TOUCH_UP:
|
||||
case EI_EVENT_TOUCH_MOTION:
|
||||
case EI_EVENT_TEXT_KEYSYM:
|
||||
case EI_EVENT_TEXT_UTF8:
|
||||
need_frame = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1656,6 +1660,8 @@ peck_ei_event_type_name(enum ei_event_type type)
|
|||
CASE_STRING(TOUCH_DOWN);
|
||||
CASE_STRING(TOUCH_UP);
|
||||
CASE_STRING(TOUCH_MOTION);
|
||||
CASE_STRING(TEXT_KEYSYM);
|
||||
CASE_STRING(TEXT_UTF8);
|
||||
}
|
||||
#undef CASE_STRING
|
||||
assert(!"Unhandled ei event type");
|
||||
|
|
@ -1695,6 +1701,8 @@ peck_eis_event_type_name(enum eis_event_type type)
|
|||
CASE_STRING(TOUCH_UP);
|
||||
CASE_STRING(TOUCH_MOTION);
|
||||
CASE_STRING(FRAME);
|
||||
CASE_STRING(TEXT_KEYSYM);
|
||||
CASE_STRING(TEXT_UTF8);
|
||||
}
|
||||
#undef CASE_STRING
|
||||
assert(!"Unhandled EIS event type");
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@
|
|||
#define libevdev_event_code_get_name(...) ""
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBXKBCOMMON
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#endif
|
||||
|
||||
#include "libei.h"
|
||||
|
||||
#include "src/util-macros.h"
|
||||
|
|
@ -227,6 +231,32 @@ print_key_event(struct ei_event *event)
|
|||
printf(" press: %s\n", press ? "true" : "false");
|
||||
}
|
||||
|
||||
static void
|
||||
print_keysym_event(struct ei_event *event)
|
||||
{
|
||||
print_device(event);
|
||||
|
||||
uint32_t keysym = ei_event_text_get_keysym(event);
|
||||
bool press = ei_event_text_get_keysym_is_press(event);
|
||||
|
||||
char buf[128] = {0};
|
||||
#if HAVE_LIBXKBCOMMON
|
||||
xkb_keysym_get_name(keysym, buf, sizeof(buf));
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), "0x%04x", keysym);
|
||||
#endif
|
||||
|
||||
printf(" keysym: 0x%08x # %s\n", keysym, buf);
|
||||
printf(" press: %s\n", press ? "true" : "false");
|
||||
}
|
||||
|
||||
static void
|
||||
print_utf8_event(struct ei_event *event)
|
||||
{
|
||||
const char *text = ei_event_text_get_utf8(event);
|
||||
printf(" utf8: '%s'\n", text);
|
||||
}
|
||||
|
||||
static void
|
||||
print_touch_event(struct ei_event *event)
|
||||
{
|
||||
|
|
@ -434,6 +464,12 @@ int main(int argc, char **argv)
|
|||
case EI_EVENT_KEYBOARD_KEY:
|
||||
print_key_event(e);
|
||||
break;
|
||||
case EI_EVENT_TEXT_KEYSYM:
|
||||
print_keysym_event(e);
|
||||
break;
|
||||
case EI_EVENT_TEXT_UTF8:
|
||||
print_utf8_event(e);
|
||||
break;
|
||||
case EI_EVENT_TOUCH_DOWN:
|
||||
case EI_EVENT_TOUCH_MOTION:
|
||||
case EI_EVENT_TOUCH_UP:
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@
|
|||
#include <xkbcommon/xkbcommon.h>
|
||||
#endif
|
||||
|
||||
#ifndef XK_dead_a
|
||||
#define XK_dead_a 0xfe80
|
||||
#endif
|
||||
|
||||
#include "libei.h"
|
||||
|
||||
#include "src/util-macros.h"
|
||||
|
|
@ -126,7 +130,7 @@ setup_xkb_keymap(struct ei_keymap *keymap)
|
|||
for (unsigned int evcode = KEY_Q; evcode <= KEY_Y; evcode++) {
|
||||
char utf8[7];
|
||||
xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkbstate, evcode + 8);
|
||||
xkb_keysym_to_utf8(keysym, utf8, sizeof(utf8));
|
||||
xkb_keysym_get_name(keysym, utf8, sizeof(utf8));
|
||||
strcat(layout, utf8);
|
||||
}
|
||||
|
||||
|
|
@ -300,12 +304,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) *text = NULL;
|
||||
|
||||
bool stop = false;
|
||||
bool have_ptr = false;
|
||||
bool have_kbd = false;
|
||||
bool have_abs = false;
|
||||
bool have_touch = false;
|
||||
bool have_text = false;
|
||||
struct ei_seat *default_seat = NULL;
|
||||
|
||||
uint32_t sequence = 0;
|
||||
|
|
@ -344,7 +350,9 @@ 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_TEXT,
|
||||
NULL);
|
||||
break;
|
||||
}
|
||||
case EI_EVENT_SEAT_REMOVED:
|
||||
|
|
@ -376,6 +384,10 @@ int main(int argc, char **argv)
|
|||
touch = ei_device_ref(device);
|
||||
handle_regions(device);
|
||||
}
|
||||
if (ei_device_has_capability(device, EI_DEVICE_CAP_TEXT)) {
|
||||
colorprint("New text device: %s\n", ei_device_get_name(device));
|
||||
text = ei_device_ref(device);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EI_EVENT_DEVICE_RESUMED:
|
||||
|
|
@ -403,6 +415,12 @@ int main(int argc, char **argv)
|
|||
colorprint("Touch device was resumed\n");
|
||||
have_touch = true;
|
||||
}
|
||||
if (ei_event_get_device(e) == text) {
|
||||
if (!receiver)
|
||||
ei_device_start_emulating(text, ++sequence);
|
||||
colorprint("Text device was resumed\n");
|
||||
have_text = true;
|
||||
}
|
||||
break;
|
||||
case EI_EVENT_DEVICE_PAUSED:
|
||||
if (ei_event_get_device(e) == ptr) {
|
||||
|
|
@ -421,6 +439,10 @@ int main(int argc, char **argv)
|
|||
colorprint("Touch device was paused\n");
|
||||
have_touch = false;
|
||||
}
|
||||
if (ei_event_get_device(e) == text) {
|
||||
colorprint("Text device was paused\n");
|
||||
have_text = false;
|
||||
}
|
||||
break;
|
||||
case EI_EVENT_DEVICE_REMOVED:
|
||||
{
|
||||
|
|
@ -498,6 +520,26 @@ int main(int argc, char **argv)
|
|||
colorprint("touch up %u\n", ei_event_touch_get_id(e));
|
||||
}
|
||||
break;
|
||||
case EI_EVENT_TEXT_KEYSYM:
|
||||
{
|
||||
char buf[128];
|
||||
uint32_t keysym = ei_event_text_get_keysym(e);
|
||||
#if HAVE_LIBXKBCOMMON
|
||||
xkb_keysym_to_utf8(keysym, buf, sizeof(buf));
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), "0x%04x", keysym);
|
||||
#endif
|
||||
colorprint("text keysym %u [%s] (%s)\n",
|
||||
keysym,
|
||||
buf,
|
||||
ei_event_text_get_keysym_is_press(e) ? "press" : "release");
|
||||
}
|
||||
break;
|
||||
case EI_EVENT_TEXT_UTF8:
|
||||
{
|
||||
colorprint("text utf8 '%s'\n", ei_event_text_get_utf8(e));
|
||||
}
|
||||
break;
|
||||
case EI_EVENT_SYNC:
|
||||
{
|
||||
colorprint("sync\n");
|
||||
|
|
@ -585,6 +627,23 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
if (have_text) {
|
||||
static int key = 0;
|
||||
colorprint("sending text event\n");
|
||||
ei_device_text_keysym(text, XK_dead_a + key, false);
|
||||
ei_device_frame(text, now);
|
||||
now += interval;
|
||||
ei_device_text_keysym(text, XK_dead_a + key, false);
|
||||
ei_device_frame(text, now);
|
||||
now += interval;
|
||||
key = (key + 1) % 6;
|
||||
|
||||
colorprint("sending text utf8 event\n");
|
||||
ei_device_text_utf8(text, "👋 World!");
|
||||
ei_device_frame(text, now);
|
||||
now += interval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -597,13 +656,17 @@ int main(int argc, char **argv)
|
|||
ei_device_close(abs);
|
||||
if (touch)
|
||||
ei_device_close(touch);
|
||||
if (text)
|
||||
ei_device_close(text);
|
||||
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_TEXT,
|
||||
NULL);
|
||||
ei_seat_unref(default_seat);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,10 @@
|
|||
#include <xkbcommon/xkbcommon.h>
|
||||
#endif
|
||||
|
||||
#ifndef XK_ssharp
|
||||
#define XK_ssharp 0x00df /* U+00DF LATIN SMALL LETTER SHARP S */
|
||||
#endif
|
||||
|
||||
#include "src/util-color.h"
|
||||
#include "src/util-mem.h"
|
||||
#include "src/util-memfile.h"
|
||||
|
|
@ -124,6 +128,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->text);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -319,6 +324,17 @@ add_device(struct eis_demo_server *server, struct eis_client *client,
|
|||
case EIS_DEVICE_CAP_SCROLL:
|
||||
/* Mixed in with pointer/abs - good enough for a demo server */
|
||||
break;
|
||||
case EIS_DEVICE_CAP_TEXT:
|
||||
{
|
||||
struct eis_device *text = eis_seat_new_device(seat);
|
||||
eis_device_configure_name(text, "test text device");
|
||||
eis_device_configure_capability(text, EIS_DEVICE_CAP_TEXT);
|
||||
colorprint("Creating text device %s for %s\n", eis_device_get_name(text),
|
||||
eis_client_get_name(client));
|
||||
eis_device_add(text);
|
||||
device = steal(&text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return device;
|
||||
|
|
@ -371,6 +387,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_TEXT);
|
||||
eis_seat_add(seat);
|
||||
/* Note: we don't have a ref to this seat ourselves anywhere */
|
||||
break;
|
||||
|
|
@ -436,12 +453,23 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server,
|
|||
}
|
||||
}
|
||||
|
||||
if (eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TEXT)) {
|
||||
if (!democlient->text)
|
||||
democlient->text = add_device(server, client, seat, EIS_DEVICE_CAP_TEXT);
|
||||
} else {
|
||||
if (democlient->text) {
|
||||
eis_device_remove(democlient->text);
|
||||
democlient->text = eis_device_unref(democlient->text);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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_TEXT))
|
||||
eis_seat_remove(seat);
|
||||
|
||||
break;
|
||||
|
|
@ -479,6 +507,9 @@ eis_demo_server_printf_handle_event(struct eis_demo_server *server,
|
|||
if (democlient->touchscreen == device)
|
||||
democlient->touchscreen = NULL;
|
||||
|
||||
if (democlient->text == device)
|
||||
democlient->text = NULL;
|
||||
|
||||
eis_device_unref(device);
|
||||
}
|
||||
break;
|
||||
|
|
@ -551,6 +582,27 @@ 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_TEXT_KEYSYM:
|
||||
{
|
||||
char buf[128] = {0};
|
||||
uint32_t keysym = eis_event_text_get_keysym(e);
|
||||
#if HAVE_LIBXKBCOMMON
|
||||
xkb_keysym_get_name(keysym, buf, sizeof(buf));
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), "0x%04x", keysym);
|
||||
#endif
|
||||
colorprint("text keysym %u [%s] (%s)\n",
|
||||
keysym,
|
||||
buf,
|
||||
eis_event_text_get_keysym_is_press(e) ? "press" : "release");
|
||||
}
|
||||
break;
|
||||
case EIS_EVENT_TEXT_UTF8:
|
||||
{
|
||||
const char *text = eis_event_text_get_utf8(e);
|
||||
colorprint("text utf8 '%s'\n", text);
|
||||
}
|
||||
break;
|
||||
case EIS_EVENT_FRAME:
|
||||
{
|
||||
colorprint("frame timestamp: %" PRIu64 "\n",
|
||||
|
|
@ -744,6 +796,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 *text = democlient->text;
|
||||
if (ptr) {
|
||||
colorprint("sending motion event\n");
|
||||
eis_device_pointer_motion(ptr, -1, 1);
|
||||
|
|
@ -816,6 +869,23 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
if (text) {
|
||||
static int key = 0;
|
||||
colorprint("sending text event\n");
|
||||
|
||||
eis_device_text_keysym(text, XK_ssharp + key, true); /* KEY_Q */
|
||||
eis_device_frame(text, now);
|
||||
now += interval;
|
||||
eis_device_text_keysym(text, XK_ssharp + key, false); /* KEY_Q */
|
||||
eis_device_frame(text, now);
|
||||
now += interval;
|
||||
key = (key + 1) % 6;
|
||||
|
||||
eis_device_text_utf8(text, "👋🏽 World");
|
||||
eis_device_frame(text, now);
|
||||
now += interval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ struct eis_demo_client {
|
|||
struct eis_device *abs;
|
||||
struct eis_device *touchscreen;
|
||||
struct eis_touch *touch;
|
||||
struct eis_device *text;
|
||||
};
|
||||
|
||||
struct eis_demo_server {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,12 @@ if build_libei
|
|||
|
||||
executable('ei-debug-events',
|
||||
'ei-debug-events.c',
|
||||
dependencies: [dep_libutil, dep_libei, dep_libevdev],
|
||||
dependencies: [
|
||||
dep_libutil,
|
||||
dep_libei,
|
||||
dep_libevdev,
|
||||
dep_libxkbcommon
|
||||
],
|
||||
include_directories: [inc_builddir],
|
||||
install: true,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue