2020-07-14 14:41:32 +10:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-07-29 11:53:03 +10:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
#include "util-io.h"
|
|
|
|
|
#include "util-logger.h"
|
|
|
|
|
#include "util-macros.h"
|
|
|
|
|
#include "util-object.h"
|
|
|
|
|
#include "util-sources.h"
|
|
|
|
|
#include "util-strings.h"
|
|
|
|
|
|
2020-07-14 14:41:32 +10:00
|
|
|
#include "libei.h"
|
2020-07-29 11:53:03 +10:00
|
|
|
#include "libei-private.h"
|
|
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
#include "proto/ei.pb-c.h"
|
|
|
|
|
|
2020-07-29 11:53:03 +10:00
|
|
|
/* The message type for the wire format */
|
|
|
|
|
enum message_type {
|
|
|
|
|
MESSAGE_HELLO,
|
|
|
|
|
MESSAGE_CONNECTED,
|
|
|
|
|
MESSAGE_DISCONNECTED,
|
2020-08-04 10:29:33 +10:00
|
|
|
MESSAGE_ACCEPTED,
|
|
|
|
|
MESSAGE_REMOVED,
|
2020-07-29 11:53:03 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct message {
|
|
|
|
|
enum message_type type;
|
|
|
|
|
union {
|
|
|
|
|
struct message_hello {
|
|
|
|
|
uint8_t pad; /* no data */
|
|
|
|
|
} hello;
|
|
|
|
|
struct message_connected {
|
|
|
|
|
uint8_t pad; /* no data */
|
|
|
|
|
} connected;
|
|
|
|
|
struct message_disconnected {
|
|
|
|
|
uint8_t pad; /* no data */
|
|
|
|
|
} disconnected;
|
2020-08-04 10:29:33 +10:00
|
|
|
struct message_accepted {
|
2020-07-29 11:53:03 +10:00
|
|
|
uint32_t deviceid;
|
2020-08-04 10:29:33 +10:00
|
|
|
} accepted;
|
|
|
|
|
struct message_removed {
|
2020-07-29 11:53:03 +10:00
|
|
|
uint32_t deviceid;
|
2020-08-04 10:29:33 +10:00
|
|
|
} removed;
|
2020-07-29 11:53:03 +10:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
message_free(struct message *msg)
|
|
|
|
|
{
|
|
|
|
|
switch (msg->type) {
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
free(msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEFINE_TRIVIAL_CLEANUP_FUNC(struct message*, message_free);
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ei_event_destroy(struct ei_event *event)
|
|
|
|
|
{
|
|
|
|
|
switch (event->type) {
|
|
|
|
|
case EI_EVENT_CONNECT:
|
|
|
|
|
case EI_EVENT_DISCONNECT:
|
|
|
|
|
case EI_EVENT_DEVICE_ADDED:
|
|
|
|
|
case EI_EVENT_DEVICE_REMOVED:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
abort(); /* not yet implemented */
|
|
|
|
|
}
|
|
|
|
|
ei_device_unref(event->device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static
|
2020-08-03 07:33:32 +10:00
|
|
|
OBJECT_IMPLEMENT_INIT(ei_event);
|
2020-08-06 13:59:20 +10:00
|
|
|
static
|
|
|
|
|
OBJECT_IMPLEMENT_REF(ei_event);
|
2020-07-29 11:53:03 +10:00
|
|
|
_public_
|
2020-08-03 07:33:32 +10:00
|
|
|
OBJECT_IMPLEMENT_UNREF(ei_event);
|
2020-07-29 11:53:03 +10:00
|
|
|
_public_
|
2020-08-03 07:33:32 +10:00
|
|
|
OBJECT_IMPLEMENT_GETTER(ei_event, type, enum ei_event_type);
|
2020-07-29 11:53:03 +10:00
|
|
|
_public_
|
2020-08-03 07:33:32 +10:00
|
|
|
OBJECT_IMPLEMENT_GETTER(ei_event, device, struct ei_device*);
|
2020-07-29 11:53:03 +10:00
|
|
|
|
2020-08-07 11:28:39 +10:00
|
|
|
static void
|
|
|
|
|
ei_disconnect(struct ei *ei);
|
|
|
|
|
|
2020-07-29 11:53:03 +10:00
|
|
|
static void
|
|
|
|
|
ei_destroy(struct ei *ei)
|
|
|
|
|
{
|
2020-08-07 11:28:39 +10:00
|
|
|
ei_disconnect(ei);
|
2020-08-07 11:39:50 +10:00
|
|
|
|
|
|
|
|
struct ei_event *e;
|
|
|
|
|
while ((e = ei_get_event(ei)) != NULL)
|
|
|
|
|
ei_event_unref(e);
|
|
|
|
|
|
2020-07-29 11:53:03 +10:00
|
|
|
ei->logger = logger_unref(ei->logger);
|
2020-08-05 16:09:27 +10:00
|
|
|
if (ei->backend_interface.destroy)
|
|
|
|
|
ei->backend_interface.destroy(ei, ei->backend);
|
|
|
|
|
ei->backend = NULL;
|
2020-07-29 11:53:03 +10:00
|
|
|
sink_unref(ei->sink);
|
2020-07-30 09:40:16 +10:00
|
|
|
free(ei->name);
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
2020-08-05 16:09:27 +10:00
|
|
|
OBJECT_IMPLEMENT_CREATE(ei);
|
2020-07-29 11:53:03 +10:00
|
|
|
_public_
|
2020-08-03 07:33:32 +10:00
|
|
|
OBJECT_IMPLEMENT_REF(ei);
|
2020-07-29 11:53:03 +10:00
|
|
|
_public_
|
2020-08-03 07:33:32 +10:00
|
|
|
OBJECT_IMPLEMENT_UNREF(ei);
|
2020-07-29 11:53:03 +10:00
|
|
|
#define _cleanup_ei_ _cleanup_(ei_cleanup)
|
|
|
|
|
_public_
|
2020-08-03 07:33:32 +10:00
|
|
|
OBJECT_IMPLEMENT_SETTER(ei, user_data, void *);
|
|
|
|
|
OBJECT_IMPLEMENT_GETTER(ei, user_data, void *);
|
2020-07-29 11:53:03 +10:00
|
|
|
|
2020-08-05 16:09:27 +10:00
|
|
|
_public_ struct ei *
|
|
|
|
|
ei_new(void *user_data)
|
2020-07-29 11:53:03 +10:00
|
|
|
{
|
2020-08-05 16:09:27 +10:00
|
|
|
_cleanup_ei_ struct ei *ei = ei_create(NULL);
|
|
|
|
|
|
2020-08-07 11:28:39 +10:00
|
|
|
ei->state = EI_STATE_NEW;
|
2020-07-29 11:53:03 +10:00
|
|
|
list_init(&ei->event_queue);
|
|
|
|
|
list_init(&ei->devices);
|
|
|
|
|
|
|
|
|
|
ei->logger = logger_new(ei);
|
|
|
|
|
logger_set_priority(ei->logger, LOGGER_DEBUG);
|
|
|
|
|
ei->sink = sink_new();
|
|
|
|
|
if (!ei->sink)
|
2020-08-05 16:09:27 +10:00
|
|
|
return NULL;
|
2020-07-29 11:53:03 +10:00
|
|
|
|
2020-08-05 16:09:27 +10:00
|
|
|
ei->user_data = user_data;
|
|
|
|
|
ei->backend = NULL;
|
|
|
|
|
|
|
|
|
|
return steal(&ei);
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_public_ int
|
|
|
|
|
ei_get_fd(struct ei *ei)
|
|
|
|
|
{
|
|
|
|
|
return sink_get_fd(ei->sink);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_public_ void
|
|
|
|
|
ei_dispatch(struct ei *ei)
|
|
|
|
|
{
|
|
|
|
|
sink_dispatch(ei->sink);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ei_queue_event(struct ei *ei, struct ei_event *event)
|
|
|
|
|
{
|
|
|
|
|
list_append(&ei->event_queue, &event->link);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ei_queue_connect_event(struct ei *ei)
|
|
|
|
|
{
|
|
|
|
|
struct ei_event_client *e = xalloc(sizeof(*e));
|
|
|
|
|
ei_event_init_object(&e->base, &ei->object);
|
|
|
|
|
e->base.type = EI_EVENT_CONNECT;
|
|
|
|
|
|
|
|
|
|
ei_queue_event(ei, &e->base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ei_queue_disconnect_event(struct ei *ei)
|
|
|
|
|
{
|
|
|
|
|
struct ei_event_client *e = xalloc(sizeof(*e));
|
|
|
|
|
ei_event_init_object(&e->base, &ei->object);
|
|
|
|
|
e->base.type = EI_EVENT_DISCONNECT;
|
|
|
|
|
|
|
|
|
|
ei_queue_event(ei, &e->base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ei_queue_added_event(struct ei_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct ei *ei= ei_device_get_context(device);
|
|
|
|
|
|
|
|
|
|
struct ei_event_client *e = xalloc(sizeof(*e));
|
|
|
|
|
ei_event_init_object(&e->base, &ei->object);
|
|
|
|
|
e->base.type = EI_EVENT_DEVICE_ADDED;
|
|
|
|
|
e->base.device = ei_device_ref(device);
|
|
|
|
|
|
|
|
|
|
ei_queue_event(ei, &e->base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ei_queue_removed_event(struct ei_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct ei *ei= ei_device_get_context(device);
|
|
|
|
|
|
|
|
|
|
struct ei_event_client *e = xalloc(sizeof(*e));
|
|
|
|
|
ei_event_init_object(&e->base, &ei->object);
|
|
|
|
|
e->base.type = EI_EVENT_DEVICE_REMOVED;
|
|
|
|
|
e->base.device = ei_device_ref(device);
|
|
|
|
|
|
|
|
|
|
ei_queue_event(ei, &e->base);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
static int
|
|
|
|
|
connection_send_msg(struct ei *ei, const ClientMessage *msg)
|
|
|
|
|
{
|
2020-08-03 11:59:05 +10:00
|
|
|
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);
|
|
|
|
|
return min(0, xsend(source_get_fd(ei->source), buf, sizeof(buf)));
|
2020-07-31 12:24:44 +10:00
|
|
|
}
|
|
|
|
|
|
2020-07-29 11:53:03 +10:00
|
|
|
static int
|
|
|
|
|
connection_send_connect(struct ei *ei)
|
|
|
|
|
{
|
2020-08-07 11:28:39 +10:00
|
|
|
if (ei->state == EI_STATE_NEW ||
|
|
|
|
|
ei->state == EI_STATE_DISCONNECTED)
|
2020-07-29 11:53:03 +10:00
|
|
|
return 0;
|
|
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
ClientMessage msg = CLIENT_MESSAGE__INIT;
|
|
|
|
|
Connect connect = CONNECT__INIT;
|
2020-07-30 09:40:16 +10:00
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
msg.connect = &connect;
|
|
|
|
|
msg.msg_case = CLIENT_MESSAGE__MSG_CONNECT;
|
2020-07-30 09:40:16 +10:00
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
return connection_send_msg(ei, &msg);
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
connection_send_disconnect(struct ei *ei)
|
|
|
|
|
{
|
2020-08-07 11:28:39 +10:00
|
|
|
if (ei->state == EI_STATE_NEW ||
|
|
|
|
|
ei->state == EI_STATE_DISCONNECTED)
|
2020-07-29 11:53:03 +10:00
|
|
|
return 0;
|
|
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
ClientMessage msg = CLIENT_MESSAGE__INIT;
|
|
|
|
|
Disconnect disconnect = DISCONNECT__INIT;
|
|
|
|
|
|
|
|
|
|
msg.disconnect = &disconnect;
|
|
|
|
|
msg.msg_case = CLIENT_MESSAGE__MSG_DISCONNECT;
|
|
|
|
|
|
|
|
|
|
return connection_send_msg(ei, &msg);
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
connection_send_add(struct ei *ei, struct ei_device *device)
|
|
|
|
|
{
|
2020-08-07 11:28:39 +10:00
|
|
|
if (ei->state == EI_STATE_NEW ||
|
|
|
|
|
ei->state == EI_STATE_DISCONNECTED)
|
2020-07-29 11:53:03 +10:00
|
|
|
return 0;
|
|
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
ClientMessage msg = CLIENT_MESSAGE__INIT;
|
|
|
|
|
AddDevice add = ADD_DEVICE__INIT;
|
2020-07-29 11:53:03 +10:00
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
add.deviceid = device->id;
|
|
|
|
|
add.capabilities = device->capabilities;
|
2020-07-29 11:53:03 +10:00
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
msg.add = &add;
|
|
|
|
|
msg.msg_case = CLIENT_MESSAGE__MSG_ADD;
|
|
|
|
|
|
|
|
|
|
return connection_send_msg(ei, &msg);
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
connection_send_remove(struct ei *ei, struct ei_device *device)
|
|
|
|
|
{
|
2020-08-07 11:28:39 +10:00
|
|
|
if (ei->state == EI_STATE_NEW ||
|
|
|
|
|
ei->state == EI_STATE_DISCONNECTED)
|
2020-07-29 11:53:03 +10:00
|
|
|
return 0;
|
|
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
ClientMessage msg = CLIENT_MESSAGE__INIT;
|
|
|
|
|
RemoveDevice remove = REMOVE_DEVICE__INIT;
|
|
|
|
|
|
|
|
|
|
remove.deviceid = device->id;
|
2020-07-29 11:53:03 +10:00
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
msg.remove = &remove;
|
|
|
|
|
msg.msg_case = CLIENT_MESSAGE__MSG_REMOVE;
|
2020-07-29 11:53:03 +10:00
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
return connection_send_msg(ei, &msg);
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
connection_send_rel(struct ei *ei, struct ei_device *device, int32_t x, int32_t y)
|
|
|
|
|
{
|
2020-08-07 11:28:39 +10:00
|
|
|
if (ei->state == EI_STATE_NEW ||
|
|
|
|
|
ei->state == EI_STATE_DISCONNECTED)
|
2020-07-29 11:53:03 +10:00
|
|
|
return 0;
|
|
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
ClientMessage msg = CLIENT_MESSAGE__INIT;
|
|
|
|
|
PointerRelative rel = POINTER_RELATIVE__INIT;
|
2020-07-29 11:53:03 +10:00
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
rel.deviceid = device->id;
|
|
|
|
|
rel.x = x;
|
|
|
|
|
rel.y = y;
|
2020-07-29 11:53:03 +10:00
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
msg.rel = &rel;
|
|
|
|
|
msg.msg_case = CLIENT_MESSAGE__MSG_REL;
|
|
|
|
|
|
|
|
|
|
return connection_send_msg(ei, &msg);
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
2020-08-03 12:00:31 +10:00
|
|
|
static int
|
|
|
|
|
connection_send_button(struct ei *ei, struct ei_device *device,
|
|
|
|
|
uint32_t b, bool is_press)
|
|
|
|
|
{
|
2020-08-07 11:28:39 +10:00
|
|
|
if (ei->state == EI_STATE_NEW ||
|
|
|
|
|
ei->state == EI_STATE_DISCONNECTED)
|
2020-08-03 12:00:31 +10:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
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 connection_send_msg(ei, &msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
connection_send_key(struct ei *ei, struct ei_device *device,
|
|
|
|
|
uint32_t k, bool is_press)
|
|
|
|
|
{
|
2020-08-07 11:28:39 +10:00
|
|
|
if (ei->state == EI_STATE_NEW ||
|
|
|
|
|
ei->state == EI_STATE_DISCONNECTED)
|
2020-08-03 12:00:31 +10:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
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 connection_send_msg(ei, &msg);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-29 11:53:03 +10:00
|
|
|
static void
|
|
|
|
|
ei_disconnect(struct ei *ei)
|
|
|
|
|
{
|
2020-08-07 11:28:39 +10:00
|
|
|
if (ei->state == EI_STATE_DISCONNECTED ||
|
|
|
|
|
ei->state == EI_STATE_DISCONNECTING)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
enum ei_state state = ei->state;
|
|
|
|
|
|
|
|
|
|
/* We need the disconnecting state to be re-entrant
|
|
|
|
|
ei_device_remove() may call ei_disconnect() on a socket error */
|
|
|
|
|
ei->state = EI_STATE_DISCONNECTING;
|
|
|
|
|
|
|
|
|
|
struct ei_device *d, *tmp;
|
|
|
|
|
list_for_each_safe(d, tmp, &ei->devices, link) {
|
|
|
|
|
ei_device_remove(d);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state != EI_STATE_NEW) {
|
|
|
|
|
connection_send_disconnect(ei);
|
|
|
|
|
ei_queue_disconnect_event(ei);
|
|
|
|
|
}
|
2020-07-29 11:53:03 +10:00
|
|
|
ei->state = EI_STATE_DISCONNECTED;
|
2020-08-07 11:28:39 +10:00
|
|
|
if (ei->source)
|
|
|
|
|
source_remove(ei->source);
|
2020-07-29 11:53:03 +10:00
|
|
|
ei->source = source_unref(ei->source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ei_add_device(struct ei_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
|
int rc = connection_send_add(ei, device);
|
|
|
|
|
if (rc) {
|
|
|
|
|
ei_disconnect(ei);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
2020-08-07 11:28:39 +10:00
|
|
|
|
|
|
|
|
ei_device_ref(device);
|
2020-07-29 11:53:03 +10:00
|
|
|
list_append(&ei->devices, &device->link);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ei_remove_device(struct ei_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
|
int rc = connection_send_remove(ei, device);
|
|
|
|
|
|
|
|
|
|
list_remove(&device->link);
|
|
|
|
|
ei_device_unref(device);
|
|
|
|
|
|
2020-08-07 11:28:39 +10:00
|
|
|
if (rc)
|
|
|
|
|
ei_disconnect(ei);
|
|
|
|
|
return rc;
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
ei_accepted(struct ei *ei, uint32_t deviceid)
|
|
|
|
|
{
|
|
|
|
|
struct ei_device *d;
|
|
|
|
|
|
|
|
|
|
list_for_each(d, &ei->devices, link) {
|
|
|
|
|
if (d->id == deviceid) {
|
|
|
|
|
ei_device_accepted(d);
|
|
|
|
|
ei_queue_added_event(d);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FIXME: could be a device since removed by the client */
|
|
|
|
|
return -EBADMSG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
ei_removed(struct ei *ei, uint32_t deviceid)
|
|
|
|
|
{
|
|
|
|
|
struct ei_device *d;
|
|
|
|
|
|
|
|
|
|
list_for_each(d, &ei->devices, link) {
|
|
|
|
|
if (d->id == deviceid) {
|
|
|
|
|
ei_queue_removed_event(d);
|
|
|
|
|
list_remove(&d->link);
|
|
|
|
|
ei_device_unref(d);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ei_pointer_rel(struct ei_device *device, int32_t x, int32_t y)
|
|
|
|
|
{
|
|
|
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
|
int rc = connection_send_rel(ei, device, x, y);
|
|
|
|
|
if (rc)
|
|
|
|
|
ei_disconnect(ei);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-03 12:00:31 +10:00
|
|
|
int
|
|
|
|
|
ei_pointer_button(struct ei_device *device, uint32_t button, bool is_press)
|
|
|
|
|
{
|
|
|
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
|
int rc = connection_send_button(ei, device, button, is_press);
|
|
|
|
|
if (rc)
|
|
|
|
|
ei_disconnect(ei);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ei_keyboard_key(struct ei_device *device, uint32_t key, bool is_press)
|
|
|
|
|
{
|
|
|
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
|
int rc = connection_send_key(ei, device, key, is_press);
|
|
|
|
|
if (rc)
|
|
|
|
|
ei_disconnect(ei);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
2020-07-29 11:53:03 +10:00
|
|
|
|
|
|
|
|
_public_ struct ei_event*
|
|
|
|
|
ei_get_event(struct ei *ei)
|
|
|
|
|
{
|
|
|
|
|
if (list_empty(&ei->event_queue))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
struct ei_event *e = list_first_entry(&ei->event_queue, e, link);
|
|
|
|
|
list_remove(&e->link);
|
|
|
|
|
|
2020-08-05 16:45:09 +10:00
|
|
|
return e;
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
2020-08-06 13:59:20 +10:00
|
|
|
_public_ struct ei_event*
|
|
|
|
|
ei_peek_event(struct ei *ei)
|
|
|
|
|
{
|
|
|
|
|
if (list_empty(&ei->event_queue))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
struct ei_event *e = list_first_entry(&ei->event_queue, e, link);
|
|
|
|
|
return ei_event_ref(e);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-04 10:53:38 +10:00
|
|
|
_public_ enum ei_event_type
|
|
|
|
|
ei_next_event_type(struct ei *ei)
|
|
|
|
|
{
|
|
|
|
|
if (list_empty(&ei->event_queue))
|
|
|
|
|
return EI_EVENT_NONE;
|
|
|
|
|
|
|
|
|
|
struct ei_event *e = list_first_entry(&ei->event_queue, e, link);
|
|
|
|
|
return e->type;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-29 11:53:03 +10:00
|
|
|
static struct message *
|
2020-08-03 11:59:05 +10:00
|
|
|
connection_parse_message(const char *data, size_t *len)
|
2020-07-29 11:53:03 +10:00
|
|
|
{
|
2020-08-03 11:59:05 +10:00
|
|
|
/* Every message is prefixed by a fixed-length Frame message which
|
|
|
|
|
* contains the length of the next message. Parse that one first,
|
|
|
|
|
* then the real message */
|
|
|
|
|
static size_t framelen = 0;
|
|
|
|
|
if (framelen == 0) {
|
|
|
|
|
Frame f = FRAME__INIT;
|
|
|
|
|
f.length = 0xffff;
|
|
|
|
|
framelen = frame__get_packed_size(&f);
|
|
|
|
|
assert(framelen >= 5);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-29 11:53:03 +10:00
|
|
|
_cleanup_(message_freep) struct message *msg = xalloc(sizeof(*msg));
|
2020-08-03 11:59:05 +10:00
|
|
|
|
|
|
|
|
Frame *frame = frame__unpack(NULL, framelen, (const unsigned char *)data);
|
|
|
|
|
if (!frame)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
size_t msglen = frame->length;
|
|
|
|
|
frame__free_unpacked(frame, NULL);
|
|
|
|
|
|
|
|
|
|
const unsigned char *msgdata = (const unsigned char *)data + framelen;
|
|
|
|
|
|
|
|
|
|
ServerMessage *proto = server_message__unpack(NULL, msglen, msgdata);
|
2020-07-31 12:24:44 +10:00
|
|
|
if (!proto)
|
|
|
|
|
return NULL;
|
2020-07-29 11:53:03 +10:00
|
|
|
|
2020-08-03 11:59:05 +10:00
|
|
|
bool success = true;
|
2020-07-31 12:24:44 +10:00
|
|
|
switch (proto->msg_case) {
|
|
|
|
|
case SERVER_MESSAGE__MSG_HELLO:
|
2020-07-29 11:53:03 +10:00
|
|
|
*msg = (struct message) {
|
|
|
|
|
.type = MESSAGE_HELLO,
|
|
|
|
|
};
|
2020-07-31 12:24:44 +10:00
|
|
|
break;
|
|
|
|
|
case SERVER_MESSAGE__MSG_CONNECTED:
|
2020-07-29 11:53:03 +10:00
|
|
|
*msg = (struct message) {
|
|
|
|
|
.type = MESSAGE_CONNECTED,
|
|
|
|
|
};
|
2020-07-31 12:24:44 +10:00
|
|
|
break;
|
|
|
|
|
case SERVER_MESSAGE__MSG_DISCONNECTED:
|
2020-07-29 11:53:03 +10:00
|
|
|
*msg = (struct message) {
|
|
|
|
|
.type = MESSAGE_DISCONNECTED,
|
|
|
|
|
};
|
2020-07-31 12:24:44 +10:00
|
|
|
break;
|
|
|
|
|
case SERVER_MESSAGE__MSG_ACCEPTED:
|
|
|
|
|
{
|
|
|
|
|
Accepted *a = proto->accepted;
|
|
|
|
|
*msg = (struct message) {
|
2020-08-04 10:29:33 +10:00
|
|
|
.type = MESSAGE_ACCEPTED,
|
|
|
|
|
.accepted.deviceid = a->deviceid,
|
2020-07-31 12:24:44 +10:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SERVER_MESSAGE__MSG_REMOVED:
|
|
|
|
|
{
|
|
|
|
|
Removed *r = proto->removed;
|
|
|
|
|
*msg = (struct message) {
|
2020-08-04 10:29:33 +10:00
|
|
|
.type = MESSAGE_REMOVED,
|
|
|
|
|
.removed.deviceid = r->deviceid,
|
2020-07-31 12:24:44 +10:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2020-08-03 11:59:05 +10:00
|
|
|
success = false;
|
|
|
|
|
break;
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
2020-07-31 12:24:44 +10:00
|
|
|
server_message__free_unpacked(proto, NULL);
|
2020-08-03 11:59:05 +10:00
|
|
|
|
|
|
|
|
return success ? steal(&msg) : NULL;
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
connection_new_handle_msg(struct ei *ei, struct message *msg)
|
|
|
|
|
{
|
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
|
|
switch (msg->type) {
|
|
|
|
|
case MESSAGE_HELLO:
|
|
|
|
|
ei->state = EI_STATE_CONNECTING;
|
|
|
|
|
rc = connection_send_connect(ei);
|
|
|
|
|
break;
|
|
|
|
|
case MESSAGE_CONNECTED:
|
|
|
|
|
case MESSAGE_DISCONNECTED:
|
2020-08-04 10:29:33 +10:00
|
|
|
case MESSAGE_ACCEPTED:
|
|
|
|
|
case MESSAGE_REMOVED:
|
2020-07-29 11:53:03 +10:00
|
|
|
rc = -EPROTO;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
connection_connecting_handle_msg(struct ei *ei, struct message *msg)
|
|
|
|
|
{
|
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
|
|
switch (msg->type) {
|
|
|
|
|
case MESSAGE_HELLO:
|
|
|
|
|
rc = -EPROTO;
|
|
|
|
|
break;
|
|
|
|
|
case MESSAGE_CONNECTED:
|
|
|
|
|
ei->state = EI_STATE_CONNECTED;
|
|
|
|
|
ei_queue_connect_event(ei);
|
|
|
|
|
break;
|
|
|
|
|
case MESSAGE_DISCONNECTED:
|
|
|
|
|
rc = -ECANCELED;
|
|
|
|
|
break;
|
2020-08-04 10:29:33 +10:00
|
|
|
case MESSAGE_ACCEPTED:
|
|
|
|
|
case MESSAGE_REMOVED:
|
2020-07-29 11:53:03 +10:00
|
|
|
rc = -EPROTO;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
connection_connected_handle_msg(struct ei *ei, struct message *msg)
|
|
|
|
|
{
|
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
|
|
switch (msg->type) {
|
|
|
|
|
case MESSAGE_HELLO:
|
|
|
|
|
case MESSAGE_CONNECTED:
|
|
|
|
|
rc = -EPROTO;
|
|
|
|
|
break;
|
|
|
|
|
case MESSAGE_DISCONNECTED:
|
|
|
|
|
rc = -ECANCELED;
|
|
|
|
|
break;
|
2020-08-04 10:29:33 +10:00
|
|
|
case MESSAGE_ACCEPTED:
|
|
|
|
|
rc = ei_accepted(ei, msg->accepted.deviceid);
|
2020-07-29 11:53:03 +10:00
|
|
|
break;
|
2020-08-04 10:29:33 +10:00
|
|
|
case MESSAGE_REMOVED:
|
|
|
|
|
rc = ei_removed(ei, msg->removed.deviceid);
|
2020-07-29 11:53:03 +10:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-07-31 14:14:21 +10:00
|
|
|
connection_dispatch(struct source *source, void *userdata)
|
2020-07-29 11:53:03 +10:00
|
|
|
{
|
2020-07-31 14:14:21 +10:00
|
|
|
struct ei *ei = userdata;
|
2020-07-29 11:53:03 +10:00
|
|
|
enum ei_state old_state = ei->state;
|
|
|
|
|
_cleanup_(message_freep) struct message *msg = NULL;
|
|
|
|
|
|
|
|
|
|
_cleanup_iobuf_ struct iobuf *buf = iobuf_new(64);
|
|
|
|
|
int rc = iobuf_append_from_fd(buf, source_get_fd(source));
|
|
|
|
|
if (rc == -EAGAIN) {
|
|
|
|
|
return;
|
|
|
|
|
} else if (rc == 0) {
|
|
|
|
|
rc = -ECANCELED;
|
|
|
|
|
goto error;
|
|
|
|
|
} else if (rc < 0) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-31 14:14:21 +10:00
|
|
|
size_t idx = 0;
|
|
|
|
|
while (true) {
|
|
|
|
|
const char *data = iobuf_data(buf) + idx;
|
|
|
|
|
size_t len = iobuf_len(buf) - idx;
|
2020-07-29 11:53:03 +10:00
|
|
|
|
2020-07-31 14:14:21 +10:00
|
|
|
if (len == 0)
|
2020-07-29 11:53:03 +10:00
|
|
|
break;
|
2020-07-31 14:14:21 +10:00
|
|
|
|
|
|
|
|
msg = connection_parse_message(data, &len);
|
|
|
|
|
if (!msg) {
|
|
|
|
|
rc = -EBADMSG;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idx += len;
|
|
|
|
|
|
|
|
|
|
switch (ei->state) {
|
2020-08-07 11:28:39 +10:00
|
|
|
case EI_STATE_NEW:
|
|
|
|
|
abort();
|
2020-08-07 10:52:16 +10:00
|
|
|
case EI_STATE_BACKEND:
|
2020-07-31 14:14:21 +10:00
|
|
|
rc = connection_new_handle_msg(ei, msg);
|
|
|
|
|
break;
|
|
|
|
|
case EI_STATE_CONNECTING:
|
|
|
|
|
rc = connection_connecting_handle_msg(ei, msg);
|
|
|
|
|
break;
|
|
|
|
|
case EI_STATE_CONNECTED:
|
|
|
|
|
rc = connection_connected_handle_msg(ei, msg);
|
|
|
|
|
break;
|
2020-08-07 11:28:39 +10:00
|
|
|
case EI_STATE_DISCONNECTING:
|
2020-07-31 14:14:21 +10:00
|
|
|
case EI_STATE_DISCONNECTED:
|
|
|
|
|
abort();
|
|
|
|
|
}
|
2020-07-29 11:53:03 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
if (rc < 0)
|
|
|
|
|
ei_disconnect(ei);
|
|
|
|
|
|
|
|
|
|
static const char *states[] = {
|
2020-08-07 11:28:39 +10:00
|
|
|
"NEW",
|
2020-08-07 10:52:16 +10:00
|
|
|
"BACKEND",
|
2020-07-29 11:53:03 +10:00
|
|
|
"CONNECTING",
|
|
|
|
|
"CONNECTED",
|
|
|
|
|
"DISCONNECTED",
|
2020-08-07 11:28:39 +10:00
|
|
|
"DISCONNECTING",
|
2020-07-29 11:53:03 +10:00
|
|
|
};
|
|
|
|
|
if (rc)
|
|
|
|
|
log_warn(ei, "Connnection error: %s\n", strerror(-rc));
|
|
|
|
|
log_debug(ei, "Connnection dispatch: %s -> %s\n",
|
|
|
|
|
states[old_state],
|
|
|
|
|
states[ei->state]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ei_set_connection(struct ei *ei, int fd)
|
|
|
|
|
{
|
2020-08-06 15:16:39 +10:00
|
|
|
struct source *source = source_add_autoclose(ei->sink, fd, connection_dispatch, ei);
|
2020-08-07 10:54:30 +10:00
|
|
|
if (!source)
|
2020-07-29 11:53:03 +10:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2020-08-07 10:54:30 +10:00
|
|
|
ei->source = source_ref(source);
|
2020-08-07 11:28:39 +10:00
|
|
|
ei->state = EI_STATE_BACKEND;
|
2020-08-07 10:54:30 +10:00
|
|
|
|
2020-07-29 11:53:03 +10:00
|
|
|
return 0;
|
|
|
|
|
}
|
2020-07-30 09:40:16 +10:00
|
|
|
|
|
|
|
|
_public_ void
|
|
|
|
|
ei_configure_name(struct ei *ei, const char *name)
|
|
|
|
|
{
|
2020-08-07 11:28:39 +10:00
|
|
|
if (ei->state != EI_STATE_NEW)
|
2020-08-04 11:08:50 +10:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (strlen(name) > 1024)
|
|
|
|
|
return;
|
|
|
|
|
|
2020-07-30 09:40:16 +10:00
|
|
|
free(ei->name);
|
|
|
|
|
ei->name = xstrdup(name);
|
|
|
|
|
}
|
2020-08-04 14:40:13 +10:00
|
|
|
|
|
|
|
|
#ifdef _enable_tests_
|
|
|
|
|
#include <munit.h>
|
|
|
|
|
|
|
|
|
|
static MunitResult
|
|
|
|
|
test_init_unref(const MunitParameter params[], void *user_data)
|
|
|
|
|
{
|
2020-08-05 16:09:27 +10:00
|
|
|
struct ei *ei = ei_new(NULL);
|
2020-08-04 14:40:13 +10:00
|
|
|
|
2020-08-07 11:28:39 +10:00
|
|
|
munit_assert_int(ei->state, ==, EI_STATE_NEW);
|
2020-08-04 14:40:13 +10:00
|
|
|
munit_assert(list_empty(&ei->event_queue));
|
|
|
|
|
munit_assert(list_empty(&ei->devices));
|
|
|
|
|
|
|
|
|
|
munit_assert_not_null(ei->sink);
|
|
|
|
|
munit_assert_not_null(ei->logger);
|
|
|
|
|
|
|
|
|
|
struct ei *refd = ei_ref(ei);
|
|
|
|
|
munit_assert_ptr_equal(ei, refd);
|
|
|
|
|
munit_assert_int(ei->object.refcount, ==, 2);
|
|
|
|
|
|
|
|
|
|
struct ei *unrefd = ei_unref(ei);
|
|
|
|
|
munit_assert_null(unrefd);
|
|
|
|
|
|
|
|
|
|
unrefd = ei_unref(ei);
|
|
|
|
|
munit_assert_null(unrefd);
|
|
|
|
|
|
|
|
|
|
return MUNIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static MunitResult
|
|
|
|
|
test_configure_name(const MunitParameter params[], void *user_data)
|
|
|
|
|
{
|
2020-08-07 11:28:39 +10:00
|
|
|
struct ei *ei = ei_new(NULL);
|
2020-08-04 14:40:13 +10:00
|
|
|
|
2020-08-07 11:28:39 +10:00
|
|
|
ei_configure_name(ei, "foo");
|
|
|
|
|
munit_assert_string_equal(ei->name, "foo");
|
|
|
|
|
ei_configure_name(ei, "bar");
|
|
|
|
|
munit_assert_string_equal(ei->name, "bar");
|
2020-08-04 14:40:13 +10:00
|
|
|
|
|
|
|
|
/* ignore names that are too long */
|
|
|
|
|
char buf[1200] = {0};
|
|
|
|
|
memset(buf, 'a', sizeof(buf) - 1);
|
2020-08-07 11:28:39 +10:00
|
|
|
ei_configure_name(ei, buf);
|
|
|
|
|
munit_assert_string_equal(ei->name, "bar");
|
2020-08-04 14:40:13 +10:00
|
|
|
|
|
|
|
|
/* ignore names in all other states */
|
2020-08-07 11:28:39 +10:00
|
|
|
for (enum ei_state state = EI_STATE_NEW + 1;
|
2020-08-04 14:40:13 +10:00
|
|
|
state <= EI_STATE_DISCONNECTED;
|
|
|
|
|
state++) {
|
2020-08-07 11:28:39 +10:00
|
|
|
ei->state = state;
|
|
|
|
|
ei_configure_name(ei, "expect ignored");
|
|
|
|
|
munit_assert_string_equal(ei->name, "bar");
|
2020-08-04 14:40:13 +10:00
|
|
|
}
|
|
|
|
|
|
2020-08-07 11:28:39 +10:00
|
|
|
ei_unref(ei);
|
2020-08-07 10:58:07 +10:00
|
|
|
|
2020-08-04 14:40:13 +10:00
|
|
|
return MUNIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static MunitTest ei_tests[] = {
|
|
|
|
|
{ "/init_unref", test_init_unref, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},
|
|
|
|
|
{ "/name", test_configure_name, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},
|
|
|
|
|
{ NULL },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const MunitSuite libei_suite
|
|
|
|
|
__attribute__((used))
|
|
|
|
|
__attribute__((section("test_section"))) = {
|
|
|
|
|
"",
|
|
|
|
|
ei_tests,
|
|
|
|
|
NULL,
|
|
|
|
|
1,
|
|
|
|
|
MUNIT_SUITE_OPTION_NONE,
|
|
|
|
|
};
|
|
|
|
|
#endif
|