libei/src/libeis-proto.c
Peter Hutterer 8d7d6ca8b7 proto: drop the Packet message, replace with 4 byte prefix
We need some sort of length field to be able to know how long the next
message is. But for simplicity, we might as well just write that
explicitly on the wire instead of wrapping our messages into yet another
message. This makes the wire format slightly simpler since the first 4
bytes are now always the length, without the previous 0x0d prefix
caused by the protobuf encoding.

0x0d == (field number << 3) | wire_type == 1 << 3 | 5
(see https://protobuf.dev/programming-guides/encoding/#structure)
2023-02-09 11:47:45 +10:00

646 lines
18 KiB
C

/* 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.
*/
#include "config.h"
#include <stdbool.h>
#include "util-object.h"
#include "util-io.h"
#include "util-strings.h"
#include "util-tristate.h"
#include "ei.pb-c.h"
#include "libeis-proto.h"
#include "brei-shared.h"
static inline void
log_wire_message(struct eis *eis, const ServerMessage *msg)
{
const char *message = NULL;
#define MSG_STRING_CASE(_name) \
case SERVER_MESSAGE__MSG_##_name: message = #_name; break;
switch (msg->msg_case) {
#if PROTOBUF_C_VERSION_NUMBER >= 1004000
case _SERVER_MESSAGE__MSG__CASE_IS_INT_SIZE:
#else
case _SERVER_MESSAGE__MSG_IS_INT_SIZE:
#endif
/* protobuf-internal thing */
return;
case SERVER_MESSAGE__MSG__NOT_SET:
assert(!"SERVER_MESSAGE__MSG__NOT_SET");
MSG_STRING_CASE(CONNECTED);
MSG_STRING_CASE(DISCONNECTED);
MSG_STRING_CASE(SEAT_ADDED);
MSG_STRING_CASE(SEAT_REMOVED);
MSG_STRING_CASE(DEVICE_ADDED);
MSG_STRING_CASE(DEVICE_DONE);
MSG_STRING_CASE(DEVICE_KEYMAP);
MSG_STRING_CASE(DEVICE_REGION);
MSG_STRING_CASE(DEVICE_REMOVED);
MSG_STRING_CASE(DEVICE_RESUMED);
MSG_STRING_CASE(DEVICE_PAUSED);
MSG_STRING_CASE(KEYBOARD_MODIFIERS);
MSG_STRING_CASE(PROPERTY);
MSG_STRING_CASE(VERSION);
/* events */
MSG_STRING_CASE(START_EMULATING);
MSG_STRING_CASE(STOP_EMULATING);
MSG_STRING_CASE(POINTER_RELATIVE);
MSG_STRING_CASE(POINTER_ABSOLUTE);
MSG_STRING_CASE(POINTER_BUTTON);
MSG_STRING_CASE(POINTER_SCROLL);
MSG_STRING_CASE(POINTER_SCROLL_STOP);
MSG_STRING_CASE(POINTER_SCROLL_DISCRETE);
MSG_STRING_CASE(KEYBOARD_KEY);
MSG_STRING_CASE(TOUCH_DOWN);
MSG_STRING_CASE(TOUCH_MOTION);
MSG_STRING_CASE(TOUCH_UP);
MSG_STRING_CASE(FRAME);
break;
}
if (message == NULL)
assert(!"Unimplemented message type");
log_debug(eis, "sending wire message %s", message);
#undef MSG_STRING_CASE
}
static int
eis_proto_send_msg(struct eis_client *client, const ServerMessage *msg)
{
log_wire_message(eis_client_get_context(client), msg);
size_t msglen = server_message__get_packed_size(msg);
uint8_t buf[4 + msglen];
*(uint32_t*)buf = msglen;
server_message__pack(msg, buf + 4);
return min(0, xsend(source_get_fd(client->source), buf, sizeof(buf)));
}
static int
eis_proto_send_msg_with_fds(struct eis_client *client, const ServerMessage *msg, int *fds)
{
log_wire_message(eis_client_get_context(client), msg);
size_t msglen = server_message__get_packed_size(msg);
uint8_t buf[4 + msglen];
*(uint32_t*)buf = msglen;
server_message__pack(msg, buf + 4);
return min(0, xsend_with_fd(source_get_fd(client->source), buf, sizeof(buf), fds));
}
#define prepare_msg(_type, _struct, _field) \
ServerMessage msg = SERVER_MESSAGE__INIT; \
_struct _field = _type##__INIT; \
msg.msg_case = SERVER_MESSAGE__MSG_##_type; \
msg._field = &_field
static int
eis_proto_send_version(struct eis_client *client, uint32_t v)
{
prepare_msg(VERSION, Version, version);
version.version = v;
version.eis = "EIS";
return eis_proto_send_msg(client, &msg);
}
static int
eis_proto_send_disconnected(struct eis_client *client)
{
prepare_msg(DISCONNECTED, Disconnected, disconnected);
return eis_proto_send_msg(client, &msg);
}
static int
eis_proto_send_connected(struct eis_client *client)
{
prepare_msg(CONNECTED, Connected, connected);
return eis_proto_send_msg(client, &msg);
}
static int
eis_proto_send_seat_added(struct eis_seat *seat)
{
prepare_msg(SEAT_ADDED, SeatAdded, seat_added);
seat_added.seatid = seat->id;
seat_added.name = seat->name;
seat_added.capabilities = seat->capabilities_mask;
return eis_proto_send_msg(eis_seat_get_client(seat), &msg);
}
static int
eis_proto_send_seat_removed(struct eis_seat *seat)
{
prepare_msg(SEAT_REMOVED, SeatRemoved, seat_removed);
seat_removed.seatid = seat->id;
return eis_proto_send_msg(eis_seat_get_client(seat), &msg);
}
static int
eis_proto_send_device_added(struct eis_device *device)
{
prepare_msg(DEVICE_ADDED, DeviceAdded, device_added);
struct eis_seat *seat = eis_device_get_seat(device);
device_added.deviceid = device->id;
device_added.seatid = seat->id;
device_added.name = device->name;
device_added.capabilities = device->capabilities;
device_added.type = device->type;
device_added.width = device->width;
device_added.height = device->height;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_device_keymap(struct eis_device *device)
{
prepare_msg(DEVICE_KEYMAP, DeviceKeymap, device_keymap);
int fds[2] = {-1, -1};
if (!device->keymap)
return 0;
struct eis_keymap *k = device->keymap;
device_keymap.deviceid = device->id;
device_keymap.keymap_type = k->type;
device_keymap.keymap_size = k->size;
fds[0] = k->fd;
return eis_proto_send_msg_with_fds(eis_device_get_client(device), &msg, fds);
}
static int
eis_proto_send_device_done(struct eis_device *device)
{
prepare_msg(DEVICE_DONE, DeviceDone, device_done);
device_done.deviceid = device->id;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_device_region(struct eis_device *device, const struct eis_region *r)
{
prepare_msg(DEVICE_REGION, DeviceRegion, device_region);
device_region.deviceid = device->id;
device_region.offset_x = r->x;
device_region.offset_y = r->y;
device_region.width = r->width;
device_region.height = r->height;
device_region.scale = r->physical_scale;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_keyboard_modifiers(struct eis_device *device, const struct eis_xkb_modifiers *mods)
{
prepare_msg(KEYBOARD_MODIFIERS, KeyboardModifiers, keyboard_modifiers);
keyboard_modifiers.deviceid = device->id;
keyboard_modifiers.depressed = mods->depressed;
keyboard_modifiers.locked = mods->locked;
keyboard_modifiers.latched = mods->latched;
keyboard_modifiers.group = mods->group;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_device_removed(struct eis_device *device)
{
prepare_msg(DEVICE_REMOVED, DeviceRemoved, device_removed);
device_removed.deviceid = device->id;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_device_paused(struct eis_device *device)
{
prepare_msg(DEVICE_PAUSED, DevicePaused, device_paused);
device_paused.deviceid = device->id;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_device_resumed(struct eis_device *device)
{
prepare_msg(DEVICE_RESUMED, DeviceResumed, device_resumed);
device_resumed.deviceid = device->id;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_property(struct eis_client *client, const char *name,
const char *value, uint32_t permissions)
{
prepare_msg(PROPERTY, Property, property);
property.name = (char*)name;
property.value = (char*)value;
property.permissions = permissions;
return eis_proto_send_msg(client, &msg);
}
static int
eis_proto_send_start_emulating(struct eis_device *device, uint32_t deviceid)
{
prepare_msg(START_EMULATING, StartEmulating, start_emulating);
start_emulating.deviceid = device->id;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_stop_emulating(struct eis_device *device, uint32_t deviceid)
{
prepare_msg(STOP_EMULATING, StopEmulating, stop_emulating);
stop_emulating.deviceid = device->id;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_rel(struct eis_device *device, uint32_t deviceid, double x, double y)
{
prepare_msg(POINTER_RELATIVE, PointerRelative, pointer_relative);
pointer_relative.deviceid = device->id;
pointer_relative.x = x;
pointer_relative.y = y;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_abs(struct eis_device *device, uint32_t deviceid, double x, double y)
{
prepare_msg(POINTER_ABSOLUTE, PointerAbsolute, pointer_absolute);
pointer_absolute.deviceid = device->id;
pointer_absolute.x = x;
pointer_absolute.y = y;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_button(struct eis_device *device, uint32_t deviceid,
uint32_t button, bool is_press)
{
prepare_msg(POINTER_BUTTON, PointerButton, pointer_button);
pointer_button.deviceid = device->id;
pointer_button.button = button;
pointer_button.state = is_press;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_key(struct eis_device *device, uint32_t deviceid,
uint32_t key, bool is_press)
{
prepare_msg(KEYBOARD_KEY, KeyboardKey, keyboard_key);
keyboard_key.deviceid = device->id;
keyboard_key.key = key;
keyboard_key.state = is_press;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_scroll(struct eis_device *device, uint32_t deviceid,
double x, double y)
{
prepare_msg(POINTER_SCROLL, PointerScroll, pointer_scroll);
pointer_scroll.deviceid = device->id;
pointer_scroll.x = x;
pointer_scroll.y = y;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_scroll_stop(struct eis_device *device, uint32_t deviceid,
bool x, bool y, bool is_cancel)
{
prepare_msg(POINTER_SCROLL_STOP, PointerScrollStop, pointer_scroll_stop);
pointer_scroll_stop.deviceid = device->id;
pointer_scroll_stop.x = x;
pointer_scroll_stop.y = y;
pointer_scroll_stop.is_cancel = is_cancel;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_scroll_discrete(struct eis_device *device, uint32_t deviceid,
int32_t x, int32_t y)
{
prepare_msg(POINTER_SCROLL_DISCRETE, PointerScrollDiscrete, pointer_scroll_discrete);
pointer_scroll_discrete.deviceid = device->id;
pointer_scroll_discrete.x = x;
pointer_scroll_discrete.y = y;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_touch_down(struct eis_device *device, uint32_t deviceid,
uint32_t tid, double x, double y)
{
prepare_msg(TOUCH_DOWN, TouchDown, touch_down);
touch_down.deviceid = device->id;
touch_down.touchid = tid;
touch_down.x = x;
touch_down.y = y;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_touch_motion(struct eis_device *device, uint32_t deviceid,
uint32_t tid, double x, double y)
{
prepare_msg(TOUCH_MOTION, TouchMotion, touch_motion);
touch_motion.deviceid = device->id;
touch_motion.touchid = tid;
touch_motion.x = x;
touch_motion.y = y;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_touch_up(struct eis_device *device, uint32_t deviceid, uint32_t tid)
{
prepare_msg(TOUCH_UP, TouchUp, touch_up);
touch_up.deviceid = device->id;
touch_up.touchid = tid;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static int
eis_proto_send_frame(struct eis_device *device, uint32_t deviceid, uint64_t time)
{
prepare_msg(FRAME, Frame, frame);
frame.deviceid = device->id;
frame.timestamp = time;
return eis_proto_send_msg(eis_device_get_client(device), &msg);
}
static const struct eis_proto_requests requests = {
.disconnected = eis_proto_send_disconnected,
.connected = eis_proto_send_connected,
.seat_added = eis_proto_send_seat_added,
.seat_removed = eis_proto_send_seat_removed,
.device_added = eis_proto_send_device_added,
.device_removed = eis_proto_send_device_removed,
.device_paused = eis_proto_send_device_paused,
.device_resumed = eis_proto_send_device_resumed,
.device_keymap = eis_proto_send_device_keymap,
.device_done = eis_proto_send_device_done,
.device_region = eis_proto_send_device_region,
.keyboard_modifiers = eis_proto_send_keyboard_modifiers,
.property = eis_proto_send_property,
.version = eis_proto_send_version,
/* events */
.start_emulating = eis_proto_send_start_emulating,
.stop_emulating = eis_proto_send_stop_emulating,
.rel = eis_proto_send_rel,
.abs = eis_proto_send_abs,
.button = eis_proto_send_button,
.scroll = eis_proto_send_scroll,
.scroll_stop = eis_proto_send_scroll_stop,
.scroll_discrete = eis_proto_send_scroll_discrete,
.key = eis_proto_send_key,
.touch_down = eis_proto_send_touch_down,
.touch_motion = eis_proto_send_touch_motion,
.touch_up = eis_proto_send_touch_up,
.frame = eis_proto_send_frame,
};
const struct eis_proto_requests *
eis_proto_get_requests(void) {
return &requests;
}
static void clientmessage_cleanup(ClientMessage **m) {
if (*m)
client_message__free_unpacked(*m, NULL);
}
#define _cleanup_clientmessage_ _cleanup_(clientmessage_cleanup)
int
eis_proto_handle_message(struct eis_client *client,
const struct eis_proto_interface *interface,
struct brei_message *bmsg)
{
_cleanup_clientmessage_ ClientMessage *proto = client_message__unpack(NULL, bmsg->len,
(const unsigned char*)bmsg->data);
if (!proto)
return -EAGAIN;
#define call(field, ...) \
({ \
int r = (interface->field == NULL) ? -EPROTO : interface->field(__VA_ARGS__); \
log_debug(eis_client_get_context(client), "message type '" #field "': errno %d (%s)", -r, strerror(-r)); \
r; \
})
int rc;
switch (proto->msg_case) {
case CLIENT_MESSAGE__MSG_CONNECT:
rc = call(connect, client, proto->connect->version,
proto->connect->name, proto->connect->is_sender);
break;
case CLIENT_MESSAGE__MSG_CONNECT_DONE:
rc = call(connect_done, client);
break;
case CLIENT_MESSAGE__MSG_DISCONNECT:
rc = call(disconnect, client);
break;
case CLIENT_MESSAGE__MSG_BIND_SEAT:
rc = call(bind_seat, client,
proto->bind_seat->seatid,
proto->bind_seat->capabilities);
break;
case CLIENT_MESSAGE__MSG_CLOSE_DEVICE:
rc = call(close_device, client,
proto->close_device->deviceid);
break;
case CLIENT_MESSAGE__MSG_SET_PROPERTY:
rc = call(set_property, client, proto->set_property->name,
proto->set_property->value[0] ? proto->set_property->value : NULL,
proto->set_property->permissions);
break;
case CLIENT_MESSAGE__MSG_GET_VERSION:
rc = call(get_version, client);
break;
/* Events */
case CLIENT_MESSAGE__MSG_START_EMULATING:
rc = call(start_emulating, client,
proto->start_emulating->deviceid);
break;
case CLIENT_MESSAGE__MSG_STOP_EMULATING:
rc = call(stop_emulating, client,
proto->stop_emulating->deviceid);
break;
case CLIENT_MESSAGE__MSG_POINTER_RELATIVE:
rc = call(rel, client,
proto->pointer_relative->deviceid,
proto->pointer_relative->x,
proto->pointer_relative->y);
break;
case CLIENT_MESSAGE__MSG_POINTER_ABSOLUTE:
rc = call(abs, client,
proto->pointer_absolute->deviceid,
proto->pointer_absolute->x,
proto->pointer_absolute->y);
break;
case CLIENT_MESSAGE__MSG_POINTER_BUTTON:
rc = call(button, client,
proto->pointer_button->deviceid,
proto->pointer_button->button,
proto->pointer_button->state);
break;
case CLIENT_MESSAGE__MSG_POINTER_SCROLL:
rc = call(scroll, client,
proto->pointer_scroll->deviceid,
proto->pointer_scroll->x,
proto->pointer_scroll->y);
break;
case CLIENT_MESSAGE__MSG_POINTER_SCROLL_STOP:
rc = call(scroll_stop, client,
proto->pointer_scroll_stop->deviceid,
proto->pointer_scroll_stop->x,
proto->pointer_scroll_stop->y,
proto->pointer_scroll_stop->is_cancel);
break;
case CLIENT_MESSAGE__MSG_POINTER_SCROLL_DISCRETE:
rc = call(scroll_discrete, client,
proto->pointer_scroll_discrete->deviceid,
proto->pointer_scroll_discrete->x,
proto->pointer_scroll_discrete->y);
break;
case CLIENT_MESSAGE__MSG_KEYBOARD_KEY:
rc = call(key, client,
proto->keyboard_key->deviceid,
proto->keyboard_key->key,
proto->keyboard_key->state);
break;
case CLIENT_MESSAGE__MSG_TOUCH_DOWN:
rc = call(touch_down, client,
proto->touch_down->deviceid,
proto->touch_down->touchid,
proto->touch_down->x,
proto->touch_down->y);
break;
case CLIENT_MESSAGE__MSG_TOUCH_MOTION:
rc = call(touch_motion, client,
proto->touch_motion->deviceid,
proto->touch_motion->touchid,
proto->touch_motion->x,
proto->touch_motion->y);
break;
case CLIENT_MESSAGE__MSG_TOUCH_UP:
rc = call(touch_up, client,
proto->touch_up->deviceid,
proto->touch_up->touchid);
break;
case CLIENT_MESSAGE__MSG_FRAME:
rc = call(frame, client, proto->frame->deviceid, proto->frame->timestamp);
break;
case CLIENT_MESSAGE__MSG_CONFIGURE_START:
rc = call(configure_start, client,
proto->configure_start->version);
break;
case CLIENT_MESSAGE__MSG_CONFIGURE_FINISH:
rc = call(configure_finish, client);
break;
case CLIENT_MESSAGE__MSG_CONFIGURE_NAME:
rc = call(configure_name, client,
proto->configure_name->name);
break;
case CLIENT_MESSAGE__MSG_CONFIGURE_CAPABILITIES:
rc = call(configure_capabilities, client,
proto->configure_capabilities->allowed_capabilities);
break;
case CLIENT_MESSAGE__MSG_CONFIGURE_PROPERTY:
rc = call(configure_property, client, proto->configure_property->name,
proto->configure_property->value[0] ? proto->configure_property->value : NULL,
proto->configure_property->permissions);
break;
default:
rc = -EBADMSG;
break;
}
return rc < 0 ? rc : (int)bmsg->len;
}