libei/src/libei-proto.c
Peter Hutterer 56b82c2b8f Add DeviceRegions to replace the pointer/touch ranges
This is required for supporting synergy/barrier and similar clients.
Replacing the touch and pointer range we now have server-defined
rectangular regions that specify the active zones for this device.

For example, a dual-monitor EIS server would create two touch devices
with one region each for the respective monitors - libei-generated
touches would thus fall on the right area of the monitor. Or just one
device with one region if the second screen should be inaccessible.

A relative device may have multiple regions since it can reach all
screens in the layout.

This leaks the screen layout to libei but that is necessary for the
functionality to work. A libei client may need to control devices
through absolute coordinates and it needs to know where screen
transitions from one to the next screen happen:

  +-----------++----------------+
  |           ||                |
  |          B||Q               |
  |           |+----------------+
  |           |
  |          A|P
  +-----------+

In the above example, position P is unreachable and a client that
controls input on both screens must know that it cannot transition from
A to P but it can transition from B to Q.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2021-07-23 15:06:01 +10:00

474 lines
11 KiB
C

/*
* 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 "proto/ei.pb-c.h"
#include "libei-proto.h"
#include "brei-shared.h"
void
message_free(struct message *msg)
{
switch (msg->type) {
case MESSAGE_SEAT_ADDED:
free(msg->seat_added.name);
break;
case MESSAGE_DEVICE_ADDED:
xclose(msg->device_added.keymap_fd);
free(msg->device_added.name);
break;
default:
break;
}
free(msg);
}
static void servermessage_cleanup(ServerMessage **m) {
if (*m)
server_message__free_unpacked(*m, NULL);
}
#define _cleanup_servermessage_ _cleanup_(servermessage_cleanup)
struct message *
ei_proto_parse_message(struct brei_message *bmsg, size_t *consumed)
{
_cleanup_servermessage_ ServerMessage *proto = server_message__unpack(NULL,
bmsg->len,
(const unsigned char*)bmsg->data);
if (!proto)
return NULL;
*consumed = bmsg->len;
_cleanup_message_ struct message *msg = xalloc(sizeof(*msg));
bool success = true;
switch (proto->msg_case) {
case SERVER_MESSAGE__MSG_CONNECTED:
*msg = (struct message) {
.type = MESSAGE_CONNECTED,
};
break;
case SERVER_MESSAGE__MSG_DISCONNECTED:
*msg = (struct message) {
.type = MESSAGE_DISCONNECTED,
};
break;
case SERVER_MESSAGE__MSG_SEAT_ADDED:
{
SeatAdded *a = proto->seat_added;
*msg = (struct message) {
.type = MESSAGE_SEAT_ADDED,
.seat_added.seatid = a->seatid,
.seat_added.name = xstrdup(a->name),
.seat_added.capabilities = a->capabilities,
};
}
break;
case SERVER_MESSAGE__MSG_SEAT_REMOVED:
{
SeatRemoved *r = proto->seat_removed;
*msg = (struct message) {
.type = MESSAGE_SEAT_REMOVED,
.seat_removed.seatid = r->seatid,
};
}
break;
case SERVER_MESSAGE__MSG_DEVICE_ADDED:
{
DeviceAdded *a = proto->device_added;
*msg = (struct message) {
.type = MESSAGE_DEVICE_ADDED,
.device_added.deviceid = a->deviceid,
.device_added.name = a->name[0] ? xstrdup(a->name) : NULL,
.device_added.capabilities = a->capabilities,
.device_added.keymap_fd = -1,
.device_added.keymap_type = a->keymap_type,
.device_added.keymap_from_server = a->keymap_from_server,
.device_added.keymap_size = a->keymap_size,
.device_added.seatid = a->seatid,
};
if (a->keymap_type && a->keymap_from_server)
msg->device_added.keymap_fd = brei_message_take_fd(bmsg);
}
break;
case SERVER_MESSAGE__MSG_DEVICE_ADDED_DONE:
{
DeviceAddedDone *d = proto->device_added_done;
*msg = (struct message) {
.type = MESSAGE_DEVICE_ADDED_DONE,
.device_added_done.deviceid = d->deviceid,
};
}
break;
case SERVER_MESSAGE__MSG_DEVICE_REGION:
{
DeviceRegion *r = proto->device_region;
*msg = (struct message) {
.type = MESSAGE_DEVICE_REGION,
.device_region.deviceid = r->deviceid,
.device_region.x = r->offset_x,
.device_region.y = r->offset_y,
.device_region.w = r->width,
.device_region.h = r->height,
};
}
break;
case SERVER_MESSAGE__MSG_DEVICE_REMOVED:
{
DeviceRemoved *r = proto->device_removed;
*msg = (struct message) {
.type = MESSAGE_DEVICE_REMOVED,
.device_removed.deviceid = r->deviceid,
};
}
break;
case SERVER_MESSAGE__MSG_DEVICE_RESUMED:
{
DeviceResumed *r = proto->device_resumed;
*msg = (struct message) {
.type = MESSAGE_DEVICE_RESUMED,
.resumed.deviceid = r->deviceid,
};
}
break;
case SERVER_MESSAGE__MSG_DEVICE_SUSPENDED:
{
DeviceSuspended *r = proto->device_suspended;
*msg = (struct message) {
.type = MESSAGE_DEVICE_SUSPENDED,
.suspended.deviceid = r->deviceid,
};
}
break;
default:
success = false;
break;
}
return success ? steal(&msg) : NULL;
}
static inline void
log_wire_message(struct ei *ei, const ClientMessage *msg, int error)
{
const char *message = NULL;
#define MSG_STRING_CASE(_name) \
case CLIENT_MESSAGE__MSG_##_name: message = #_name; break;
switch (msg->msg_case) {
case CLIENT_MESSAGE__MSG__NOT_SET:
abort();
MSG_STRING_CASE(CONNECT);
MSG_STRING_CASE(DISCONNECT);
MSG_STRING_CASE(BIND_SEAT);
MSG_STRING_CASE(UNBIND_SEAT);
MSG_STRING_CASE(CLOSE_DEVICE);
MSG_STRING_CASE(REL);
MSG_STRING_CASE(ABS);
MSG_STRING_CASE(BUTTON);
MSG_STRING_CASE(SCROLL);
MSG_STRING_CASE(DISC);
MSG_STRING_CASE(KEY);
MSG_STRING_CASE(TOUCH);
MSG_STRING_CASE(CONFIGURE_NAME);
MSG_STRING_CASE(CONFIGURE_CAPS);
default:
assert(!"Unimplemented message type");
break;
}
log_debug(ei, "sending wire message %s (%s)\n", message,
strerror(-error));
#undef MSG_STRING_CASE
}
static int
ei_proto_send_msg(struct ei *ei, const ClientMessage *msg)
{
size_t msglen = client_message__get_packed_size(msg);
Frame frame = FRAME__INIT;
frame.length = msglen;
size_t framelen = frame__get_packed_size(&frame);
uint8_t buf[framelen + msglen];
frame__pack(&frame, buf);
client_message__pack(msg, buf + framelen);
int rc = min(0, xsend(source_get_fd(ei->source), buf, sizeof(buf)));
log_wire_message(ei, msg, rc);
return rc;
}
_unused_ static int
ei_proto_send_msg_with_fds(struct ei *ei, const ClientMessage *msg, int *fds)
{
size_t msglen = client_message__get_packed_size(msg);
Frame frame = FRAME__INIT;
frame.length = msglen;
size_t framelen = frame__get_packed_size(&frame);
uint8_t buf[framelen + msglen];
frame__pack(&frame, buf);
client_message__pack(msg, buf + framelen);
int rc = min(0, xsend_with_fd(source_get_fd(ei->source), buf, sizeof(buf), fds));
log_wire_message(ei, msg, rc);
return rc;
}
int
ei_proto_send_connect(struct ei *ei)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
Connect connect = CONNECT__INIT;
connect.name = ei->name;
msg.connect = &connect;
msg.msg_case = CLIENT_MESSAGE__MSG_CONNECT;
return ei_proto_send_msg(ei, &msg);
}
int
ei_proto_send_disconnect(struct ei *ei)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
Disconnect disconnect = DISCONNECT__INIT;
msg.disconnect = &disconnect;
msg.msg_case = CLIENT_MESSAGE__MSG_DISCONNECT;
return ei_proto_send_msg(ei, &msg);
}
int
ei_proto_send_bind_seat(struct ei *ei, struct ei_seat *seat, uint32_t capabilities)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
BindSeat bind = BIND_SEAT__INIT;
bind.seatid = seat->id;
bind.capabilities = capabilities;
msg.bind_seat = &bind;
msg.msg_case = CLIENT_MESSAGE__MSG_BIND_SEAT;
return ei_proto_send_msg(ei, &msg);
}
int
ei_proto_send_unbind_seat(struct ei *ei, struct ei_seat *seat)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
UnbindSeat unbind = UNBIND_SEAT__INIT;
unbind.seatid = seat->id;
msg.unbind_seat = &unbind;
msg.msg_case = CLIENT_MESSAGE__MSG_UNBIND_SEAT;
return ei_proto_send_msg(ei, &msg);
}
int
ei_proto_send_close_device(struct ei *ei, struct ei_device *device)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
CloseDevice close = CLOSE_DEVICE__INIT;
close.deviceid = device->id;
msg.close_device = &close;
msg.msg_case = CLIENT_MESSAGE__MSG_CLOSE_DEVICE;
return ei_proto_send_msg(ei, &msg);
}
int
ei_proto_send_rel(struct ei *ei, struct ei_device *device,
double x, double y)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
PointerRelative rel = POINTER_RELATIVE__INIT;
rel.deviceid = device->id;
rel.x = x;
rel.y = y;
msg.rel = &rel;
msg.msg_case = CLIENT_MESSAGE__MSG_REL;
return ei_proto_send_msg(ei, &msg);
}
int
ei_proto_send_abs(struct ei *ei, struct ei_device *device,
double x, double y)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
PointerAbsolute abs = POINTER_ABSOLUTE__INIT;
abs.deviceid = device->id;
abs.x = x;
abs.y = y;
msg.abs = &abs;
msg.msg_case = CLIENT_MESSAGE__MSG_ABS;
return ei_proto_send_msg(ei, &msg);
}
int
ei_proto_send_button(struct ei *ei, struct ei_device *device,
uint32_t b, bool is_press)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
PointerButton button = POINTER_BUTTON__INIT;
button.deviceid = device->id;
button.button = b;
button.state = is_press;
msg.button = &button;
msg.msg_case = CLIENT_MESSAGE__MSG_BUTTON;
return ei_proto_send_msg(ei, &msg);
}
int ei_proto_send_scroll(struct ei *ei, struct ei_device *device,
double x, double y)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
Scroll scroll = SCROLL__INIT;
scroll.deviceid = device->id;
scroll.x = x;
scroll.y = y;
msg.scroll = &scroll;
msg.msg_case = CLIENT_MESSAGE__MSG_SCROLL;
return ei_proto_send_msg(ei, &msg);
}
int ei_proto_send_scroll_discrete(struct ei *ei, struct ei_device *device,
int32_t x, int32_t y)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
ScrollDiscrete disc = SCROLL_DISCRETE__INIT;
disc.deviceid = device->id;
disc.x = x;
disc.y = y;
msg.disc = &disc;
msg.msg_case = CLIENT_MESSAGE__MSG_DISC;
return ei_proto_send_msg(ei, &msg);
}
int
ei_proto_send_key(struct ei *ei, struct ei_device *device,
uint32_t k, bool is_press)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
KeyboardKey key = KEYBOARD_KEY__INIT;
key.deviceid = device->id;
key.key = k;
key.state = is_press;
msg.key = &key;
msg.msg_case = CLIENT_MESSAGE__MSG_KEY;
return ei_proto_send_msg(ei, &msg);
}
int
ei_proto_send_touch_down(struct ei *ei, struct ei_device *device,
uint32_t tid, double x, double y)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
Touch touch = TOUCH__INIT;
touch.deviceid = device->id;
touch.touchid = tid;
touch.is_down = true;
touch.is_up = false;
touch.x = x;
touch.y = y;
msg.touch = &touch;
msg.msg_case = CLIENT_MESSAGE__MSG_TOUCH;
return ei_proto_send_msg(ei, &msg);
}
int
ei_proto_send_touch_motion(struct ei *ei, struct ei_device *device,
uint32_t tid, double x, double y)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
Touch touch = TOUCH__INIT;
touch.deviceid = device->id;
touch.touchid = tid;
touch.is_down = false;
touch.is_up = false;
touch.x = x;
touch.y = y;
msg.touch = &touch;
msg.msg_case = CLIENT_MESSAGE__MSG_TOUCH;
return ei_proto_send_msg(ei, &msg);
}
int
ei_proto_send_touch_up(struct ei *ei, struct ei_device *device, uint32_t tid)
{
ClientMessage msg = CLIENT_MESSAGE__INIT;
Touch touch = TOUCH__INIT;
touch.deviceid = device->id;
touch.touchid = tid;
touch.is_down = false;
touch.is_up = true;
msg.touch = &touch;
msg.msg_case = CLIENT_MESSAGE__MSG_TOUCH;
return ei_proto_send_msg(ei, &msg);
}