X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-terminal%2Fidev.c;h=44be7c30d2c1919fa87aa182f7814e5cee597b71;hb=b6e676ce41508e2aeea22202fc8f234126177f52;hp=2316a6652952c8571559ef34df2bf34ec2bdcd4f;hpb=c93e5a62ff599528c3bf2a8656825403aaebe093;p=elogind.git
diff --git a/src/libsystemd-terminal/idev.c b/src/libsystemd-terminal/idev.c
index 2316a6652..44be7c30d 100644
--- a/src/libsystemd-terminal/idev.c
+++ b/src/libsystemd-terminal/idev.c
@@ -19,21 +19,16 @@
along with systemd; If not, see .
***/
-#include
#include
-#include
#include
#include
#include
#include
-#include
#include "hashmap.h"
#include "idev.h"
#include "idev-internal.h"
#include "login-shared.h"
#include "macro.h"
-#include "set.h"
-#include "udev-util.h"
#include "util.h"
static void element_open(idev_element *e);
@@ -274,6 +269,22 @@ static void element_disable(idev_element *e) {
}
}
+static void element_resume(idev_element *e, int fd) {
+ assert(e);
+ assert(fd >= 0);
+
+ if (e->vtable->resume)
+ e->vtable->resume(e, fd);
+}
+
+static void element_pause(idev_element *e, const char *mode) {
+ assert(e);
+ assert(mode);
+
+ if (e->vtable->pause)
+ e->vtable->pause(e, mode);
+}
+
/*
* Sessions
*/
@@ -335,8 +346,8 @@ static int session_add_device(idev_session *s, idev_device *d) {
error:
if (r < 0)
- log_debug("idev: %s: error while adding device '%s': %s",
- s->name, d->name, strerror(-r));
+ log_debug_errno(r, "idev: %s: error while adding device '%s': %m",
+ s->name, d->name);
return r;
}
@@ -356,8 +367,8 @@ static int session_remove_device(idev_session *s, idev_device *d) {
idev_device_disable(d);
if (error < 0)
- log_debug("idev: %s: error while removing device '%s': %s",
- s->name, d->name, strerror(-error));
+ log_debug_errno(error, "idev: %s: error while removing device '%s': %m",
+ s->name, d->name);
idev_device_free(d);
return error;
}
@@ -404,8 +415,8 @@ static int session_remove_element(idev_session *s, idev_element *e) {
element_disable(e);
if (error < 0)
- log_debug("idev: %s: error while removing element '%s': %s",
- s->name, e->name, strerror(-r));
+ log_debug_errno(r, "idev: %s: error while removing element '%s': %m",
+ s->name, e->name);
idev_element_free(e);
return error;
}
@@ -417,6 +428,98 @@ idev_session *idev_find_session(idev_context *c, const char *name) {
return hashmap_get(c->session_map, name);
}
+static int session_resume_device_fn(sd_bus *bus,
+ sd_bus_message *signal,
+ void *userdata,
+ sd_bus_error *ret_error) {
+ idev_session *s = userdata;
+ idev_element *e;
+ uint32_t major, minor;
+ int r, fd;
+
+ r = sd_bus_message_read(signal, "uuh", &major, &minor, &fd);
+ if (r < 0) {
+ log_debug("idev: %s: erroneous ResumeDevice signal", s->name);
+ return 0;
+ }
+
+ e = idev_find_evdev(s, makedev(major, minor));
+ if (!e)
+ return 0;
+
+ element_resume(e, fd);
+ return 0;
+}
+
+static int session_pause_device_fn(sd_bus *bus,
+ sd_bus_message *signal,
+ void *userdata,
+ sd_bus_error *ret_error) {
+ idev_session *s = userdata;
+ idev_element *e;
+ uint32_t major, minor;
+ const char *mode;
+ int r;
+
+ r = sd_bus_message_read(signal, "uus", &major, &minor, &mode);
+ if (r < 0) {
+ log_debug("idev: %s: erroneous PauseDevice signal", s->name);
+ return 0;
+ }
+
+ e = idev_find_evdev(s, makedev(major, minor));
+ if (!e)
+ return 0;
+
+ element_pause(e, mode);
+ return 0;
+}
+
+static int session_setup_bus(idev_session *s) {
+ _cleanup_free_ char *match = NULL;
+ int r;
+
+ if (!s->managed)
+ return 0;
+
+ match = strjoin("type='signal',"
+ "sender='org.freedesktop.login1',"
+ "interface='org.freedesktop.login1.Session',"
+ "member='ResumeDevice',"
+ "path='", s->path, "'",
+ NULL);
+ if (!match)
+ return -ENOMEM;
+
+ r = sd_bus_add_match(s->context->sysbus,
+ &s->slot_resume_device,
+ match,
+ session_resume_device_fn,
+ s);
+ if (r < 0)
+ return r;
+
+ free(match);
+ match = strjoin("type='signal',"
+ "sender='org.freedesktop.login1',"
+ "interface='org.freedesktop.login1.Session',"
+ "member='PauseDevice',"
+ "path='", s->path, "'",
+ NULL);
+ if (!match)
+ return -ENOMEM;
+
+ r = sd_bus_add_match(s->context->sysbus,
+ &s->slot_pause_device,
+ match,
+ session_pause_device_fn,
+ s);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
int idev_session_new(idev_session **out,
idev_context *c,
unsigned int flags,
@@ -454,14 +557,18 @@ int idev_session_new(idev_session **out,
return r;
}
- s->element_map = hashmap_new(string_hash_func, string_compare_func);
+ s->element_map = hashmap_new(&string_hash_ops);
if (!s->element_map)
return -ENOMEM;
- s->device_map = hashmap_new(string_hash_func, string_compare_func);
+ s->device_map = hashmap_new(&string_hash_ops);
if (!s->device_map)
return -ENOMEM;
+ r = session_setup_bus(s);
+ if (r < 0)
+ return r;
+
r = hashmap_put(c->session_map, s->name, s);
if (r < 0)
return r;
@@ -485,6 +592,8 @@ idev_session *idev_session_free(idev_session *s) {
if (s->name)
hashmap_remove_value(s->context->session_map, s->name, s);
+ s->slot_pause_device = sd_bus_slot_unref(s->slot_pause_device);
+ s->slot_resume_device = sd_bus_slot_unref(s->slot_resume_device);
s->context = idev_context_unref(s->context);
hashmap_free(s->device_map);
hashmap_free(s->element_map);
@@ -525,10 +634,40 @@ void idev_session_disable(idev_session *s) {
}
}
+static int add_link(idev_element *e, idev_device *d) {
+ idev_link *l;
+
+ assert(e);
+ assert(d);
+
+ l = new0(idev_link, 1);
+ if (!l)
+ return -ENOMEM;
+
+ l->element = e;
+ l->device = d;
+ LIST_PREPEND(links_by_element, e->links, l);
+ LIST_PREPEND(links_by_device, d->links, l);
+ device_attach(d, l);
+
+ return 0;
+}
+
+static int guess_type(struct udev_device *d) {
+ const char *id_key;
+
+ id_key = udev_device_get_property_value(d, "ID_INPUT_KEY");
+ if (streq_ptr(id_key, "1"))
+ return IDEV_DEVICE_KEYBOARD;
+
+ return IDEV_DEVICE_CNT;
+}
+
int idev_session_add_evdev(idev_session *s, struct udev_device *ud) {
idev_element *e;
+ idev_device *d;
dev_t devnum;
- int r;
+ int r, type;
assert_return(s, -EINVAL);
assert_return(ud, -EINVAL);
@@ -549,7 +688,34 @@ int idev_session_add_evdev(idev_session *s, struct udev_device *ud) {
if (r != 0)
return r;
- return 0;
+ type = guess_type(ud);
+ if (type < 0)
+ return type;
+
+ switch (type) {
+ case IDEV_DEVICE_KEYBOARD:
+ d = idev_find_keyboard(s, e->name);
+ if (d) {
+ log_debug("idev: %s: keyboard for new evdev element '%s' already available",
+ s->name, e->name);
+ return 0;
+ }
+
+ r = idev_keyboard_new(&d, s, e->name);
+ if (r < 0)
+ return r;
+
+ r = add_link(e, d);
+ if (r < 0) {
+ idev_device_free(d);
+ return r;
+ }
+
+ return session_add_device(s, d);
+ default:
+ /* unknown elements are silently ignored */
+ return 0;
+ }
}
int idev_session_remove_evdev(idev_session *s, struct udev_device *ud) {
@@ -590,11 +756,11 @@ int idev_context_new(idev_context **out, sd_event *event, sd_bus *sysbus) {
if (sysbus)
c->sysbus = sd_bus_ref(sysbus);
- c->session_map = hashmap_new(string_hash_func, string_compare_func);
+ c->session_map = hashmap_new(&string_hash_ops);
if (!c->session_map)
return -ENOMEM;
- c->data_map = hashmap_new(string_hash_func, string_compare_func);
+ c->data_map = hashmap_new(&string_hash_ops);
if (!c->data_map)
return -ENOMEM;