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:
Peter Hutterer 2020-07-29 11:53:03 +10:00
parent 27670000e4
commit 802dfd4ca6
7 changed files with 1007 additions and 6 deletions

View file

@ -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
View 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
View 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
View 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);
}

View file

@ -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;
}

View file

@ -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
View 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;
}