mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2026-01-08 02:20:13 +01:00
Add the minimal implementation for a UNIX socket libei client
This is the minimum framework to support connecting to a libeis server, adding
a device and sending relative pointer motion events.It's missing a bunch of
checks and verifications, many of the hooks are still missing, etc.
See the tools/ directory for an example. The protocol is as outlined in
commit f7a24b2fbd.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
parent
27670000e4
commit
802dfd4ca6
7 changed files with 1007 additions and 6 deletions
|
|
@ -35,10 +35,15 @@ dep_libutil = declare_dependency(link_with: lib_util)
|
|||
lib_libei = shared_library('ei',
|
||||
'src/libei.h',
|
||||
'src/libei.c',
|
||||
'src/libei-device.c',
|
||||
'src/libei-socket.c',
|
||||
dependencies: [dep_libutil],
|
||||
install: true
|
||||
)
|
||||
|
||||
dep_libei = declare_dependency(link_with: lib_libei,
|
||||
include_directories: 'src')
|
||||
|
||||
pkgconfig.generate(lib_libei,
|
||||
filebase: 'libei',
|
||||
name : 'libEI',
|
||||
|
|
@ -72,5 +77,8 @@ executable('eis-socket-server',
|
|||
'tools/eis-socket-server.c',
|
||||
dependencies: [dep_libeis])
|
||||
|
||||
executable('ei-socket-client',
|
||||
'tools/ei-socket-client.c',
|
||||
dependencies: [dep_libei])
|
||||
|
||||
configure_file(output : 'config.h', configuration : config_h)
|
||||
|
|
|
|||
126
src/libei-device.c
Normal file
126
src/libei-device.c
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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 "util-macros.h"
|
||||
#include "util-bits.h"
|
||||
|
||||
#include "libei-private.h"
|
||||
|
||||
static void
|
||||
ei_device_destroy(struct ei_device *device)
|
||||
{
|
||||
}
|
||||
|
||||
_public_
|
||||
OBJECT_DECLARE_REF(ei_device);
|
||||
_public_
|
||||
OBJECT_DECLARE_UNREF(ei_device);
|
||||
OBJECT_DECLARE_CREATE(ei_device);
|
||||
static
|
||||
OBJECT_DECLARE_PARENT(ei_device, ei);
|
||||
|
||||
_public_ struct ei*
|
||||
ei_device_get_context(struct ei_device *device)
|
||||
{
|
||||
return ei_device_parent(device);
|
||||
}
|
||||
|
||||
_public_
|
||||
struct ei_device *
|
||||
ei_device_new(struct ei *ei)
|
||||
{
|
||||
static uint32_t deviceid = 0;
|
||||
struct ei_device *device = ei_device_create(&ei->object);
|
||||
|
||||
device->capabilities = 0;
|
||||
device->id = ++deviceid;
|
||||
device->state = EI_DEVICE_STATE_NEW;
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
_public_ bool
|
||||
ei_device_configure_capability(struct ei_device *device,
|
||||
enum ei_device_capability cap)
|
||||
{
|
||||
switch (cap) {
|
||||
case EI_DEVICE_CAP_POINTER:
|
||||
case EI_DEVICE_CAP_POINTER_ABSOLUTE:
|
||||
case EI_DEVICE_CAP_KEYBOARD:
|
||||
case EI_DEVICE_CAP_TOUCH:
|
||||
device->capabilities |= bit(cap);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_public_ void
|
||||
ei_device_add(struct ei_device *device)
|
||||
{
|
||||
ei_add_device(device);
|
||||
}
|
||||
|
||||
_public_ void
|
||||
ei_device_remove(struct ei_device *device)
|
||||
{
|
||||
ei_remove_device(device);
|
||||
device->state = EI_DEVICE_STATE_REMOVED;
|
||||
}
|
||||
|
||||
void
|
||||
ei_device_accepted(struct ei_device *device)
|
||||
{
|
||||
device->state = EI_DEVICE_STATE_ACCEPTED;
|
||||
}
|
||||
|
||||
_public_ bool
|
||||
ei_device_has_capability(struct ei_device *device,
|
||||
enum ei_device_capability cap)
|
||||
{
|
||||
switch (cap) {
|
||||
case EI_DEVICE_CAP_POINTER:
|
||||
case EI_DEVICE_CAP_POINTER_ABSOLUTE:
|
||||
case EI_DEVICE_CAP_KEYBOARD:
|
||||
case EI_DEVICE_CAP_TOUCH:
|
||||
return flag_is_set(device->capabilities, cap);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_public_ void
|
||||
ei_device_pointer_motion(struct ei_device *device,
|
||||
int32_t x, int32_t y)
|
||||
{
|
||||
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER))
|
||||
return;
|
||||
|
||||
if (device->state != EI_DEVICE_STATE_ACCEPTED)
|
||||
return;
|
||||
|
||||
ei_pointer_rel(device, x, y);
|
||||
}
|
||||
101
src/libei-private.h
Normal file
101
src/libei-private.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util-object.h"
|
||||
|
||||
#include "libei.h"
|
||||
#include "util-list.h"
|
||||
#include "util-sources.h"
|
||||
|
||||
struct ei_backend {
|
||||
void (*destroy)(struct ei *ei);
|
||||
};
|
||||
|
||||
enum ei_state {
|
||||
EI_STATE_NEW, /* just connected, server sent hello */
|
||||
EI_STATE_CONNECTING, /* client requested connect */
|
||||
EI_STATE_CONNECTED, /* server has sent connect */
|
||||
EI_STATE_DISCONNECTED,
|
||||
};
|
||||
|
||||
struct ei {
|
||||
struct object object;
|
||||
void *user_data;
|
||||
struct logger *logger;
|
||||
struct sink *sink;
|
||||
struct source *source;
|
||||
struct ei_backend backend;
|
||||
enum ei_state state;
|
||||
struct list event_queue;
|
||||
struct list devices;
|
||||
};
|
||||
|
||||
enum ei_device_state {
|
||||
EI_DEVICE_STATE_NEW,
|
||||
EI_DEVICE_STATE_ACCEPTED,
|
||||
EI_DEVICE_STATE_REMOVED,
|
||||
};
|
||||
|
||||
struct ei_device {
|
||||
struct object object;
|
||||
struct list link;
|
||||
uint32_t id;
|
||||
enum ei_device_state state;
|
||||
uint32_t capabilities;
|
||||
};
|
||||
|
||||
struct ei_event {
|
||||
struct object object;
|
||||
enum ei_event_type type;
|
||||
struct list link;
|
||||
struct ei_client *client;
|
||||
struct ei_device *device;
|
||||
};
|
||||
|
||||
struct ei_event_client {
|
||||
struct ei_event base;
|
||||
};
|
||||
|
||||
void
|
||||
ei_init_object(struct ei *ei, struct object *parent);
|
||||
|
||||
int
|
||||
ei_init(struct ei *ei);
|
||||
|
||||
int
|
||||
ei_set_connection(struct ei *ei, int fd);
|
||||
|
||||
int
|
||||
ei_add_device(struct ei_device *device);
|
||||
|
||||
int
|
||||
ei_remove_device(struct ei_device *device);
|
||||
|
||||
int
|
||||
ei_pointer_rel(struct ei_device *device,
|
||||
int32_t x, int32_t y);
|
||||
|
||||
void
|
||||
ei_device_accepted(struct ei_device *device);
|
||||
111
src/libei-socket.c
Normal file
111
src/libei-socket.c
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#include "util-logger.h"
|
||||
#include "util-mem.h"
|
||||
#include "util-macros.h"
|
||||
#include "util-sources.h"
|
||||
#include "util-strings.h"
|
||||
|
||||
#include "libei.h"
|
||||
#include "libei-private.h"
|
||||
|
||||
struct ei_socket {
|
||||
struct ei base;
|
||||
struct source *connection;
|
||||
};
|
||||
|
||||
static inline struct ei_socket *
|
||||
ei_socket(struct ei *ei)
|
||||
{
|
||||
return container_of(ei, struct ei_socket, base);
|
||||
}
|
||||
|
||||
static void
|
||||
socket_destroy(struct ei *ei)
|
||||
{
|
||||
}
|
||||
|
||||
const struct ei_backend backend = {
|
||||
.destroy = socket_destroy,
|
||||
};
|
||||
|
||||
_public_ struct ei *
|
||||
ei_socket_new_context(void *user_data)
|
||||
{
|
||||
struct ei_socket *ctx = xalloc(sizeof *ctx);
|
||||
|
||||
ei_init_object(&ctx->base, NULL);
|
||||
|
||||
ctx->base.user_data = user_data;
|
||||
ctx->base.backend = backend;
|
||||
|
||||
return &ctx->base;
|
||||
}
|
||||
|
||||
_public_ int
|
||||
ei_socket_init(struct ei *ei, const char *socketpath)
|
||||
{
|
||||
assert(ei);
|
||||
assert(socketpath);
|
||||
assert(socketpath[0] != '\0');
|
||||
|
||||
int rc = ei_init(ei);
|
||||
if (rc)
|
||||
return -rc;
|
||||
|
||||
_cleanup_free_ char *path;
|
||||
if (socketpath[0] == '/') {
|
||||
path = xstrdup(socketpath);
|
||||
} else {
|
||||
const char *xdg = getenv("XDG_RUNTIME_DIR");
|
||||
if (!xdg)
|
||||
return -ENOTDIR;
|
||||
path = xaprintf("%s/%s", xdg, socketpath);
|
||||
}
|
||||
|
||||
struct sockaddr_un addr = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = {0},
|
||||
};
|
||||
if (!xsnprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path))
|
||||
return -EINVAL;
|
||||
|
||||
int sockfd = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0);
|
||||
if (sockfd == -1)
|
||||
return -errno;
|
||||
|
||||
if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
|
||||
return -errno;
|
||||
|
||||
return ei_set_connection(ei, sockfd);
|
||||
}
|
||||
533
src/libei.c
533
src/libei.c
|
|
@ -21,4 +21,537 @@
|
|||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#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"
|
||||
|
||||
#include "libei.h"
|
||||
#include "libei-private.h"
|
||||
|
||||
/* The message type for the wire format */
|
||||
enum message_type {
|
||||
MESSAGE_HELLO,
|
||||
MESSAGE_CONNECTED,
|
||||
MESSAGE_DISCONNECTED,
|
||||
MESSAGE_ACCEPT,
|
||||
MESSAGE_REMOVE,
|
||||
};
|
||||
|
||||
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;
|
||||
struct message_accept {
|
||||
uint32_t deviceid;
|
||||
} accept;
|
||||
struct message_remove{
|
||||
uint32_t deviceid;
|
||||
} remove;
|
||||
};
|
||||
};
|
||||
|
||||
static void
|
||||
message_free(struct message *msg)
|
||||
{
|
||||
switch (msg->type) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
free(msg);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct message*, message_free);
|
||||
|
||||
|
||||
static struct ei_event* ei_event_ref(struct ei_event *event);
|
||||
|
||||
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
|
||||
OBJECT_DECLARE_INIT(ei_event);
|
||||
/* this one is not public */
|
||||
OBJECT_DECLARE_REF(ei_event);
|
||||
_public_
|
||||
OBJECT_DECLARE_UNREF(ei_event);
|
||||
_public_
|
||||
OBJECT_DECLARE_GETTER(ei_event, type, enum ei_event_type);
|
||||
_public_
|
||||
OBJECT_DECLARE_GETTER(ei_event, device, struct ei_device*);
|
||||
|
||||
static void
|
||||
ei_destroy(struct ei *ei)
|
||||
{
|
||||
ei->logger = logger_unref(ei->logger);
|
||||
if (ei->backend.destroy)
|
||||
ei->backend.destroy(ei);
|
||||
sink_unref(ei->sink);
|
||||
}
|
||||
|
||||
OBJECT_DECLARE_INIT(ei);
|
||||
_public_
|
||||
OBJECT_DECLARE_REF(ei);
|
||||
_public_
|
||||
OBJECT_DECLARE_UNREF(ei);
|
||||
#define _cleanup_ei_ _cleanup_(ei_cleanup)
|
||||
_public_
|
||||
OBJECT_DECLARE_SETTER(ei, user_data, void *);
|
||||
OBJECT_DECLARE_GETTER(ei, user_data, void *);
|
||||
|
||||
int
|
||||
ei_init(struct ei *ei)
|
||||
{
|
||||
ei_init_object(ei, NULL);
|
||||
ei->state = EI_STATE_NEW;
|
||||
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)
|
||||
return -EMFILE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
|
||||
static int
|
||||
connection_send_connect(struct ei *ei)
|
||||
{
|
||||
if (ei->state == EI_STATE_DISCONNECTED)
|
||||
return 0;
|
||||
|
||||
const char buf[] = "connect myclient\n"; /* FIXME */
|
||||
return min(0, xwrite(source_get_fd(ei->source), buf, sizeof(buf)));
|
||||
}
|
||||
|
||||
static int
|
||||
connection_send_disconnect(struct ei *ei)
|
||||
{
|
||||
if (ei->state == EI_STATE_DISCONNECTED)
|
||||
return 0;
|
||||
|
||||
const char buf[] = "disconnect\n";
|
||||
return min(0, xwrite(source_get_fd(ei->source), buf, sizeof(buf)));
|
||||
}
|
||||
|
||||
static int
|
||||
connection_send_add(struct ei *ei, struct ei_device *device)
|
||||
{
|
||||
if (ei->state == EI_STATE_DISCONNECTED)
|
||||
return 0;
|
||||
|
||||
char buf[64];
|
||||
|
||||
if (!xsnprintf(buf, sizeof(buf), "add %d %d\n", device->id, device->capabilities))
|
||||
return -ENOMEM;
|
||||
|
||||
return min(0, xwrite(source_get_fd(ei->source), buf, strlen(buf)));
|
||||
}
|
||||
|
||||
static int
|
||||
connection_send_remove(struct ei *ei, struct ei_device *device)
|
||||
{
|
||||
if (ei->state == EI_STATE_DISCONNECTED)
|
||||
return 0;
|
||||
|
||||
char buf[64];
|
||||
|
||||
if (!xsnprintf(buf, sizeof(buf), "remove %d\n", device->id))
|
||||
return -ENOMEM;
|
||||
|
||||
return min(0, xwrite(source_get_fd(ei->source), buf, strlen(buf)));
|
||||
}
|
||||
|
||||
static int
|
||||
connection_send_rel(struct ei *ei, struct ei_device *device, int32_t x, int32_t y)
|
||||
{
|
||||
if (ei->state == EI_STATE_DISCONNECTED)
|
||||
return 0;
|
||||
|
||||
char buf[64];
|
||||
|
||||
if (!xsnprintf(buf, sizeof(buf), "rel %d %d %d\n", device->id, x, y))
|
||||
return -ENOMEM;
|
||||
|
||||
return min(0, xwrite(source_get_fd(ei->source), buf, strlen(buf)));
|
||||
}
|
||||
|
||||
static void
|
||||
ei_disconnect(struct ei *ei)
|
||||
{
|
||||
connection_send_disconnect(ei);
|
||||
/* FIXME: unroll the devices here */
|
||||
ei_queue_disconnect_event(ei);
|
||||
ei->state = EI_STATE_DISCONNECTED;
|
||||
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;
|
||||
}
|
||||
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);
|
||||
if (rc) {
|
||||
ei_disconnect(ei);
|
||||
return rc;
|
||||
}
|
||||
|
||||
list_remove(&device->link);
|
||||
ei_device_unref(device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
_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 ei_event_ref(e);
|
||||
}
|
||||
|
||||
static struct message *
|
||||
connection_parse_message(const char *data_in, size_t len)
|
||||
{
|
||||
_cleanup_(message_freep) struct message *msg = xalloc(sizeof(*msg));
|
||||
_cleanup_free_ char *data = strstrip(data_in, "\n");
|
||||
_cleanup_(strv_freep) char **tokens = strv_from_string(data, " ");
|
||||
|
||||
if (streq(tokens[0], "hello")) {
|
||||
*msg = (struct message) {
|
||||
.type = MESSAGE_HELLO,
|
||||
};
|
||||
} else if (streq(tokens[0], "connected")) {
|
||||
*msg = (struct message) {
|
||||
.type = MESSAGE_CONNECTED,
|
||||
};
|
||||
} else if (streq(tokens[0], "disconnected")) {
|
||||
*msg = (struct message) {
|
||||
.type = MESSAGE_DISCONNECTED,
|
||||
};
|
||||
} else if (streq(tokens[0], "accept")) {
|
||||
if (!tokens[1])
|
||||
goto error;
|
||||
|
||||
uint32_t deviceid;
|
||||
if (!xatou(tokens[1], &deviceid))
|
||||
goto error;
|
||||
|
||||
*msg = (struct message) {
|
||||
.type = MESSAGE_ACCEPT,
|
||||
.accept.deviceid = deviceid,
|
||||
};
|
||||
} else if (streq(tokens[0], "remove")) {
|
||||
if (!tokens[1])
|
||||
goto error;
|
||||
|
||||
uint32_t deviceid;
|
||||
if (!xatou(tokens[1], &deviceid))
|
||||
goto error;
|
||||
|
||||
*msg = (struct message) {
|
||||
.type = MESSAGE_REMOVE,
|
||||
.remove.deviceid = deviceid,
|
||||
};
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return steal(&msg);
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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:
|
||||
case MESSAGE_ACCEPT:
|
||||
case MESSAGE_REMOVE:
|
||||
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;
|
||||
case MESSAGE_ACCEPT:
|
||||
case MESSAGE_REMOVE:
|
||||
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;
|
||||
case MESSAGE_ACCEPT:
|
||||
rc = ei_accepted(ei, msg->accept.deviceid);
|
||||
break;
|
||||
case MESSAGE_REMOVE:
|
||||
rc = ei_removed(ei, msg->remove.deviceid);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
connection_dispatch(struct source *source, void *data)
|
||||
{
|
||||
struct ei *ei = data;
|
||||
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;
|
||||
}
|
||||
|
||||
msg = connection_parse_message(iobuf_data(buf), iobuf_len(buf));
|
||||
if (!msg) {
|
||||
rc = -EBADMSG;
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (ei->state) {
|
||||
case EI_STATE_NEW:
|
||||
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;
|
||||
case EI_STATE_DISCONNECTED:
|
||||
abort();
|
||||
}
|
||||
|
||||
error:
|
||||
if (rc < 0)
|
||||
ei_disconnect(ei);
|
||||
|
||||
static const char *states[] = {
|
||||
"NEW",
|
||||
"CONNECTING",
|
||||
"CONNECTED",
|
||||
"DISCONNECTED",
|
||||
};
|
||||
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)
|
||||
{
|
||||
ei->source = source_add_autoclose(ei->sink, fd, connection_dispatch, ei);
|
||||
if (!ei->source)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
23
src/libei.h
23
src/libei.h
|
|
@ -35,7 +35,7 @@ struct ei_event_keyboard;
|
|||
struct ei_event_touch;
|
||||
|
||||
enum ei_device_capability {
|
||||
EI_DEVICE_CAP_POINTER,
|
||||
EI_DEVICE_CAP_POINTER = 1,
|
||||
EI_DEVICE_CAP_POINTER_ABSOLUTE,
|
||||
EI_DEVICE_CAP_KEYBOARD,
|
||||
EI_DEVICE_CAP_TOUCH,
|
||||
|
|
@ -63,10 +63,6 @@ enum ei_event_type {
|
|||
* return value for ei_next_event_type().
|
||||
*/
|
||||
EI_EVENT_NONE = 0,
|
||||
/**
|
||||
* Message sent by the server immediately on connect.
|
||||
*/
|
||||
EI_EVENT_HELLO,
|
||||
/**
|
||||
* The server has approved the connection to this client.
|
||||
*/
|
||||
|
|
@ -105,12 +101,24 @@ enum ei_event_type {
|
|||
struct ei *
|
||||
ei_new(void);
|
||||
|
||||
struct ei *
|
||||
ei_socket_new_context(void *user_data);
|
||||
|
||||
int
|
||||
ei_socket_init(struct ei *ei, const char *socketpath);
|
||||
|
||||
struct ei *
|
||||
ei_ref(struct ei *ctx);
|
||||
|
||||
struct ei *
|
||||
ei_unref(struct ei *ei);
|
||||
|
||||
void
|
||||
ei_set_user_data(struct ei *ei, void *user_data);
|
||||
|
||||
void *
|
||||
ei_get_user_data(struct ei *ei);
|
||||
|
||||
/**
|
||||
* Connect to the libeis server via the org.freedesktop.portal
|
||||
*
|
||||
|
|
@ -189,7 +197,7 @@ ei_device_configure_name(struct ei_device *device, const char *name);
|
|||
*
|
||||
* @param cap The capability to enable
|
||||
*/
|
||||
void
|
||||
bool
|
||||
ei_device_configure_capability(struct ei_device *device,
|
||||
enum ei_device_capability cap);
|
||||
|
||||
|
|
@ -439,6 +447,9 @@ ei_device_pointer_scroll_discrete(struct ei_device *device, int32_t x, int32_t y
|
|||
void
|
||||
ei_device_keyboard_key(struct ei_device *device, uint32_t keycode, bool is_press);
|
||||
|
||||
enum ei_event_type
|
||||
ei_event_get_type(struct ei_event *event);
|
||||
|
||||
/**
|
||||
* Return the device from this event.
|
||||
*
|
||||
|
|
|
|||
111
tools/ei-socket-client.c
Normal file
111
tools/ei-socket-client.c
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 <assert.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libei.h"
|
||||
|
||||
#include "src/util-mem.h"
|
||||
#include "src/util-strings.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char SOCKETNAME[] = "eis.socket";
|
||||
struct ei *ei = ei_socket_new_context(NULL);
|
||||
assert(ei);
|
||||
|
||||
/* This should be handled by libei */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
int rc = ei_socket_init(ei, SOCKETNAME);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "init failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("client: connected to %s\n", SOCKETNAME);
|
||||
|
||||
struct pollfd fds = {
|
||||
.fd = ei_get_fd(ei),
|
||||
.events = POLLIN,
|
||||
.revents = 0,
|
||||
};
|
||||
|
||||
struct ei_device *ptr = ei_device_new(ei);
|
||||
ei_device_configure_capability(ptr, EI_DEVICE_CAP_POINTER);
|
||||
|
||||
bool stop = false;
|
||||
bool have_device = false;
|
||||
while (!stop && poll(&fds, 1, 2000) > -1) {
|
||||
ei_dispatch(ei);
|
||||
|
||||
while (true) {
|
||||
struct ei_event *e = ei_get_event(ei);
|
||||
if (!e)
|
||||
break;
|
||||
|
||||
switch(ei_event_get_type(e)) {
|
||||
case EI_EVENT_CONNECT:
|
||||
printf("client: connected\n");
|
||||
ei_device_add(ptr);
|
||||
break;
|
||||
case EI_EVENT_DISCONNECT:
|
||||
{
|
||||
printf("client: disconnected us\n");
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
case EI_EVENT_DEVICE_ADDED:
|
||||
{
|
||||
printf("client: our device was accepted\n");
|
||||
have_device = true;
|
||||
break;
|
||||
}
|
||||
case EI_EVENT_DEVICE_REMOVED:
|
||||
{
|
||||
printf("client: our device was removed\n");
|
||||
have_device = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
ei_event_unref(e);
|
||||
}
|
||||
|
||||
if (have_device) {
|
||||
ei_device_pointer_motion(ptr, -1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
ei_unref(ei);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue