976 lines
26 KiB
Diff
976 lines
26 KiB
Diff
From e202fa31fb2d60084e7b2ab7976a81c138184d40 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Wed, 27 Aug 2014 18:17:27 +0200
|
|
Subject: [PATCH] terminal: add input interface
|
|
|
|
The idev-interface provides input drivers for all libsystemd-terminal
|
|
based applications. It is split into 4 main objects:
|
|
idev_context: The context object tracks global state of the input
|
|
interface. This will include data like system-keymaps,
|
|
xkb contexts and more.
|
|
idev_session: A session serves as controller for a set of devices.
|
|
Each session on an idev-context is independent of each
|
|
other. The session is also the main notification object.
|
|
All events raised via idev are reported through the
|
|
session interface. Apart of that, the session is a
|
|
pretty dumb object that just contains devices.
|
|
idev_element: Elements provide real hardware in the idev stack. For
|
|
each hardware device, one element is added. Elements
|
|
have no knowledge of higher-level device types, they
|
|
only provide raw input data to the upper levels. For
|
|
example, each evdev device is represented by a different
|
|
element in an idev session.
|
|
idev_device: Devices are objects that the application deals with. An
|
|
application is usually not interested in elements (and
|
|
those are hidden to applications), instead, they want
|
|
high-level input devices like keyboard, touchpads, mice
|
|
and more. Device are the high-level interface provided
|
|
by idev. Each device might be fed by a set of elements.
|
|
Elements drive the device. If elements are removed,
|
|
devices are destroyed. If elements are added, suitable
|
|
devices are created.
|
|
|
|
Applications should monitor the system for sessions and hardware devices.
|
|
For each session they want to operate on, they create an idev_session
|
|
object and add hardware to that object. The idev interface requires the
|
|
application to monitor the system (preferably via sysview_*, but not
|
|
required) for hardware devices. Whenever hardware is added to the idev
|
|
session, new devices *might* be created. The relationship between hardware
|
|
and high-level idev-devices is hidden in the idev-session and not exposed.
|
|
|
|
Internally, the idev elements and devices are virtual objects. Each real
|
|
hardware and device type inherits those virtual objects and provides real
|
|
elements and devices. Those types will be added in follow-up commits.
|
|
|
|
Data flow from hardware to the application is done via idev_*_feed()
|
|
functions. Data flow from applications to hardware is done via
|
|
idev_*_feedback() functions. Feedback is usually used for LEDs, FF and
|
|
similar operations.
|
|
---
|
|
Makefile.am | 3 +
|
|
src/libsystemd-terminal/idev-internal.h | 165 +++++++++
|
|
src/libsystemd-terminal/idev.c | 587 ++++++++++++++++++++++++++++++++
|
|
src/libsystemd-terminal/idev.h | 133 ++++++++
|
|
4 files changed, 888 insertions(+)
|
|
create mode 100644 src/libsystemd-terminal/idev-internal.h
|
|
create mode 100644 src/libsystemd-terminal/idev.c
|
|
create mode 100644 src/libsystemd-terminal/idev.h
|
|
|
|
diff --git a/Makefile.am b/Makefile.am
|
|
index 3a263f8c17..82f474e20e 100644
|
|
--- a/Makefile.am
|
|
+++ b/Makefile.am
|
|
@@ -2971,6 +2971,9 @@ libsystemd_terminal_la_CFLAGS = \
|
|
$(AM_CFLAGS)
|
|
|
|
libsystemd_terminal_la_SOURCES = \
|
|
+ src/libsystemd-terminal/idev.h \
|
|
+ src/libsystemd-terminal/idev-internal.h \
|
|
+ src/libsystemd-terminal/idev.c \
|
|
src/libsystemd-terminal/sysview.h \
|
|
src/libsystemd-terminal/sysview-internal.h \
|
|
src/libsystemd-terminal/sysview.c \
|
|
diff --git a/src/libsystemd-terminal/idev-internal.h b/src/libsystemd-terminal/idev-internal.h
|
|
new file mode 100644
|
|
index 0000000000..bffefbb9c1
|
|
--- /dev/null
|
|
+++ b/src/libsystemd-terminal/idev-internal.h
|
|
@@ -0,0 +1,165 @@
|
|
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
|
+
|
|
+/***
|
|
+ This file is part of systemd.
|
|
+
|
|
+ Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
|
|
+
|
|
+ systemd is free software; you can redistribute it and/or modify it
|
|
+ under the terms of the GNU Lesser General Public License as published by
|
|
+ the Free Software Foundation; either version 2.1 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ systemd is distributed in the hope that it will be useful, but
|
|
+ WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ Lesser General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU Lesser General Public License
|
|
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
+***/
|
|
+
|
|
+#pragma once
|
|
+
|
|
+#include <inttypes.h>
|
|
+#include <stdbool.h>
|
|
+#include <stdlib.h>
|
|
+#include <systemd/sd-bus.h>
|
|
+#include <systemd/sd-event.h>
|
|
+#include "hashmap.h"
|
|
+#include "idev.h"
|
|
+#include "list.h"
|
|
+#include "util.h"
|
|
+
|
|
+typedef struct idev_link idev_link;
|
|
+typedef struct idev_device_vtable idev_device_vtable;
|
|
+typedef struct idev_element idev_element;
|
|
+typedef struct idev_element_vtable idev_element_vtable;
|
|
+
|
|
+/*
|
|
+ * Element Links
|
|
+ */
|
|
+
|
|
+struct idev_link {
|
|
+ /* element-to-device connection */
|
|
+ LIST_FIELDS(idev_link, links_by_element);
|
|
+ idev_element *element;
|
|
+
|
|
+ /* device-to-element connection */
|
|
+ LIST_FIELDS(idev_link, links_by_device);
|
|
+ idev_device *device;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Devices
|
|
+ */
|
|
+
|
|
+struct idev_device_vtable {
|
|
+ void (*free) (idev_device *d);
|
|
+ void (*attach) (idev_device *d, idev_link *l);
|
|
+ void (*detach) (idev_device *d, idev_link *l);
|
|
+ int (*feed) (idev_device *d, idev_data *data);
|
|
+};
|
|
+
|
|
+struct idev_device {
|
|
+ const idev_device_vtable *vtable;
|
|
+ idev_session *session;
|
|
+ char *name;
|
|
+
|
|
+ LIST_HEAD(idev_link, links);
|
|
+
|
|
+ bool public : 1;
|
|
+ bool enabled : 1;
|
|
+};
|
|
+
|
|
+#define IDEV_DEVICE_INIT(_vtable, _session) ((idev_device){ \
|
|
+ .vtable = (_vtable), \
|
|
+ .session = (_session), \
|
|
+ })
|
|
+
|
|
+idev_device *idev_find_device(idev_session *s, const char *name);
|
|
+
|
|
+int idev_device_add(idev_device *d, const char *name);
|
|
+idev_device *idev_device_free(idev_device *d);
|
|
+
|
|
+DEFINE_TRIVIAL_CLEANUP_FUNC(idev_device*, idev_device_free);
|
|
+
|
|
+int idev_device_feed(idev_device *d, idev_data *data);
|
|
+void idev_device_feedback(idev_device *d, idev_data *data);
|
|
+
|
|
+/*
|
|
+ * Elements
|
|
+ */
|
|
+
|
|
+struct idev_element_vtable {
|
|
+ void (*free) (idev_element *e);
|
|
+ void (*enable) (idev_element *e);
|
|
+ void (*disable) (idev_element *e);
|
|
+ void (*open) (idev_element *e);
|
|
+ void (*close) (idev_element *e);
|
|
+ void (*feedback) (idev_element *e, idev_data *data);
|
|
+};
|
|
+
|
|
+struct idev_element {
|
|
+ const idev_element_vtable *vtable;
|
|
+ idev_session *session;
|
|
+ unsigned long n_open;
|
|
+ char *name;
|
|
+
|
|
+ LIST_HEAD(idev_link, links);
|
|
+
|
|
+ bool enabled : 1;
|
|
+ bool readable : 1;
|
|
+ bool writable : 1;
|
|
+};
|
|
+
|
|
+#define IDEV_ELEMENT_INIT(_vtable, _session) ((idev_element){ \
|
|
+ .vtable = (_vtable), \
|
|
+ .session = (_session), \
|
|
+ })
|
|
+
|
|
+idev_element *idev_find_element(idev_session *s, const char *name);
|
|
+
|
|
+int idev_element_add(idev_element *e, const char *name);
|
|
+idev_element *idev_element_free(idev_element *e);
|
|
+
|
|
+DEFINE_TRIVIAL_CLEANUP_FUNC(idev_element*, idev_element_free);
|
|
+
|
|
+int idev_element_feed(idev_element *e, idev_data *data);
|
|
+void idev_element_feedback(idev_element *e, idev_data *data);
|
|
+
|
|
+/*
|
|
+ * Sessions
|
|
+ */
|
|
+
|
|
+struct idev_session {
|
|
+ idev_context *context;
|
|
+ char *name;
|
|
+ char *path;
|
|
+
|
|
+ Hashmap *element_map;
|
|
+ Hashmap *device_map;
|
|
+
|
|
+ idev_event_fn event_fn;
|
|
+ void *userdata;
|
|
+
|
|
+ bool custom : 1;
|
|
+ bool managed : 1;
|
|
+ bool enabled : 1;
|
|
+};
|
|
+
|
|
+idev_session *idev_find_session(idev_context *c, const char *name);
|
|
+int idev_session_raise_device_data(idev_session *s, idev_device *d, idev_data *data);
|
|
+
|
|
+/*
|
|
+ * Contexts
|
|
+ */
|
|
+
|
|
+struct idev_context {
|
|
+ unsigned long ref;
|
|
+ sd_event *event;
|
|
+ sd_bus *sysbus;
|
|
+
|
|
+ Hashmap *session_map;
|
|
+ Hashmap *data_map;
|
|
+};
|
|
diff --git a/src/libsystemd-terminal/idev.c b/src/libsystemd-terminal/idev.c
|
|
new file mode 100644
|
|
index 0000000000..5e3080797a
|
|
--- /dev/null
|
|
+++ b/src/libsystemd-terminal/idev.c
|
|
@@ -0,0 +1,587 @@
|
|
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
|
+
|
|
+/***
|
|
+ This file is part of systemd.
|
|
+
|
|
+ Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
|
|
+
|
|
+ systemd is free software; you can redistribute it and/or modify it
|
|
+ under the terms of the GNU Lesser General Public License as published by
|
|
+ the Free Software Foundation; either version 2.1 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ systemd is distributed in the hope that it will be useful, but
|
|
+ WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ Lesser General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU Lesser General Public License
|
|
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
+***/
|
|
+
|
|
+#include <inttypes.h>
|
|
+#include <stdbool.h>
|
|
+#include <stdlib.h>
|
|
+#include <systemd/sd-bus.h>
|
|
+#include <systemd/sd-event.h>
|
|
+#include <systemd/sd-login.h>
|
|
+#include "hashmap.h"
|
|
+#include "idev.h"
|
|
+#include "idev-internal.h"
|
|
+#include "login-shared.h"
|
|
+#include "macro.h"
|
|
+#include "set.h"
|
|
+#include "util.h"
|
|
+
|
|
+static void element_open(idev_element *e);
|
|
+static void element_close(idev_element *e);
|
|
+
|
|
+/*
|
|
+ * Devices
|
|
+ */
|
|
+
|
|
+idev_device *idev_find_device(idev_session *s, const char *name) {
|
|
+ assert_return(s, NULL);
|
|
+ assert_return(name, NULL);
|
|
+
|
|
+ return hashmap_get(s->device_map, name);
|
|
+}
|
|
+
|
|
+int idev_device_add(idev_device *d, const char *name) {
|
|
+ int r;
|
|
+
|
|
+ assert_return(d, -EINVAL);
|
|
+ assert_return(d->vtable, -EINVAL);
|
|
+ assert_return(d->session, -EINVAL);
|
|
+ assert_return(name, -EINVAL);
|
|
+
|
|
+ d->name = strdup(name);
|
|
+ if (!d->name)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ r = hashmap_put(d->session->device_map, d->name, d);
|
|
+ if (r < 0)
|
|
+ return r;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+idev_device *idev_device_free(idev_device *d) {
|
|
+ idev_device tmp;
|
|
+
|
|
+ if (!d)
|
|
+ return NULL;
|
|
+
|
|
+ assert(!d->enabled);
|
|
+ assert(!d->public);
|
|
+ assert(!d->links);
|
|
+ assert(d->vtable);
|
|
+ assert(d->vtable->free);
|
|
+
|
|
+ if (d->name)
|
|
+ hashmap_remove_value(d->session->device_map, d->name, d);
|
|
+
|
|
+ tmp = *d;
|
|
+ d->vtable->free(d);
|
|
+
|
|
+ free(tmp.name);
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+int idev_device_feed(idev_device *d, idev_data *data) {
|
|
+ assert(d);
|
|
+ assert(data);
|
|
+ assert(data->type < IDEV_DATA_CNT);
|
|
+
|
|
+ if (d->vtable->feed)
|
|
+ return d->vtable->feed(d, data);
|
|
+ else
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void idev_device_feedback(idev_device *d, idev_data *data) {
|
|
+ idev_link *l;
|
|
+
|
|
+ assert(d);
|
|
+ assert(data);
|
|
+ assert(data->type < IDEV_DATA_CNT);
|
|
+
|
|
+ LIST_FOREACH(links_by_device, l, d->links)
|
|
+ idev_element_feedback(l->element, data);
|
|
+}
|
|
+
|
|
+static void device_attach(idev_device *d, idev_link *l) {
|
|
+ assert(d);
|
|
+ assert(l);
|
|
+
|
|
+ if (d->vtable->attach)
|
|
+ d->vtable->attach(d, l);
|
|
+
|
|
+ if (d->enabled)
|
|
+ element_open(l->element);
|
|
+}
|
|
+
|
|
+static void device_detach(idev_device *d, idev_link *l) {
|
|
+ assert(d);
|
|
+ assert(l);
|
|
+
|
|
+ if (d->enabled)
|
|
+ element_close(l->element);
|
|
+
|
|
+ if (d->vtable->detach)
|
|
+ d->vtable->detach(d, l);
|
|
+}
|
|
+
|
|
+void idev_device_enable(idev_device *d) {
|
|
+ idev_link *l;
|
|
+
|
|
+ assert(d);
|
|
+
|
|
+ if (!d->enabled) {
|
|
+ d->enabled = true;
|
|
+ LIST_FOREACH(links_by_device, l, d->links)
|
|
+ element_open(l->element);
|
|
+ }
|
|
+}
|
|
+
|
|
+void idev_device_disable(idev_device *d) {
|
|
+ idev_link *l;
|
|
+
|
|
+ assert(d);
|
|
+
|
|
+ if (d->enabled) {
|
|
+ d->enabled = false;
|
|
+ LIST_FOREACH(links_by_device, l, d->links)
|
|
+ element_close(l->element);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Elements
|
|
+ */
|
|
+
|
|
+idev_element *idev_find_element(idev_session *s, const char *name) {
|
|
+ assert_return(s, NULL);
|
|
+ assert_return(name, NULL);
|
|
+
|
|
+ return hashmap_get(s->element_map, name);
|
|
+}
|
|
+
|
|
+int idev_element_add(idev_element *e, const char *name) {
|
|
+ int r;
|
|
+
|
|
+ assert_return(e, -EINVAL);
|
|
+ assert_return(e->vtable, -EINVAL);
|
|
+ assert_return(e->session, -EINVAL);
|
|
+ assert_return(name, -EINVAL);
|
|
+
|
|
+ e->name = strdup(name);
|
|
+ if (!e->name)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ r = hashmap_put(e->session->element_map, e->name, e);
|
|
+ if (r < 0)
|
|
+ return r;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+idev_element *idev_element_free(idev_element *e) {
|
|
+ idev_element tmp;
|
|
+
|
|
+ if (!e)
|
|
+ return NULL;
|
|
+
|
|
+ assert(!e->enabled);
|
|
+ assert(!e->links);
|
|
+ assert(e->n_open == 0);
|
|
+ assert(e->vtable);
|
|
+ assert(e->vtable->free);
|
|
+
|
|
+ if (e->name)
|
|
+ hashmap_remove_value(e->session->element_map, e->name, e);
|
|
+
|
|
+ tmp = *e;
|
|
+ e->vtable->free(e);
|
|
+
|
|
+ free(tmp.name);
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+int idev_element_feed(idev_element *e, idev_data *data) {
|
|
+ int r, error = 0;
|
|
+ idev_link *l;
|
|
+
|
|
+ assert(e);
|
|
+ assert(data);
|
|
+ assert(data->type < IDEV_DATA_CNT);
|
|
+
|
|
+ LIST_FOREACH(links_by_element, l, e->links) {
|
|
+ r = idev_device_feed(l->device, data);
|
|
+ if (r != 0)
|
|
+ error = r;
|
|
+ }
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+void idev_element_feedback(idev_element *e, idev_data *data) {
|
|
+ assert(e);
|
|
+ assert(data);
|
|
+ assert(data->type < IDEV_DATA_CNT);
|
|
+
|
|
+ if (e->vtable->feedback)
|
|
+ e->vtable->feedback(e, data);
|
|
+}
|
|
+
|
|
+static void element_open(idev_element *e) {
|
|
+ assert(e);
|
|
+
|
|
+ if (e->n_open++ == 0 && e->vtable->open)
|
|
+ e->vtable->open(e);
|
|
+}
|
|
+
|
|
+static void element_close(idev_element *e) {
|
|
+ assert(e);
|
|
+ assert(e->n_open > 0);
|
|
+
|
|
+ if (--e->n_open == 0 && e->vtable->close)
|
|
+ e->vtable->close(e);
|
|
+}
|
|
+
|
|
+static void element_enable(idev_element *e) {
|
|
+ assert(e);
|
|
+
|
|
+ if (!e->enabled) {
|
|
+ e->enabled = true;
|
|
+ if (e->vtable->enable)
|
|
+ e->vtable->enable(e);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void element_disable(idev_element *e) {
|
|
+ assert(e);
|
|
+
|
|
+ if (e->enabled) {
|
|
+ e->enabled = false;
|
|
+ if (e->vtable->disable)
|
|
+ e->vtable->disable(e);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Sessions
|
|
+ */
|
|
+
|
|
+static int session_raise(idev_session *s, idev_event *ev) {
|
|
+ return s->event_fn(s, s->userdata, ev);
|
|
+}
|
|
+
|
|
+static int session_raise_device_add(idev_session *s, idev_device *d) {
|
|
+ idev_event event = {
|
|
+ .type = IDEV_EVENT_DEVICE_ADD,
|
|
+ .device_add = {
|
|
+ .device = d,
|
|
+ },
|
|
+ };
|
|
+
|
|
+ return session_raise(s, &event);
|
|
+}
|
|
+
|
|
+static int session_raise_device_remove(idev_session *s, idev_device *d) {
|
|
+ idev_event event = {
|
|
+ .type = IDEV_EVENT_DEVICE_REMOVE,
|
|
+ .device_remove = {
|
|
+ .device = d,
|
|
+ },
|
|
+ };
|
|
+
|
|
+ return session_raise(s, &event);
|
|
+}
|
|
+
|
|
+int idev_session_raise_device_data(idev_session *s, idev_device *d, idev_data *data) {
|
|
+ idev_event event = {
|
|
+ .type = IDEV_EVENT_DEVICE_DATA,
|
|
+ .device_data = {
|
|
+ .device = d,
|
|
+ .data = *data,
|
|
+ },
|
|
+ };
|
|
+
|
|
+ return session_raise(s, &event);
|
|
+}
|
|
+
|
|
+static int session_add_device(idev_session *s, idev_device *d) {
|
|
+ int r;
|
|
+
|
|
+ assert(s);
|
|
+ assert(d);
|
|
+
|
|
+ log_debug("idev: %s: add device '%s'", s->name, d->name);
|
|
+
|
|
+ d->public = true;
|
|
+ r = session_raise_device_add(s, d);
|
|
+ if (r != 0) {
|
|
+ d->public = false;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ if (r < 0)
|
|
+ log_debug("idev: %s: error while adding device '%s': %s",
|
|
+ s->name, d->name, strerror(-r));
|
|
+ return r;
|
|
+}
|
|
+
|
|
+static int session_remove_device(idev_session *s, idev_device *d) {
|
|
+ int r, error = 0;
|
|
+
|
|
+ assert(s);
|
|
+ assert(d);
|
|
+
|
|
+ log_debug("idev: %s: remove device '%s'", s->name, d->name);
|
|
+
|
|
+ d->public = false;
|
|
+ r = session_raise_device_remove(s, d);
|
|
+ if (r != 0)
|
|
+ error = r;
|
|
+
|
|
+ idev_device_disable(d);
|
|
+
|
|
+ if (error < 0)
|
|
+ log_debug("idev: %s: error while removing device '%s': %s",
|
|
+ s->name, d->name, strerror(-error));
|
|
+ idev_device_free(d);
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static int session_add_element(idev_session *s, idev_element *e) {
|
|
+ assert(s);
|
|
+ assert(e);
|
|
+
|
|
+ log_debug("idev: %s: add element '%s'", s->name, e->name);
|
|
+
|
|
+ if (s->enabled)
|
|
+ element_enable(e);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int session_remove_element(idev_session *s, idev_element *e) {
|
|
+ int r, error = 0;
|
|
+ idev_device *d;
|
|
+ idev_link *l;
|
|
+
|
|
+ assert(s);
|
|
+ assert(e);
|
|
+
|
|
+ log_debug("idev: %s: remove element '%s'", s->name, e->name);
|
|
+
|
|
+ while ((l = e->links)) {
|
|
+ d = l->device;
|
|
+ LIST_REMOVE(links_by_device, d->links, l);
|
|
+ LIST_REMOVE(links_by_element, e->links, l);
|
|
+ device_detach(d, l);
|
|
+
|
|
+ if (!d->links) {
|
|
+ r = session_remove_device(s, d);
|
|
+ if (r != 0)
|
|
+ error = r;
|
|
+ }
|
|
+
|
|
+ l->device = NULL;
|
|
+ l->element = NULL;
|
|
+ free(l);
|
|
+ }
|
|
+
|
|
+ element_disable(e);
|
|
+
|
|
+ if (error < 0)
|
|
+ log_debug("idev: %s: error while removing element '%s': %s",
|
|
+ s->name, e->name, strerror(-r));
|
|
+ idev_element_free(e);
|
|
+ return error;
|
|
+}
|
|
+
|
|
+idev_session *idev_find_session(idev_context *c, const char *name) {
|
|
+ assert_return(c, NULL);
|
|
+ assert_return(name, NULL);
|
|
+
|
|
+ return hashmap_get(c->session_map, name);
|
|
+}
|
|
+
|
|
+int idev_session_new(idev_session **out,
|
|
+ idev_context *c,
|
|
+ unsigned int flags,
|
|
+ const char *name,
|
|
+ idev_event_fn event_fn,
|
|
+ void *userdata) {
|
|
+ _cleanup_(idev_session_freep) idev_session *s = NULL;
|
|
+ int r;
|
|
+
|
|
+ assert_return(out, -EINVAL);
|
|
+ assert_return(c, -EINVAL);
|
|
+ assert_return(name, -EINVAL);
|
|
+ assert_return(event_fn, -EINVAL);
|
|
+ assert_return((flags & IDEV_SESSION_CUSTOM) == !session_id_valid(name), -EINVAL);
|
|
+ assert_return(!(flags & IDEV_SESSION_CUSTOM) || !(flags & IDEV_SESSION_MANAGED), -EINVAL);
|
|
+ assert_return(!(flags & IDEV_SESSION_MANAGED) || c->sysbus, -EINVAL);
|
|
+
|
|
+ s = new0(idev_session, 1);
|
|
+ if (!s)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ s->context = idev_context_ref(c);
|
|
+ s->custom = flags & IDEV_SESSION_CUSTOM;
|
|
+ s->managed = flags & IDEV_SESSION_MANAGED;
|
|
+ s->event_fn = event_fn;
|
|
+ s->userdata = userdata;
|
|
+
|
|
+ s->name = strdup(name);
|
|
+ if (!s->name)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ if (s->managed) {
|
|
+ r = sd_bus_path_encode("/org/freedesktop/login1/session", s->name, &s->path);
|
|
+ if (r < 0)
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+ s->element_map = hashmap_new(string_hash_func, string_compare_func);
|
|
+ if (!s->element_map)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ s->device_map = hashmap_new(string_hash_func, string_compare_func);
|
|
+ if (!s->device_map)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ r = hashmap_put(c->session_map, s->name, s);
|
|
+ if (r < 0)
|
|
+ return r;
|
|
+
|
|
+ *out = s;
|
|
+ s = NULL;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+idev_session *idev_session_free(idev_session *s) {
|
|
+ idev_element *e;
|
|
+
|
|
+ if (!s)
|
|
+ return NULL;
|
|
+
|
|
+ while ((e = hashmap_first(s->element_map)))
|
|
+ session_remove_element(s, e);
|
|
+
|
|
+ assert(hashmap_size(s->device_map) == 0);
|
|
+
|
|
+ if (s->name)
|
|
+ hashmap_remove_value(s->context->session_map, s->name, s);
|
|
+
|
|
+ s->context = idev_context_unref(s->context);
|
|
+ hashmap_free(s->device_map);
|
|
+ hashmap_free(s->element_map);
|
|
+ free(s->path);
|
|
+ free(s->name);
|
|
+ free(s);
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+bool idev_session_is_enabled(idev_session *s) {
|
|
+ return s && s->enabled;
|
|
+}
|
|
+
|
|
+void idev_session_enable(idev_session *s) {
|
|
+ idev_element *e;
|
|
+ Iterator i;
|
|
+
|
|
+ assert(s);
|
|
+
|
|
+ if (!s->enabled) {
|
|
+ s->enabled = true;
|
|
+ HASHMAP_FOREACH(e, s->element_map, i)
|
|
+ element_enable(e);
|
|
+ }
|
|
+}
|
|
+
|
|
+void idev_session_disable(idev_session *s) {
|
|
+ idev_element *e;
|
|
+ Iterator i;
|
|
+
|
|
+ assert(s);
|
|
+
|
|
+ if (s->enabled) {
|
|
+ s->enabled = false;
|
|
+ HASHMAP_FOREACH(e, s->element_map, i)
|
|
+ element_disable(e);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Contexts
|
|
+ */
|
|
+
|
|
+int idev_context_new(idev_context **out, sd_event *event, sd_bus *sysbus) {
|
|
+ _cleanup_(idev_context_unrefp) idev_context *c = NULL;
|
|
+
|
|
+ assert_return(out, -EINVAL);
|
|
+ assert_return(event, -EINVAL);
|
|
+
|
|
+ c = new0(idev_context, 1);
|
|
+ if (!c)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ c->ref = 1;
|
|
+ c->event = sd_event_ref(event);
|
|
+
|
|
+ if (sysbus)
|
|
+ c->sysbus = sd_bus_ref(sysbus);
|
|
+
|
|
+ c->session_map = hashmap_new(string_hash_func, string_compare_func);
|
|
+ if (!c->session_map)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ c->data_map = hashmap_new(string_hash_func, string_compare_func);
|
|
+ if (!c->data_map)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ *out = c;
|
|
+ c = NULL;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void context_cleanup(idev_context *c) {
|
|
+ assert(hashmap_size(c->data_map) == 0);
|
|
+ assert(hashmap_size(c->session_map) == 0);
|
|
+
|
|
+ hashmap_free(c->data_map);
|
|
+ hashmap_free(c->session_map);
|
|
+ c->sysbus = sd_bus_unref(c->sysbus);
|
|
+ c->event = sd_event_unref(c->event);
|
|
+ free(c);
|
|
+}
|
|
+
|
|
+idev_context *idev_context_ref(idev_context *c) {
|
|
+ assert_return(c, NULL);
|
|
+ assert_return(c->ref > 0, NULL);
|
|
+
|
|
+ ++c->ref;
|
|
+ return c;
|
|
+}
|
|
+
|
|
+idev_context *idev_context_unref(idev_context *c) {
|
|
+ if (!c)
|
|
+ return NULL;
|
|
+
|
|
+ assert_return(c->ref > 0, NULL);
|
|
+
|
|
+ if (--c->ref == 0)
|
|
+ context_cleanup(c);
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
diff --git a/src/libsystemd-terminal/idev.h b/src/libsystemd-terminal/idev.h
|
|
new file mode 100644
|
|
index 0000000000..6f618f37af
|
|
--- /dev/null
|
|
+++ b/src/libsystemd-terminal/idev.h
|
|
@@ -0,0 +1,133 @@
|
|
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
|
+
|
|
+/***
|
|
+ This file is part of systemd.
|
|
+
|
|
+ Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
|
|
+
|
|
+ systemd is free software; you can redistribute it and/or modify it
|
|
+ under the terms of the GNU Lesser General Public License as published by
|
|
+ the Free Software Foundation; either version 2.1 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ systemd is distributed in the hope that it will be useful, but
|
|
+ WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ Lesser General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU Lesser General Public License
|
|
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
+***/
|
|
+
|
|
+/*
|
|
+ * IDev
|
|
+ */
|
|
+
|
|
+#pragma once
|
|
+
|
|
+#include <inttypes.h>
|
|
+#include <stdbool.h>
|
|
+#include <stdlib.h>
|
|
+#include <systemd/sd-bus.h>
|
|
+#include <systemd/sd-event.h>
|
|
+#include "util.h"
|
|
+
|
|
+typedef struct idev_data idev_data;
|
|
+
|
|
+typedef struct idev_event idev_event;
|
|
+typedef struct idev_device idev_device;
|
|
+typedef struct idev_session idev_session;
|
|
+typedef struct idev_context idev_context;
|
|
+
|
|
+/*
|
|
+ * Types
|
|
+ */
|
|
+
|
|
+enum {
|
|
+ IDEV_ELEMENT_CNT
|
|
+};
|
|
+
|
|
+enum {
|
|
+ IDEV_DEVICE_CNT
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Data Packets
|
|
+ */
|
|
+
|
|
+enum {
|
|
+ IDEV_DATA_RESYNC,
|
|
+ IDEV_DATA_CNT
|
|
+};
|
|
+
|
|
+struct idev_data {
|
|
+ unsigned int type;
|
|
+ bool resync : 1;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Events
|
|
+ */
|
|
+
|
|
+enum {
|
|
+ IDEV_EVENT_DEVICE_ADD,
|
|
+ IDEV_EVENT_DEVICE_REMOVE,
|
|
+ IDEV_EVENT_DEVICE_DATA,
|
|
+ IDEV_EVENT_CNT
|
|
+};
|
|
+
|
|
+struct idev_event {
|
|
+ unsigned int type;
|
|
+ union {
|
|
+ struct {
|
|
+ idev_device *device;
|
|
+ } device_add, device_remove;
|
|
+
|
|
+ struct {
|
|
+ idev_device *device;
|
|
+ idev_data data;
|
|
+ } device_data;
|
|
+ };
|
|
+};
|
|
+
|
|
+typedef int (*idev_event_fn) (idev_session *s, void *userdata, idev_event *ev);
|
|
+
|
|
+/*
|
|
+ * Devices
|
|
+ */
|
|
+
|
|
+void idev_device_enable(idev_device *d);
|
|
+void idev_device_disable(idev_device *d);
|
|
+
|
|
+/*
|
|
+ * Sessions
|
|
+ */
|
|
+
|
|
+enum {
|
|
+ IDEV_SESSION_CUSTOM = (1 << 0),
|
|
+ IDEV_SESSION_MANAGED = (1 << 1),
|
|
+};
|
|
+
|
|
+int idev_session_new(idev_session **out,
|
|
+ idev_context *c,
|
|
+ unsigned int flags,
|
|
+ const char *name,
|
|
+ idev_event_fn event_fn,
|
|
+ void *userdata);
|
|
+idev_session *idev_session_free(idev_session *s);
|
|
+
|
|
+DEFINE_TRIVIAL_CLEANUP_FUNC(idev_session*, idev_session_free);
|
|
+
|
|
+bool idev_session_is_enabled(idev_session *s);
|
|
+void idev_session_enable(idev_session *s);
|
|
+void idev_session_disable(idev_session *s);
|
|
+
|
|
+/*
|
|
+ * Contexts
|
|
+ */
|
|
+
|
|
+int idev_context_new(idev_context **out, sd_event *event, sd_bus *sysbus);
|
|
+idev_context *idev_context_ref(idev_context *c);
|
|
+idev_context *idev_context_unref(idev_context *c);
|
|
+
|
|
+DEFINE_TRIVIAL_CLEANUP_FUNC(idev_context*, idev_context_unref);
|