diff --git a/meson.build b/meson.build index afea523..bf14390 100644 --- a/meson.build +++ b/meson.build @@ -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) diff --git a/src/libei-device.c b/src/libei-device.c new file mode 100644 index 0000000..750ece8 --- /dev/null +++ b/src/libei-device.c @@ -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 + +#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); +} diff --git a/src/libei-private.h b/src/libei-private.h new file mode 100644 index 0000000..3fc8763 --- /dev/null +++ b/src/libei-private.h @@ -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); diff --git a/src/libei-socket.c b/src/libei-socket.c new file mode 100644 index 0000000..f4bcd4a --- /dev/null +++ b/src/libei-socket.c @@ -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 +#include +#include +#include +#include + + +#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); +} diff --git a/src/libei.c b/src/libei.c index e92ee69..861f879 100644 --- a/src/libei.c +++ b/src/libei.c @@ -21,4 +21,537 @@ * DEALINGS IN THE SOFTWARE. */ +#include "config.h" + +#include +#include +#include +#include + +#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; +} diff --git a/src/libei.h b/src/libei.h index 4ae4ac3..3f37997 100644 --- a/src/libei.h +++ b/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. * diff --git a/tools/ei-socket-client.c b/tools/ei-socket-client.c new file mode 100644 index 0000000..8cbc5a5 --- /dev/null +++ b/tools/ei-socket-client.c @@ -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 +#include +#include +#include +#include +#include + +#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; +}