mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2026-01-10 06:50:17 +01:00
Allows for slightly easier debugging in the case where we get disconnected after some protocol error.
1598 lines
37 KiB
C
1598 lines
37 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 <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#include "util-io.h"
|
|
#include "util-macros.h"
|
|
#include "util-object.h"
|
|
#include "util-sources.h"
|
|
#include "util-strings.h"
|
|
#include "util-time.h"
|
|
#include "util-version.h"
|
|
|
|
#include "libei.h"
|
|
#include "libei-private.h"
|
|
#include "brei-shared.h"
|
|
#include "ei-proto.h"
|
|
|
|
_Static_assert(sizeof(enum ei_device_capability) == sizeof(int), "Invalid enum size");
|
|
_Static_assert(sizeof(enum ei_keymap_type) == sizeof(int), "Invalid enum size");
|
|
_Static_assert(sizeof(enum ei_event_type) == sizeof(int), "Invalid enum size");
|
|
_Static_assert(sizeof(enum ei_log_priority) == sizeof(int), "Invalid enum size");
|
|
|
|
static struct ei_seat *
|
|
ei_find_seat(struct ei *ei, uint32_t seatid)
|
|
{
|
|
struct ei_seat *seat;
|
|
|
|
list_for_each(seat, &ei->seats, link) {
|
|
if (seat->id == seatid)
|
|
return seat;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct ei_device *
|
|
ei_find_device(struct ei *ei, uint32_t deviceid)
|
|
{
|
|
struct ei_seat *seat;
|
|
|
|
list_for_each(seat, &ei->seats, link) {
|
|
struct ei_device *device = ei_seat_find_device(seat, deviceid);
|
|
if (device)
|
|
return device;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
ei_destroy(struct ei *ei)
|
|
{
|
|
ei_disconnect(ei);
|
|
|
|
struct ei_event *e;
|
|
while ((e = ei_get_event(ei)) != NULL)
|
|
ei_event_unref(e);
|
|
|
|
if (ei->backend_interface.destroy)
|
|
ei->backend_interface.destroy(ei, ei->backend);
|
|
ei->backend = NULL;
|
|
ei_connection_unref(ei->connection);
|
|
sink_unref(ei->sink);
|
|
free(ei->name);
|
|
}
|
|
|
|
static
|
|
OBJECT_IMPLEMENT_CREATE(ei);
|
|
_public_
|
|
OBJECT_IMPLEMENT_REF(ei);
|
|
_public_
|
|
OBJECT_IMPLEMENT_UNREF_CLEANUP(ei);
|
|
_public_
|
|
OBJECT_IMPLEMENT_SETTER(ei, user_data, void *);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei, user_data, void *);
|
|
OBJECT_IMPLEMENT_GETTER(ei, connection, struct ei_connection *);
|
|
|
|
DEFINE_UNREF_CLEANUP_FUNC(ei_device);
|
|
DEFINE_UNREF_CLEANUP_FUNC(ei_region);
|
|
|
|
struct ei *
|
|
ei_get_context(struct ei *ei)
|
|
{
|
|
return ei; /* for the protocol bindings */
|
|
}
|
|
|
|
static struct ei *
|
|
ei_create_context(bool is_sender, void *user_data)
|
|
{
|
|
_unref_(ei) *ei = ei_create(NULL);
|
|
|
|
ei->state = EI_STATE_NEW;
|
|
list_init(&ei->event_queue);
|
|
list_init(&ei->seats);
|
|
list_init(&ei->proto_objects);
|
|
|
|
ei->connection = ei_connection_new(ei);
|
|
ei->next_object_id = 1;
|
|
|
|
ei_log_set_handler(ei, NULL);
|
|
ei_log_set_priority(ei, EI_LOG_PRIORITY_INFO);
|
|
ei->sink = sink_new();
|
|
if (!ei->sink)
|
|
return NULL;
|
|
|
|
ei->user_data = user_data;
|
|
ei->backend = NULL;
|
|
ei->is_sender = is_sender;
|
|
|
|
return steal(&ei);
|
|
}
|
|
|
|
uint32_t
|
|
ei_get_new_id(struct ei *ei)
|
|
{
|
|
static const int server_range = 0xff000000;
|
|
return ei->next_object_id++ & ~server_range;
|
|
}
|
|
|
|
void
|
|
ei_register_object(struct ei *ei, struct brei_object *object)
|
|
{
|
|
log_debug(ei, "registering %s v%u object %#x", object->interface->name, object->version, object->id);
|
|
list_append(&ei->proto_objects, &object->link);
|
|
}
|
|
|
|
void
|
|
ei_unregister_object(struct ei *ei, struct brei_object *object)
|
|
{
|
|
log_debug(ei, "deregistering %s v%u object %#x", object->interface->name, object->version, object->id);
|
|
list_remove(&object->link);
|
|
}
|
|
|
|
_public_ bool
|
|
ei_is_sender(struct ei *ei)
|
|
{
|
|
return ei->is_sender;
|
|
}
|
|
|
|
_public_ struct ei *
|
|
ei_new(void *user_data)
|
|
{
|
|
return ei_new_sender(user_data);
|
|
}
|
|
|
|
_public_ struct ei *
|
|
ei_new_sender(void *user_data)
|
|
{
|
|
return ei_create_context(true, user_data);
|
|
}
|
|
|
|
_public_ struct ei *
|
|
ei_new_receiver(void *user_data)
|
|
{
|
|
return ei_create_context(false, user_data);
|
|
}
|
|
|
|
_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
|
|
update_event_timestamp(struct ei_event *event, uint64_t time)
|
|
{
|
|
switch (event->type) {
|
|
case EI_EVENT_POINTER_MOTION:
|
|
case EI_EVENT_POINTER_MOTION_ABSOLUTE:
|
|
case EI_EVENT_POINTER_BUTTON:
|
|
case EI_EVENT_POINTER_SCROLL:
|
|
case EI_EVENT_POINTER_SCROLL_STOP:
|
|
case EI_EVENT_POINTER_SCROLL_CANCEL:
|
|
case EI_EVENT_POINTER_SCROLL_DISCRETE:
|
|
case EI_EVENT_KEYBOARD_KEY:
|
|
case EI_EVENT_TOUCH_DOWN:
|
|
case EI_EVENT_TOUCH_UP:
|
|
case EI_EVENT_TOUCH_MOTION:
|
|
if (event->timestamp != 0) {
|
|
log_bug(ei_event_get_context(event),
|
|
"Unexpected timestamp for event of type: %s",
|
|
ei_event_type_to_string(event->type));
|
|
return;
|
|
}
|
|
event->timestamp = time;
|
|
break;
|
|
default:
|
|
log_bug(ei_event_get_context(event),
|
|
"Unexpected event %s in pending queue event",
|
|
ei_event_type_to_string(event->type));
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
queue_event(struct ei *ei, struct ei_event *event)
|
|
{
|
|
struct ei_device *device = ei_event_get_device(event);
|
|
struct list *queue = &ei->event_queue;
|
|
const char *prefix = "";
|
|
|
|
switch (event->type) {
|
|
case EI_EVENT_POINTER_MOTION:
|
|
case EI_EVENT_POINTER_MOTION_ABSOLUTE:
|
|
case EI_EVENT_POINTER_BUTTON:
|
|
case EI_EVENT_POINTER_SCROLL:
|
|
case EI_EVENT_POINTER_SCROLL_STOP:
|
|
case EI_EVENT_POINTER_SCROLL_CANCEL:
|
|
case EI_EVENT_POINTER_SCROLL_DISCRETE:
|
|
case EI_EVENT_KEYBOARD_KEY:
|
|
case EI_EVENT_TOUCH_DOWN:
|
|
case EI_EVENT_TOUCH_UP:
|
|
case EI_EVENT_TOUCH_MOTION:
|
|
prefix = "pending ";
|
|
queue = &device->pending_event_queue;
|
|
break;
|
|
case EI_EVENT_FRAME:
|
|
/* silently discard empty frames */
|
|
if (list_empty(&device->pending_event_queue))
|
|
return;
|
|
|
|
struct ei_event *pending;
|
|
list_for_each_safe(pending, &device->pending_event_queue, link) {
|
|
update_event_timestamp(pending, event->timestamp);
|
|
list_remove(&pending->link);
|
|
list_append(&ei->event_queue, &pending->link);
|
|
}
|
|
break;
|
|
default:
|
|
if (device && !list_empty(&device->pending_event_queue))
|
|
ei_queue_frame_event(device, ei_now(ei));
|
|
break;
|
|
}
|
|
|
|
log_debug(ei, "queuing %sevent type %s (%u)",
|
|
prefix,
|
|
ei_event_type_to_string(event->type), event->type);
|
|
|
|
list_append(queue, &event->link);
|
|
}
|
|
|
|
static void
|
|
insert_event(struct ei *ei, struct ei_event *event)
|
|
{
|
|
log_debug(ei, "inserting event type %s (%u)",
|
|
ei_event_type_to_string(event->type), event->type);
|
|
|
|
list_insert(&ei->event_queue, &event->link);
|
|
}
|
|
|
|
|
|
static void
|
|
queue_connect_event(struct ei *ei)
|
|
{
|
|
struct ei_event *e = ei_event_new(ei);
|
|
e->type = EI_EVENT_CONNECT;
|
|
|
|
queue_event(ei, e);
|
|
}
|
|
|
|
static void
|
|
queue_disconnect_event(struct ei *ei)
|
|
{
|
|
struct ei_event *e = ei_event_new(ei);
|
|
e->type = EI_EVENT_DISCONNECT;
|
|
|
|
queue_event(ei, e);
|
|
}
|
|
|
|
static void
|
|
queue_seat_added_event(struct ei_seat *seat)
|
|
{
|
|
struct ei *ei= ei_seat_get_context(seat);
|
|
|
|
struct ei_event *e = ei_event_new(ei);
|
|
e->type = EI_EVENT_SEAT_ADDED;
|
|
e->seat = ei_seat_ref(seat);
|
|
|
|
queue_event(ei, e);
|
|
}
|
|
|
|
static void
|
|
queue_seat_removed_event(struct ei_seat *seat)
|
|
{
|
|
struct ei *ei= ei_seat_get_context(seat);
|
|
|
|
struct ei_event *e = ei_event_new(ei);
|
|
e->type = EI_EVENT_SEAT_REMOVED;
|
|
e->seat = ei_seat_ref(seat);
|
|
|
|
queue_event(ei, e);
|
|
}
|
|
|
|
void
|
|
ei_queue_seat_removed_event(struct ei_seat *seat)
|
|
{
|
|
queue_seat_removed_event(seat);
|
|
}
|
|
|
|
static void
|
|
queue_device_added_event(struct ei_device *device)
|
|
{
|
|
struct ei *ei= ei_device_get_context(device);
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_DEVICE_ADDED;
|
|
|
|
queue_event(ei, e);
|
|
}
|
|
|
|
static void
|
|
queue_device_removed_event(struct ei_device *device)
|
|
{
|
|
struct ei *ei= ei_device_get_context(device);
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_DEVICE_REMOVED;
|
|
|
|
queue_event(ei, e);
|
|
}
|
|
|
|
static void
|
|
insert_device_removed_event(struct ei_device *device)
|
|
{
|
|
struct ei *ei= ei_device_get_context(device);
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_DEVICE_REMOVED;
|
|
|
|
insert_event(ei, e);
|
|
}
|
|
|
|
static void
|
|
queue_paused_event(struct ei_device *device)
|
|
{
|
|
struct ei *ei= ei_device_get_context(device);
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_DEVICE_PAUSED;
|
|
|
|
queue_event(ei, e);
|
|
}
|
|
|
|
static void
|
|
queue_resumed_event(struct ei_device *device)
|
|
{
|
|
struct ei *ei= ei_device_get_context(device);
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_DEVICE_RESUMED;
|
|
|
|
queue_event(ei, e);
|
|
}
|
|
|
|
static void
|
|
queue_keyboard_modifiers_event(struct ei_device *device,
|
|
const struct ei_xkb_modifiers *mods)
|
|
{
|
|
struct ei *ei= ei_device_get_context(device);
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_KEYBOARD_MODIFIERS;
|
|
e->modifiers = *mods;
|
|
|
|
queue_event(ei, e);
|
|
}
|
|
|
|
void
|
|
ei_queue_frame_event(struct ei_device *device, uint64_t time)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_FRAME;
|
|
e->timestamp = time;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_device_start_emulating_event(struct ei_device *device, uint32_t sequence)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_DEVICE_START_EMULATING;
|
|
e->start_emulating.sequence = sequence;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_device_stop_emulating_event(struct ei_device *device)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_DEVICE_STOP_EMULATING;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_pointer_rel_event(struct ei_device *device,
|
|
double dx, double dy)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_POINTER_MOTION;
|
|
e->pointer.dx = dx;
|
|
e->pointer.dy = dy;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_pointer_abs_event(struct ei_device *device,
|
|
double x, double y)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_POINTER_MOTION_ABSOLUTE;
|
|
e->pointer.absx = x;
|
|
e->pointer.absy = y;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_pointer_button_event(struct ei_device *device, uint32_t button,
|
|
bool is_press)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_POINTER_BUTTON;
|
|
e->pointer.button = button;
|
|
e->pointer.button_is_press = is_press;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_pointer_scroll_event(struct ei_device *device,
|
|
double x, double y)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_POINTER_SCROLL;
|
|
e->pointer.sx = x;
|
|
e->pointer.sy = y;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_pointer_scroll_discrete_event(struct ei_device *device,
|
|
int32_t x, int32_t y)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_POINTER_SCROLL_DISCRETE;
|
|
e->pointer.sdx = x;
|
|
e->pointer.sdy = y;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_pointer_scroll_stop_event(struct ei_device *device, bool x, bool y)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_POINTER_SCROLL_STOP;
|
|
e->pointer.stop_x = x;
|
|
e->pointer.stop_y = y;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_pointer_scroll_cancel_event(struct ei_device *device, bool x, bool y)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_POINTER_SCROLL_CANCEL;
|
|
e->pointer.stop_x = x;
|
|
e->pointer.stop_y = y;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_keyboard_key_event(struct ei_device *device, uint32_t key,
|
|
bool is_press)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_KEYBOARD_KEY;
|
|
e->keyboard.key = key;
|
|
e->keyboard.key_is_press = is_press;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_touch_down_event(struct ei_device *device, uint32_t touchid,
|
|
double x, double y)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_TOUCH_DOWN;
|
|
e->touch.touchid = touchid,
|
|
e->touch.x = x;
|
|
e->touch.y = y;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_touch_motion_event(struct ei_device *device, uint32_t touchid,
|
|
double x, double y)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_TOUCH_MOTION;
|
|
e->touch.touchid = touchid,
|
|
e->touch.x = x;
|
|
e->touch.y = y;
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_queue_touch_up_event(struct ei_device *device, uint32_t touchid)
|
|
{
|
|
struct ei_event *e = ei_event_new_for_device(device);
|
|
|
|
e->type = EI_EVENT_TOUCH_UP;
|
|
e->touch.touchid = touchid,
|
|
|
|
queue_event(ei_device_get_context(device), e);
|
|
}
|
|
|
|
void
|
|
ei_disconnect(struct ei *ei)
|
|
{
|
|
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_seat *seat;
|
|
list_for_each_safe(seat, &ei->seats, link) {
|
|
ei_seat_remove(seat);
|
|
}
|
|
|
|
if (state != EI_STATE_NEW) {
|
|
ei_connection_request_disconnect(ei->connection);
|
|
}
|
|
queue_disconnect_event(ei);
|
|
ei->state = EI_STATE_DISCONNECTED;
|
|
if (ei->source)
|
|
source_remove(ei->source);
|
|
ei->source = source_unref(ei->source);
|
|
}
|
|
|
|
static int
|
|
handle_msg_seat_added(struct ei_connection *connection, uint32_t seatid,
|
|
uint32_t capabilities, const char *name)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
log_debug(ei, "Added seat %#x '%s' with caps %#x",
|
|
seatid, name, capabilities);
|
|
|
|
struct ei_seat *seat = ei_seat_new(ei, seatid, name, capabilities);
|
|
|
|
/* seats list owns the ref */
|
|
list_append(&ei->seats, &seat->link);
|
|
|
|
queue_seat_added_event(seat);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_msg_seat_removed(struct ei_connection *connection, uint32_t seatid)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
log_debug(ei, "server removed seat %#x", seatid);
|
|
|
|
struct ei_seat *seat = ei_find_seat(ei, seatid);
|
|
if (seat) {
|
|
ei_seat_remove(seat);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_msg_device_added(struct ei_connection *connection, uint32_t deviceid, uint32_t capabilities,
|
|
const char *name, uint32_t seatid, uint32_t type,
|
|
uint32_t width, uint32_t height)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
struct ei_seat *seat = ei_find_seat(ei, seatid);
|
|
|
|
if (!seat) {
|
|
log_bug(ei, "Invalid seat id %#x for device %s (%#x)",
|
|
seatid, name, deviceid);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Wrong device id or a device already removed by the client but we
|
|
* won't know which unless we keep some device ID table. Not worth
|
|
* it, so just silently ignore */
|
|
if (ei_seat_find_device(seat, deviceid)) {
|
|
log_error(ei, "Server sent duplicate device id %#x", deviceid);
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (type) {
|
|
case EI_DEVICE_TYPE_PHYSICAL:
|
|
case EI_DEVICE_TYPE_VIRTUAL:
|
|
break;
|
|
default:
|
|
log_error(ei, "Server sent invalid device type %u", type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
_unref_(ei_device) *device = ei_device_new(seat, deviceid);
|
|
ei_device_set_type(device, type);
|
|
if (type == EI_DEVICE_TYPE_PHYSICAL)
|
|
ei_device_set_size(device, width, height);
|
|
ei_device_set_name(device, name);
|
|
ei_device_set_capabilities(device, capabilities);
|
|
ei_device_added(device);
|
|
|
|
log_debug(ei,
|
|
"Added device %#x '%s' caps: %s%s%s%s seat: %s",
|
|
deviceid, name,
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) ? "p" : "",
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) ? "a" : "",
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD) ? "k" : "",
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH) ? "t" : "",
|
|
ei_seat_get_name(seat));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_msg_device_keymap(struct ei_connection *connection, uint32_t deviceid,
|
|
enum ei_keymap_type keymap_type,
|
|
uint32_t keymap_sz, int keymap_fd)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
log_debug(ei, "Adding keymap for %#x", deviceid);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
if (!device)
|
|
return 0;
|
|
|
|
ei_device_set_keymap(device, keymap_type, keymap_fd, keymap_sz);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ei_queue_device_removed_event(struct ei_device *device)
|
|
{
|
|
queue_device_removed_event(device);
|
|
}
|
|
|
|
void
|
|
ei_insert_device_removed_event(struct ei_device *device)
|
|
{
|
|
insert_device_removed_event(device);
|
|
}
|
|
|
|
static int
|
|
handle_msg_device_added_done(struct ei_connection *connection, uint32_t deviceid)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
log_debug(ei, "Done with device %#x", deviceid);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
if (!device)
|
|
return 0;
|
|
|
|
queue_device_added_event(device);
|
|
ei_device_done(device);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_msg_device_region(struct ei_connection *connection, uint32_t deviceid,
|
|
uint32_t x, uint32_t y,
|
|
uint32_t w, uint32_t h,
|
|
float scale)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
log_debug(ei, "Adding device region for %#x", deviceid);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
if (!device)
|
|
return 0;
|
|
|
|
_unref_(ei_region) *r = ei_region_new();
|
|
ei_region_set_offset(r, x, y);
|
|
ei_region_set_size(r, w, h);
|
|
ei_region_set_physical_scale(r, scale);
|
|
|
|
ei_device_add_region(device, r);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_msg_keyboard_modifiers(struct ei_connection *connection, uint32_t deviceid,
|
|
uint32_t depressed, uint32_t locked,
|
|
uint32_t latched, uint32_t group)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
log_debug(ei, "Setting modifiers for %#x", deviceid);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
if (!device)
|
|
return 0;
|
|
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD)) {
|
|
log_bug(ei,"Modifier event for non-keyboard");
|
|
return -EPROTO;
|
|
}
|
|
|
|
struct ei_xkb_modifiers mods = {
|
|
.depressed = depressed,
|
|
.latched = latched,
|
|
.locked = locked,
|
|
.group = group,
|
|
};
|
|
queue_keyboard_modifiers_event(device, &mods);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_msg_device_removed(struct ei_connection *connection, uint32_t deviceid)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
log_debug(ei, "Removed device %#x", deviceid);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
if (!device)
|
|
return 0;
|
|
|
|
ei_device_removed_by_server(device);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_msg_device_resumed(struct ei_connection *connection, uint32_t deviceid)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
log_debug(ei, "Resumed device %#x", deviceid);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
if (device) {
|
|
ei_device_resumed(device);
|
|
queue_resumed_event(device);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_msg_device_paused(struct ei_connection *connection, uint32_t deviceid)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
log_debug(ei, "Paused device %#x", deviceid);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
if (device) {
|
|
ei_device_paused(device);
|
|
queue_paused_event(device);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_send_close_device(struct ei_device *device)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
int rc = ei_connection_request_close_device(ei->connection, device->id);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ei_send_start_emulating(struct ei_device *device, uint32_t sequence)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
int rc = ei_connection_request_start_emulating(ei->connection, device->id, sequence);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ei_send_stop_emulating(struct ei_device *device)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
int rc = ei_connection_request_stop_emulating(ei->connection, device->id);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ei_send_seat_bind(struct ei_seat *seat, uint32_t capabilities)
|
|
{
|
|
struct ei *ei = ei_seat_get_context(seat);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
int rc = ei_connection_request_bind_seat(ei->connection, seat->id, capabilities);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ei_send_frame(struct ei_device *device, uint64_t time)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
if (!device->send_frame_event)
|
|
return 0;
|
|
|
|
device->send_frame_event = false;
|
|
|
|
int rc = ei_connection_request_frame(ei->connection, device->id, us2ms(time), time % 1000);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ei_send_pointer_rel(struct ei_device *device, double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_connection_request_pointer_relative(ei->connection, device->id, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ei_send_pointer_abs(struct ei_device *device, double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_connection_request_pointer_absolute(ei->connection, device->id, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ei_send_pointer_button(struct ei_device *device, uint32_t button, 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_connection_request_pointer_button(ei->connection, device->id, button, is_press);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int ei_send_pointer_scroll(struct ei_device *device, double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_connection_request_pointer_scroll(ei->connection, device->id, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int ei_send_pointer_scroll_stop(struct ei_device *device, double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_connection_request_pointer_scroll_stop(ei->connection, device->id, x, y, false);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int ei_send_pointer_scroll_cancel(struct ei_device *device, double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_connection_request_pointer_scroll_stop(ei->connection, device->id, x, y, true);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
|
|
int ei_send_pointer_scroll_discrete(struct ei_device *device, int32_t x, int32_t y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_connection_request_pointer_scroll_discrete(ei->connection, device->id, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ei_send_keyboard_key(struct ei_device *device, uint32_t key, 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_connection_request_keyboard_key(ei->connection, device->id, key, is_press);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ei_send_touch_down(struct ei_device *device, uint32_t tid,
|
|
double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_connection_request_touch_down(ei->connection, device->id, tid, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ei_send_touch_motion(struct ei_device *device, uint32_t tid,
|
|
double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_connection_request_touch_motion(ei->connection, device->id, tid, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ei_send_touch_up(struct ei_device *device, uint32_t tid)
|
|
{
|
|
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_connection_request_touch_up(ei->connection, device->id, tid);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
_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);
|
|
|
|
return e;
|
|
}
|
|
|
|
_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);
|
|
}
|
|
|
|
static int handle_msg_disconnected(struct ei_connection *connection, uint32_t reason, const char *explanation)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
if (reason == EI_CONNECTION_DISCONNECT_REASON_DISCONNECTED)
|
|
log_info(ei, "Disconnected by EIS");
|
|
else
|
|
log_info(ei, "Disconnected after error: %s", explanation);
|
|
|
|
return -ECANCELED;
|
|
}
|
|
|
|
#define DISCONNECT_IF_SENDER_CONTEXT(ei_) do {\
|
|
if (ei_->is_sender) { \
|
|
log_bug_client(ei_, "Invalid event from receiver EIS context. Disconnecting"); \
|
|
return -ECANCELED; \
|
|
} \
|
|
} while(0)
|
|
|
|
static int
|
|
handle_msg_start_emulating(struct ei_connection *connection, uint32_t deviceid, uint32_t sequence)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
ei_device_event_start_emulating(device, sequence);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_msg_stop_emulating(struct ei_connection *connection, uint32_t deviceid)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
ei_device_event_stop_emulating(device);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_msg_frame(struct ei_connection *connection, uint32_t deviceid, uint32_t time, uint32_t micros)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
return ei_device_event_frame(device, ms2us(time) + micros);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_msg_pointer_rel(struct ei_connection *connection, uint32_t deviceid, float x, float y)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
return ei_device_event_pointer_rel(device, x, y);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_msg_pointer_abs(struct ei_connection *connection, uint32_t deviceid, float x, float y)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
return ei_device_event_pointer_abs(device, x, y);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_msg_pointer_button(struct ei_connection *connection, uint32_t deviceid,
|
|
uint32_t button, uint32_t state)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
return ei_device_event_pointer_button(device, button, !!state);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_msg_pointer_scroll(struct ei_connection *connection, uint32_t deviceid, float x, float y)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
return ei_device_event_pointer_scroll(device, x, y);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_msg_pointer_scroll_discrete(struct ei_connection *connection, uint32_t deviceid,
|
|
int32_t x, int32_t y)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
return ei_device_event_pointer_scroll_discrete(device, x, y);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_msg_pointer_scroll_stop(struct ei_connection *connection, uint32_t deviceid,
|
|
uint32_t x, uint32_t y, uint32_t is_cancel)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device) {
|
|
if (is_cancel)
|
|
return ei_device_event_pointer_scroll_cancel(device, !!x, !!y);
|
|
else
|
|
return ei_device_event_pointer_scroll_stop(device, !!x, !!y);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_msg_keyboard_key(struct ei_connection *connection, uint32_t deviceid,
|
|
uint32_t key, uint32_t state)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
return ei_device_event_keyboard_key(device, key, !!state);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_msg_touch_down(struct ei_connection *connection, uint32_t deviceid,
|
|
uint32_t touchid, float x, float y)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
return ei_device_event_touch_down(device, touchid, x, y);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_msg_touch_motion(struct ei_connection *connection, uint32_t deviceid,
|
|
uint32_t touchid, float x, float y)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
return ei_device_event_touch_motion(device, touchid, x, y);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
handle_msg_touch_up(struct ei_connection *connection, uint32_t deviceid, uint32_t touchid)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(ei);
|
|
|
|
struct ei_device *device = ei_find_device(ei, deviceid);
|
|
|
|
if (device)
|
|
return ei_device_event_touch_up(device, touchid);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void
|
|
connected(struct ei_connection *connection, void *user_data)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
/* FIXME: the connected event *should* mean that the server
|
|
* has accepted us but we don't currently have any event
|
|
* for this to hook onto. So let's just assume "connected" means
|
|
* the server has seen our messages.
|
|
*/
|
|
ei->state = EI_STATE_CONNECTED;
|
|
queue_connect_event(ei);
|
|
}
|
|
|
|
static int
|
|
handle_msg_connection_setup(struct ei_connection *connection, uint32_t new_id, uint32_t version)
|
|
{
|
|
struct ei *ei = ei_connection_get_context(connection);
|
|
|
|
struct ei_connection_setup *setup = ei_connection_setup_new(ei, new_id, version);
|
|
if (version >= EI_CONNECTION_SETUP_REQUEST_TYPE_SINCE_VERSION)
|
|
ei_connection_setup_request_type(setup, ei->is_sender ? EI_CONNECTION_SETUP_CONTEXT_TYPE_SENDER : EI_CONNECTION_SETUP_CONTEXT_TYPE_RECEIVER);
|
|
if (version >= EI_CONNECTION_SETUP_REQUEST_NAME_SINCE_VERSION)
|
|
ei_connection_setup_request_name(setup, ei->name);
|
|
ei_connection_setup_request_done(setup);
|
|
ei_connection_setup_unref(setup);
|
|
|
|
ei->state = EI_STATE_CONNECTING;
|
|
|
|
ei_connection_sync(ei->connection, connected, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct ei_connection_interface intf_state_backend = {
|
|
.connection_setup = handle_msg_connection_setup,
|
|
/* Everything triggers -EPROTO */
|
|
};
|
|
|
|
static const struct ei_connection_interface intf_state_connecting = {
|
|
.connection_setup = handle_msg_connection_setup,
|
|
.disconnected = handle_msg_disconnected,
|
|
};
|
|
|
|
static const struct ei_connection_interface intf_state_connected = {
|
|
.connection_setup = NULL, /* EPROTO */
|
|
.disconnected = handle_msg_disconnected,
|
|
.seat_added = handle_msg_seat_added,
|
|
.seat_removed = handle_msg_seat_removed,
|
|
.device_added = handle_msg_device_added,
|
|
.device_removed = handle_msg_device_removed,
|
|
.device_resumed = handle_msg_device_resumed,
|
|
.device_paused = handle_msg_device_paused,
|
|
.device_region = handle_msg_device_region,
|
|
.device_keymap = handle_msg_device_keymap,
|
|
.device_done = handle_msg_device_added_done,
|
|
.keyboard_modifiers = handle_msg_keyboard_modifiers,
|
|
|
|
/* events */
|
|
.start_emulating = handle_msg_start_emulating,
|
|
.stop_emulating = handle_msg_stop_emulating,
|
|
.pointer_relative = handle_msg_pointer_rel,
|
|
.pointer_absolute = handle_msg_pointer_abs,
|
|
.pointer_button = handle_msg_pointer_button,
|
|
.pointer_scroll = handle_msg_pointer_scroll,
|
|
.pointer_scroll_stop = handle_msg_pointer_scroll_stop,
|
|
.pointer_scroll_discrete = handle_msg_pointer_scroll_discrete,
|
|
.keyboard_key = handle_msg_keyboard_key,
|
|
.touch_down = handle_msg_touch_down,
|
|
.touch_motion = handle_msg_touch_motion,
|
|
.touch_up = handle_msg_touch_up,
|
|
.frame = handle_msg_frame,
|
|
};
|
|
|
|
static const struct ei_connection_interface *interfaces[] = {
|
|
[EI_STATE_NEW] = NULL,
|
|
[EI_STATE_BACKEND] = &intf_state_backend,
|
|
[EI_STATE_CONNECTING] = &intf_state_connecting,
|
|
[EI_STATE_CONNECTED] = &intf_state_connected,
|
|
[EI_STATE_DISCONNECTING] = NULL,
|
|
[EI_STATE_DISCONNECTED] = NULL,
|
|
};
|
|
|
|
|
|
const struct ei_connection_interface *
|
|
ei_get_interface(struct ei *ei)
|
|
{
|
|
assert(ei->state < ARRAY_LENGTH(interfaces));
|
|
return interfaces[ei->state];
|
|
}
|
|
|
|
static int
|
|
lookup_object(uint32_t object_id, struct brei_object **object, void *userdata)
|
|
{
|
|
struct ei *ei = userdata;
|
|
|
|
struct brei_object *obj;
|
|
list_for_each(obj, &ei->proto_objects, link) {
|
|
if (obj->id == object_id) {
|
|
*object = obj;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
static void
|
|
connection_dispatch(struct source *source, void *userdata)
|
|
{
|
|
struct ei *ei = userdata;
|
|
enum ei_state old_state = ei->state;
|
|
|
|
int rc = brei_dispatch(source_get_fd(source), lookup_object, ei);
|
|
if (rc < 0) {
|
|
brei_drain_fd(source_get_fd(source));
|
|
ei_disconnect(ei);
|
|
}
|
|
|
|
static const char *states[] = {
|
|
"NEW",
|
|
"BACKEND",
|
|
"CONNECTING",
|
|
"CONNECTED",
|
|
"DISCONNECTED",
|
|
"DISCONNECTING",
|
|
};
|
|
if (rc == -ECANCELED)
|
|
log_info(ei, "Disconnected");
|
|
else if (rc)
|
|
log_warn(ei, "Connection error: %s", strerror(-rc));
|
|
|
|
if (old_state != ei->state)
|
|
log_debug(ei, "Connection dispatch: %s -> %s",
|
|
states[old_state],
|
|
states[ei->state]);
|
|
}
|
|
|
|
int
|
|
ei_send_message(struct ei *ei, uint32_t object_id,
|
|
uint32_t opcode, const char *signature, size_t nargs, ...)
|
|
{
|
|
int fd = source_get_fd(ei->source);
|
|
|
|
log_debug(ei, "sending: %#x:%u signature '%s'", object_id, opcode, signature);
|
|
|
|
va_list args;
|
|
va_start(args, nargs);
|
|
int rc = brei_send_message(fd, object_id, opcode, signature, nargs, args);
|
|
va_end(args);
|
|
return rc < 0 ? rc : 0;
|
|
}
|
|
|
|
int
|
|
ei_set_socket(struct ei *ei, int fd)
|
|
{
|
|
struct source *source = source_new(fd, connection_dispatch, ei);
|
|
int rc = sink_add_source(ei->sink, source);
|
|
if (rc == 0) {
|
|
ei->source = source_ref(source);
|
|
ei->state = EI_STATE_BACKEND;
|
|
|
|
/* The server SHOULD have already sent the connection setup, let's
|
|
* process that. If not ready, it'll happen in the next dispatch.
|
|
*
|
|
* FIXME: this will block if O_NONBLOCK is missing
|
|
*/
|
|
ei_dispatch(ei);
|
|
}
|
|
|
|
source_unref(source);
|
|
|
|
return rc < 0 ? rc : 0;
|
|
}
|
|
|
|
_public_ void
|
|
ei_configure_name(struct ei *ei, const char *name)
|
|
{
|
|
if (ei->state != EI_STATE_NEW) {
|
|
log_bug_client(ei,"Client is already connected");
|
|
return;
|
|
}
|
|
|
|
if (strlen(name) > 1024) {
|
|
log_bug_client(ei, "Client name too long");
|
|
return;
|
|
}
|
|
|
|
free(ei->name);
|
|
ei->name = xstrdup(name);
|
|
}
|
|
|
|
_public_ uint64_t
|
|
ei_now(struct ei *ei)
|
|
{
|
|
uint64_t ts = 0;
|
|
int rc = now(&ts);
|
|
|
|
if (rc < 0) {
|
|
/* We should probably disconnect here but the chances of this
|
|
* happening are so slim it's not worth worrying about. Plus,
|
|
* if this fails we're likely to be inside eis_device_frame()
|
|
* so we should flush a frame event before disconnecting and... */
|
|
log_error(ei, "clock_gettime failed: %s", strerror(-rc));
|
|
}
|
|
return ts;
|
|
}
|
|
|
|
#ifdef _enable_tests_
|
|
#include "util-munit.h"
|
|
|
|
MUNIT_TEST(test_init_unref)
|
|
{
|
|
struct ei *ei = ei_new(NULL);
|
|
|
|
munit_assert_int(ei->state, ==, EI_STATE_NEW);
|
|
munit_assert(list_empty(&ei->event_queue));
|
|
munit_assert(list_empty(&ei->seats));
|
|
|
|
munit_assert_not_null(ei->sink);
|
|
|
|
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;
|
|
}
|
|
|
|
MUNIT_TEST(test_configure_name)
|
|
{
|
|
struct ei *ei = ei_new(NULL);
|
|
|
|
ei_configure_name(ei, "foo");
|
|
munit_assert_string_equal(ei->name, "foo");
|
|
ei_configure_name(ei, "bar");
|
|
munit_assert_string_equal(ei->name, "bar");
|
|
|
|
/* ignore names that are too long */
|
|
char buf[1200] = {0};
|
|
memset(buf, 'a', sizeof(buf) - 1);
|
|
ei_configure_name(ei, buf);
|
|
munit_assert_string_equal(ei->name, "bar");
|
|
|
|
/* ignore names in all other states */
|
|
for (enum ei_state state = EI_STATE_NEW + 1;
|
|
state <= EI_STATE_DISCONNECTED;
|
|
state++) {
|
|
ei->state = state;
|
|
ei_configure_name(ei, "expect ignored");
|
|
munit_assert_string_equal(ei->name, "bar");
|
|
}
|
|
|
|
ei_unref(ei);
|
|
|
|
return MUNIT_OK;
|
|
}
|
|
#endif
|