diff options
Diffstat (limited to 'xorg-server/config')
-rw-r--r-- | xorg-server/config/Makefile.am | 20 | ||||
-rw-r--r-- | xorg-server/config/config-backends.h | 21 | ||||
-rw-r--r-- | xorg-server/config/config.c | 77 | ||||
-rw-r--r-- | xorg-server/config/dbus-api | 40 | ||||
-rw-r--r-- | xorg-server/config/dbus.c | 2 | ||||
-rw-r--r-- | xorg-server/config/hal.c | 88 | ||||
-rw-r--r-- | xorg-server/config/udev.c | 260 |
7 files changed, 434 insertions, 74 deletions
diff --git a/xorg-server/config/Makefile.am b/xorg-server/config/Makefile.am index 7fa2df877..27f251b3a 100644 --- a/xorg-server/config/Makefile.am +++ b/xorg-server/config/Makefile.am @@ -1,12 +1,20 @@ -AM_CFLAGS = @DIX_CFLAGS@ +AM_CFLAGS = $(DIX_CFLAGS) noinst_LTLIBRARIES = libconfig.la libconfig_la_SOURCES = config.c config-backends.h +if CONFIG_UDEV + +AM_CFLAGS += $(UDEV_CFLAGS) +libconfig_la_SOURCES += udev.c +libconfig_la_LIBADD = $(UDEV_LIBS) + +else + if CONFIG_NEED_DBUS -AM_CFLAGS += @DBUS_CFLAGS@ +AM_CFLAGS += $(DBUS_CFLAGS) libconfig_la_SOURCES += dbus-core.c -endif +libconfig_la_LIBADD = $(DBUS_LIBS) if CONFIG_DBUS_API dbusconfigdir = $(sysconfdir)/dbus-1/system.d @@ -16,7 +24,13 @@ libconfig_la_SOURCES += dbus.c endif if CONFIG_HAL +AM_CFLAGS += $(HAL_CFLAGS) libconfig_la_SOURCES += hal.c +libconfig_la_LIBADD += $(HAL_LIBS) endif +endif # CONFIG_NEED_DBUS + +endif # !CONFIG_UDEV + EXTRA_DIST = xorg-server.conf x11-input.fdi diff --git a/xorg-server/config/config-backends.h b/xorg-server/config/config-backends.h index 907e86b9c..0a2a22af0 100644 --- a/xorg-server/config/config-backends.h +++ b/xorg-server/config/config-backends.h @@ -26,8 +26,18 @@ #ifdef HAVE_DIX_CONFIG_H #include <dix-config.h> #endif +#include "input.h" -#ifdef CONFIG_NEED_DBUS +void remove_devices(const char *backend, const char *config_info); +BOOL device_is_duplicate(const char *config_info); +void add_option(InputOption **options, const char *key, const char *value); + +#ifdef CONFIG_UDEV +int config_udev_init(void); +void config_udev_fini(void); +#else + +# ifdef CONFIG_NEED_DBUS #include <dbus/dbus.h> typedef void (*config_dbus_core_connect_hook)(DBusConnection *connection, @@ -46,14 +56,15 @@ int config_dbus_core_init(void); void config_dbus_core_fini(void); int config_dbus_core_add_hook(struct config_dbus_core_hook *hook); void config_dbus_core_remove_hook(struct config_dbus_core_hook *hook); -#endif +# endif -#ifdef CONFIG_DBUS_API +# ifdef CONFIG_DBUS_API int config_dbus_init(void); void config_dbus_fini(void); -#endif +# endif -#ifdef CONFIG_HAL +# ifdef CONFIG_HAL int config_hal_init(void); void config_hal_fini(void); +# endif #endif diff --git a/xorg-server/config/config.c b/xorg-server/config/config.c index b01329339..7bf5e4179 100644 --- a/xorg-server/config/config.c +++ b/xorg-server/config/config.c @@ -28,13 +28,17 @@ #endif #include "os.h" +#include "inputstr.h" #include "hotplug.h" #include "config-backends.h" void config_init(void) { -#if defined(CONFIG_DBUS_API) || defined(CONFIG_HAL) +#ifdef CONFIG_UDEV + if (!config_udev_init()) + ErrorF("[config] failed to initialise udev\n"); +#elif defined(CONFIG_NEED_DBUS) if (config_dbus_core_init()) { # ifdef CONFIG_DBUS_API if (!config_dbus_init()) @@ -54,7 +58,9 @@ config_init(void) void config_fini(void) { -#if defined(CONFIG_DBUS_API) || defined(CONFIG_HAL) +#if defined(CONFIG_UDEV) + config_udev_fini(); +#elif defined(CONFIG_NEED_DBUS) # ifdef CONFIG_HAL config_hal_fini(); # endif @@ -64,3 +70,70 @@ config_fini(void) config_dbus_core_fini(); #endif } + +static void +remove_device(const char *backend, DeviceIntPtr dev) +{ + /* this only gets called for devices that have already been added */ + LogMessage(X_INFO, "config/%s: removing device %s\n", backend, dev->name); + + /* Call PIE here so we don't try to dereference a device that's + * already been removed. */ + OsBlockSignals(); + ProcessInputEvents(); + DeleteInputDeviceRequest(dev); + OsReleaseSignals(); +} + +void +remove_devices(const char *backend, const char *config_info) +{ + DeviceIntPtr dev, next; + + for (dev = inputInfo.devices; dev; dev = next) { + next = dev->next; + if (dev->config_info && strcmp(dev->config_info, config_info) == 0) + remove_device(backend, dev); + } + for (dev = inputInfo.off_devices; dev; dev = next) { + next = dev->next; + if (dev->config_info && strcmp(dev->config_info, config_info) == 0) + remove_device(backend, dev); + } +} + +BOOL +device_is_duplicate(const char *config_info) +{ + DeviceIntPtr dev; + + for (dev = inputInfo.devices; dev; dev = dev->next) + { + if (dev->config_info && (strcmp(dev->config_info, config_info) == 0)) + return TRUE; + } + + for (dev = inputInfo.off_devices; dev; dev = dev->next) + { + if (dev->config_info && (strcmp(dev->config_info, config_info) == 0)) + return TRUE; + } + + return FALSE; +} + +void +add_option(InputOption **options, const char *key, const char *value) +{ + if (!value || *value == '\0') + return; + + for (; *options; options = &(*options)->next) + ; + *options = xcalloc(sizeof(**options), 1); + if (!*options) /* Yeesh. */ + return; + (*options)->key = xstrdup(key); + (*options)->value = xstrdup(value); + (*options)->next = NULL; +} diff --git a/xorg-server/config/dbus-api b/xorg-server/config/dbus-api new file mode 100644 index 000000000..018e98657 --- /dev/null +++ b/xorg-server/config/dbus-api @@ -0,0 +1,40 @@ +D-BUS Configuration API v2 +---------------------------- + +The X server will register the bus name org.x.config.displayN, and the +object /org/x/config/N, where N is the display number. + +Currently only hotplugging of input devices is supported. + +org.x.config.input: + org.x.config.input.version: + Returns one unsigned int32, which is the API version. + + org.x.config.input.add: + Takes an argument of key/value option pairs in arrays, e.g.: + [ss][ss][ss][ss] + is the signature for four options. These options will be passed + to the input driver as with any others. + Option names beginning with _ are not allowed; they are reserved + for internal use. + + Returns a number of signed int32s. Positive integers are the + device IDs of new devices; negative numbers are X error codes, + as defined in X.h. BadMatch will be returned if the options + given do not match any device. BadValue is returned for a malformed + message. (Example: 8 is new device ID 8; -8 is BadMatch.) + + Notably, BadAlloc is never returned: the server internally signals + to D-BUS that the attempt failed for lack of memory. + + org.x.config.input.remove: + Takes one uint32 argument, which is the device ID to remove, i.e.: + u + is the signature. + + Returns one signed int32 which represents an X status as defined in + X.h. See org.x.config.input.add. Error codes are negative numbers. + + org.x.config.input.listDevices: + Lists the currently active devices. No argument. + Return value is sequence of [<id> <name>] [<id> <name>] ..., i.e. [us]. diff --git a/xorg-server/config/dbus.c b/xorg-server/config/dbus.c index 37462ace0..86d9d287f 100644 --- a/xorg-server/config/dbus.c +++ b/xorg-server/config/dbus.c @@ -147,7 +147,7 @@ add_device(DBusMessage *message, DBusMessage *reply, DBusError *error) dbus_message_iter_next(&iter); } - ret = NewInputDeviceRequest(options, &dev); + ret = NewInputDeviceRequest(options, NULL, &dev); if (ret != Success) { DebugF("[config/dbus] NewInputDeviceRequest failed\n"); goto unwind; diff --git a/xorg-server/config/hal.c b/xorg-server/config/hal.c index 28f55a02f..1b01eccaa 100644 --- a/xorg-server/config/hal.c +++ b/xorg-server/config/hal.c @@ -58,25 +58,9 @@ struct xkb_options { char* options; }; - -static void -remove_device(DeviceIntPtr dev) -{ - /* this only gets called for devices that have already been added */ - LogMessage(X_INFO, "config/hal: removing device %s\n", dev->name); - - /* Call PIE here so we don't try to dereference a device that's - * already been removed. */ - OsBlockSignals(); - ProcessInputEvents(); - DeleteInputDeviceRequest(dev); - OsReleaseSignals(); -} - static void device_removed(LibHalContext *ctx, const char *udi) { - DeviceIntPtr dev, next; char *value; value = xalloc(strlen(udi) + 5); /* "hal:" + NULL */ @@ -84,36 +68,11 @@ device_removed(LibHalContext *ctx, const char *udi) return; sprintf(value, "hal:%s", udi); - for (dev = inputInfo.devices; dev; dev = next) { - next = dev->next; - if (dev->config_info && strcmp(dev->config_info, value) == 0) - remove_device(dev); - } - for (dev = inputInfo.off_devices; dev; dev = next) { - next = dev->next; - if (dev->config_info && strcmp(dev->config_info, value) == 0) - remove_device(dev); - } + remove_devices("hal", value); xfree(value); } -static void -add_option(InputOption **options, const char *key, const char *value) -{ - if (!value || *value == '\0') - return; - - for (; *options; options = &(*options)->next) - ; - *options = xcalloc(sizeof(**options), 1); - if (!*options) /* Yeesh. */ - return; - (*options)->key = xstrdup(key); - (*options)->value = xstrdup(value); - (*options)->next = NULL; -} - static char * get_prop_string(LibHalContext *hal_ctx, const char *udi, const char *name) { @@ -166,31 +125,12 @@ get_prop_string_array(LibHalContext *hal_ctx, const char *udi, const char *prop) return ret; } -static BOOL -device_is_duplicate(char *config_info) -{ - DeviceIntPtr dev; - - for (dev = inputInfo.devices; dev; dev = dev->next) - { - if (dev->config_info && (strcmp(dev->config_info, config_info) == 0)) - return TRUE; - } - - for (dev = inputInfo.off_devices; dev; dev = dev->next) - { - if (dev->config_info && (strcmp(dev->config_info, config_info) == 0)) - return TRUE; - } - - return FALSE; -} - static void device_added(LibHalContext *hal_ctx, const char *udi) { char *path = NULL, *driver = NULL, *name = NULL, *config_info = NULL; InputOption *options = NULL, *tmpo = NULL; + InputAttributes attrs = {0}; DeviceIntPtr dev = NULL; DBusError error; struct xkb_options xkb_opts = {0}; @@ -215,10 +155,28 @@ device_added(LibHalContext *hal_ctx, const char *udi) LogMessage(X_WARNING,"config/hal: no driver or path specified for %s\n", udi); goto unwind; } + attrs.device = xstrdup(path); name = get_prop_string(hal_ctx, udi, "info.product"); if (!name) name = xstrdup("(unnamed)"); + else + attrs.product = xstrdup(name); + + attrs.vendor = get_prop_string(hal_ctx, udi, "info.vendor"); + + if (libhal_device_query_capability(hal_ctx, udi, "input.keys", NULL)) + attrs.flags |= ATTR_KEYBOARD; + if (libhal_device_query_capability(hal_ctx, udi, "input.mouse", NULL)) + attrs.flags |= ATTR_POINTER; + if (libhal_device_query_capability(hal_ctx, udi, "input.joystick", NULL)) + attrs.flags |= ATTR_JOYSTICK; + if (libhal_device_query_capability(hal_ctx, udi, "input.tablet", NULL)) + attrs.flags |= ATTR_TABLET; + if (libhal_device_query_capability(hal_ctx, udi, "input.touchpad", NULL)) + attrs.flags |= ATTR_TOUCHPAD; + if (libhal_device_query_capability(hal_ctx, udi, "input.touchscreen", NULL)) + attrs.flags |= ATTR_TOUCHSCREEN; options = xcalloc(sizeof(*options), 1); if (!options){ @@ -400,7 +358,7 @@ device_added(LibHalContext *hal_ctx, const char *udi) /* this isn't an error, but how else do you output something that the user can see? */ LogMessage(X_INFO, "config/hal: Adding input device %s\n", name); - if ((rc = NewInputDeviceRequest(options, &dev)) != Success) { + if ((rc = NewInputDeviceRequest(options, &attrs, &dev)) != Success) { LogMessage(X_ERROR, "config/hal: NewInputDeviceRequest failed (%d)\n", rc); dev = NULL; goto unwind; @@ -430,6 +388,10 @@ unwind: xfree(tmpo); } + xfree(attrs.product); + xfree(attrs.vendor); + xfree(attrs.device); + if (xkb_opts.layout) xfree(xkb_opts.layout); if (xkb_opts.rules) diff --git a/xorg-server/config/udev.c b/xorg-server/config/udev.c new file mode 100644 index 000000000..3ef0d7fb3 --- /dev/null +++ b/xorg-server/config/udev.c @@ -0,0 +1,260 @@ +/* + * Copyright © 2009 Julien Cristau + * + * 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. + * + * Author: Julien Cristau <jcristau@debian.org> + */ + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif + +#include <libudev.h> + +#include "input.h" +#include "inputstr.h" +#include "hotplug.h" +#include "config-backends.h" +#include "os.h" + +#define UDEV_XKB_PROP_KEY "xkb" + +static struct udev_monitor *udev_monitor; + +static void +device_added(struct udev_device *udev_device) +{ + const char *path, *name = NULL; + char *config_info = NULL; + const char *syspath; + const char *key, *value, *tmp; + InputOption *options = NULL, *tmpo; + InputAttributes attrs = {}; + DeviceIntPtr dev = NULL; + struct udev_list_entry *set, *entry; + struct udev_device *parent; + int rc; + + path = udev_device_get_devnode(udev_device); + + syspath = udev_device_get_syspath(udev_device); + + if (!path || !syspath) + return; + + if (!udev_device_get_property_value(udev_device, "ID_INPUT")) + return; + + options = xcalloc(sizeof(*options), 1); + if (!options) + return; + + options->key = xstrdup("_source"); + options->value = xstrdup("server/udev"); + if (!options->key || !options->value) + goto unwind; + + parent = udev_device_get_parent(udev_device); + if (parent) + name = udev_device_get_property_value(parent, "NAME"); + if (!name) + name = "(unnamed)"; + else + attrs.product = name; + add_option(&options, "name", name); + + add_option(&options, "path", path); + add_option(&options, "device", path); + attrs.device = path; + + config_info = Xprintf("udev:%s", syspath); + if (!config_info) + goto unwind; + + if (device_is_duplicate(config_info)) { + LogMessage(X_WARNING, "config/udev: device %s already added. " + "Ignoring.\n", name); + goto unwind; + } + + set = udev_device_get_properties_list_entry(udev_device); + udev_list_entry_foreach(entry, set) { + key = udev_list_entry_get_name(entry); + if (!key) + continue; + value = udev_list_entry_get_value(entry); + if (!strncasecmp(key, UDEV_XKB_PROP_KEY, + sizeof(UDEV_XKB_PROP_KEY) - 1)) { + tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1; + if (!strcasecmp(tmp, "rules")) + add_option(&options, "xkb_rules", value); + else if (!strcasecmp(tmp, "layout")) + add_option(&options, "xkb_layout", value); + else if (!strcasecmp(tmp, "variant")) + add_option(&options, "xkb_variant", value); + else if (!strcasecmp(tmp, "model")) + add_option(&options, "xkb_model", value); + else if (!strcasecmp(tmp, "options")) + add_option(&options, "xkb_options", value); + } else if (!strcmp(key, "ID_VENDOR")) { + attrs.vendor = value; + } else if (!strcmp(key, "ID_INPUT_KEY")) { + attrs.flags |= ATTR_KEYBOARD; + } else if (!strcmp(key, "ID_INPUT_MOUSE")) { + attrs.flags |= ATTR_POINTER; + } else if (!strcmp(key, "ID_INPUT_JOYSTICK")) { + attrs.flags |= ATTR_JOYSTICK; + } else if (!strcmp(key, "ID_INPUT_TABLET")) { + attrs.flags |= ATTR_TABLET; + } else if (!strcmp(key, "ID_INPUT_TOUCHPAD")) { + attrs.flags |= ATTR_TOUCHPAD; + } else if (!strcmp(key, "ID_INPUT_TOUCHSCREEN")) { + attrs.flags |= ATTR_TOUCHSCREEN; + } + } + LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n", + name, path); + rc = NewInputDeviceRequest(options, &attrs, &dev); + if (rc != Success) + goto unwind; + + for (; dev; dev = dev->next) { + xfree(dev->config_info); + dev->config_info = xstrdup(config_info); + } + + unwind: + xfree(config_info); + while (!dev && (tmpo = options)) { + options = tmpo->next; + xfree(tmpo->key); + xfree(tmpo->value); + xfree(tmpo); + } + + return; +} + +static void +device_removed(struct udev_device *device) +{ + char *value; + const char *syspath = udev_device_get_syspath(device); + + value = Xprintf("udev:%s", syspath); + if (!value) + return; + + remove_devices("udev", value); + + xfree(value); +} + +static void +wakeup_handler(pointer data, int err, pointer read_mask) +{ + int udev_fd = udev_monitor_get_fd(udev_monitor); + struct udev_device *udev_device; + const char *action; + + if (err < 0) + return; + + if (FD_ISSET(udev_fd, (fd_set *)read_mask)) { + udev_device = udev_monitor_receive_device(udev_monitor); + if (!udev_device) + return; + action = udev_device_get_action(udev_device); + if (action) { + if (!strcmp(action, "add")) + device_added(udev_device); + else if (!strcmp(action, "remove")) + device_removed(udev_device); + } + udev_device_unref(udev_device); + } +} + +static void +block_handler(pointer data, struct timeval **tv, pointer read_mask) +{ +} + +int +config_udev_init(void) +{ + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *device; + int rc; + + udev = udev_new(); + if (!udev) + return 0; + udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (!udev_monitor) + return 0; + rc = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, + "input", NULL); + if (rc < 0) + return 0; + + if (udev_monitor_enable_receiving(udev_monitor)) { + ErrorF("config/udev: failed to bind the udev monitor\n"); + return 0; + } + + enumerate = udev_enumerate_new(udev); + if (!enumerate) + return 0; + udev_enumerate_add_match_subsystem(enumerate, "input"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + udev_list_entry_foreach(device, devices) { + const char *syspath = udev_list_entry_get_name(device); + struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath); + device_added(udev_device); + udev_device_unref(udev_device); + } + udev_enumerate_unref(enumerate); + + RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); + AddGeneralSocket(udev_monitor_get_fd(udev_monitor)); + + return 1; +} + +void +config_udev_fini(void) +{ + struct udev *udev; + + if (!udev_monitor) + return; + + udev = udev_monitor_get_udev(udev_monitor); + + RemoveGeneralSocket(udev_monitor_get_fd(udev_monitor)); + RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, udev_monitor); + udev_monitor_unref(udev_monitor); + udev_monitor = NULL; + udev_unref(udev); +} |