Support to pass arbitrary options via HAL hotplugging

Parse "input.x11_options" and pass every key/name pair to the driver.
Remove check for input.capabilities, because that's part of the fdi files.

Thanks to Dustin Spicuzza <dustin@virtualroadside.com> for the patch.
This commit is contained in:
Sascha Hlusiak 2008-03-25 17:37:25 +01:00
parent f028e245a7
commit 47eb658e80
3 changed files with 173 additions and 85 deletions

View File

@ -38,9 +38,10 @@
#include "config-backends.h" #include "config-backends.h"
#include "os.h" #include "os.h"
#define TYPE_NONE 0
#define TYPE_KEYS 1 #define LIBHAL_PROP_KEY "input.x11_options."
#define TYPE_POINTER 2 #define LIBHAL_XKB_PROP_KEY "input.xkb."
struct config_hal_info { struct config_hal_info {
DBusConnection *system_bus; DBusConnection *system_bus;
@ -50,7 +51,8 @@ struct config_hal_info {
static void static void
remove_device(DeviceIntPtr dev) remove_device(DeviceIntPtr dev)
{ {
DebugF("[config/hal] removing device %s\n", dev->name); /* 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 /* Call PIE here so we don't try to dereference a device that's
* already been removed. */ * already been removed. */
@ -105,7 +107,7 @@ get_prop_string(LibHalContext *hal_ctx, const char *udi, const char *name)
char *prop, *ret; char *prop, *ret;
prop = libhal_device_get_property_string(hal_ctx, udi, name, NULL); prop = libhal_device_get_property_string(hal_ctx, udi, name, NULL);
DebugF("[config/hal] getting %s on %s returned %s\n", name, udi, prop); LogMessageVerb(X_INFO, 10, "config/hal: getting %s on %s returned %s\n", name, udi, prop);
if (prop) { if (prop) {
ret = xstrdup(prop); ret = xstrdup(prop);
libhal_free_string(prop); libhal_free_string(prop);
@ -117,6 +119,9 @@ get_prop_string(LibHalContext *hal_ctx, const char *udi, const char *name)
return ret; return ret;
} }
/* this function is no longer used... keep it here in case its needed in
* the future. */
#if 0
static char * static char *
get_prop_string_array(LibHalContext *hal_ctx, const char *udi, const char *prop) get_prop_string_array(LibHalContext *hal_ctx, const char *udi, const char *prop)
{ {
@ -150,117 +155,146 @@ get_prop_string_array(LibHalContext *hal_ctx, const char *udi, const char *prop)
return ret; return ret;
} }
#endif
static void static void
device_added(LibHalContext *hal_ctx, const char *udi) device_added(LibHalContext *hal_ctx, const char *udi)
{ {
char **props; char *path = NULL, *driver = NULL, *name = NULL, *config_info = NULL;
char *path = NULL, *driver = NULL, *name = NULL, *xkb_rules = NULL;
char *xkb_model = NULL, *xkb_layout = NULL, *xkb_variant = NULL;
char *xkb_options = NULL, *config_info = NULL;
InputOption *options = NULL, *tmpo = NULL; InputOption *options = NULL, *tmpo = NULL;
DeviceIntPtr dev; DeviceIntPtr dev;
DBusError error; DBusError error;
int type = TYPE_NONE;
int i; LibHalPropertySet *set = NULL;
LibHalPropertySetIterator set_iter;
char *psi_key = NULL, *tmp_val, *tmp_key;
dbus_error_init(&error); dbus_error_init(&error);
props = libhal_device_get_property_strlist(hal_ctx, udi,
"info.capabilities", &error);
if (!props) {
DebugF("[config/hal] couldn't get capabilities for %s: %s (%s)\n",
udi, error.name, error.message);
goto out_error;
}
for (i = 0; props[i]; i++) {
/* input.keys is the new, of which input.keyboard is a subset, but
* input.keyboard is the old 'we have keys', so we have to keep it
* around. */
if (strcmp(props[i], "input.keys") == 0 ||
strcmp(props[i], "input.keyboard") == 0)
type |= TYPE_KEYS;
if (strcmp(props[i], "input.mouse") == 0 ||
strcmp(props[i], "input.touchpad") == 0)
type |= TYPE_POINTER;
}
libhal_free_string_array(props);
if (type == TYPE_NONE)
goto out_error;
driver = get_prop_string(hal_ctx, udi, "input.x11_driver"); driver = get_prop_string(hal_ctx, udi, "input.x11_driver");
path = get_prop_string(hal_ctx, udi, "input.device"); if (!driver){
if (!driver || !path) { /* verbose, don't tell the user unless they _want_ to see it */
DebugF("[config/hal] no driver or path specified for %s\n", udi); LogMessageVerb(X_INFO,7,"config/hal: no driver specified for device %s\n", udi);
goto unwind; goto unwind;
} }
path = get_prop_string(hal_ctx, udi, "input.device");
if (!path) {
LogMessage(X_WARNING,"config/hal: no driver or path specified for %s\n", udi);
goto unwind;
}
name = get_prop_string(hal_ctx, udi, "info.product"); name = get_prop_string(hal_ctx, udi, "info.product");
if (!name) if (!name)
name = xstrdup("(unnamed)"); name = xstrdup("(unnamed)");
if (type & TYPE_KEYS) {
xkb_rules = get_prop_string(hal_ctx, udi, "input.xkb.rules");
xkb_model = get_prop_string(hal_ctx, udi, "input.xkb.model");
xkb_layout = get_prop_string(hal_ctx, udi, "input.xkb.layout");
xkb_variant = get_prop_string(hal_ctx, udi, "input.xkb.variant");
xkb_options = get_prop_string_array(hal_ctx, udi, "input.xkb.options");
}
options = xcalloc(sizeof(*options), 1); options = xcalloc(sizeof(*options), 1);
if (!options){
LogMessage(X_ERROR, "config/hal: couldn't allocate space for input options!\n");
goto unwind;
}
options->key = xstrdup("_source"); options->key = xstrdup("_source");
options->value = xstrdup("server/hal"); options->value = xstrdup("server/hal");
if (!options->key || !options->value) { if (!options->key || !options->value) {
ErrorF("[config] couldn't allocate first key/value pair\n"); LogMessage(X_ERROR, "config/hal: couldn't allocate first key/value pair\n");
goto unwind; goto unwind;
} }
/* most drivers use device.. not path. evdev uses both however, but the
* path version isn't documented apparently. support both for now. */
add_option(&options, "path", path); add_option(&options, "path", path);
add_option(&options, "device", path);
add_option(&options, "driver", driver); add_option(&options, "driver", driver);
add_option(&options, "name", name); add_option(&options, "name", name);
config_info = xalloc(strlen(udi) + 5); /* "hal:" and NULL */ config_info = xalloc(strlen(udi) + 5); /* "hal:" and NULL */
if (!config_info) if (!config_info) {
LogMessage(X_ERROR, "config/hal: couldn't allocate name\n");
goto unwind; goto unwind;
}
sprintf(config_info, "hal:%s", udi); sprintf(config_info, "hal:%s", udi);
if (xkb_rules) /* ok, grab options from hal.. iterate through all properties
add_option(&options, "xkb_rules", xkb_rules); * and lets see if any of them are options that we can add */
if (xkb_model) set = libhal_device_get_all_properties(hal_ctx, udi, &error);
add_option(&options, "xkb_model", xkb_model);
if (xkb_layout) if (!set) {
add_option(&options, "xkb_layout", xkb_layout); LogMessage(X_ERROR, "config/hal: couldn't get property list for %s: %s (%s)\n",
if (xkb_variant) udi, error.name, error.message);
add_option(&options, "xkb_variant", xkb_variant); goto unwind;
if (xkb_options) }
add_option(&options, "xkb_options", xkb_options);
libhal_psi_init(&set_iter,set);
while (libhal_psi_has_more(&set_iter)) {
/* we are looking for supported keys.. extract and add to options */
psi_key = libhal_psi_get_key(&set_iter);
if (psi_key){
DebugF("[config/hal] Adding device %s\n", name); /* normal options first (input.x11_options.<propname>) */
if (!strncasecmp(psi_key, LIBHAL_PROP_KEY, sizeof(LIBHAL_PROP_KEY)-1)){
/* only support strings for all values */
tmp_val = get_prop_string(hal_ctx, udi, psi_key);
if (tmp_val){
add_option(&options, psi_key + sizeof(LIBHAL_PROP_KEY)-1, tmp_val);
xfree(tmp_val);
}
/* evdev's XKB options... we should probably depreciate this usage */
} else if (!strncasecmp(psi_key, LIBHAL_XKB_PROP_KEY, sizeof(LIBHAL_XKB_PROP_KEY)-1)){
/* only support strings for all values */
tmp_val = get_prop_string(hal_ctx, udi, psi_key);
if (tmp_val){
/* add "xkb_" + NULL */
tmp_key = xalloc(strlen(psi_key) - ( sizeof(LIBHAL_XKB_PROP_KEY) - 1) + 5);
if (!tmp_key){
LogMessage(X_ERROR, "config/hal: couldn't allocate memory for option %s\n", psi_key);
} else {
sprintf(tmp_key, "xkb_%s", psi_key + sizeof(LIBHAL_XKB_PROP_KEY)-1);
add_option(&options, tmp_key, tmp_val);
xfree(tmp_key);
}
xfree(tmp_val);
}
}
}
/* psi_key doesn't need to be freed */
libhal_psi_next(&set_iter);
}
/* 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 (NewInputDeviceRequest(options, &dev) != Success) { if (NewInputDeviceRequest(options, &dev) != Success) {
ErrorF("[config/hal] NewInputDeviceRequest failed\n"); LogMessage(X_ERROR, "config/hal: NewInputDeviceRequest failed\n");
dev = NULL; dev = NULL;
goto unwind; goto unwind;
} }
for (; dev; dev = dev->next) for (; dev; dev = dev->next){
if (dev->config_info)
xfree(dev->config_info);
dev->config_info = xstrdup(config_info); dev->config_info = xstrdup(config_info);
}
unwind: unwind:
if (set)
libhal_free_property_set(set);
if (path) if (path)
xfree(path); xfree(path);
if (driver) if (driver)
xfree(driver); xfree(driver);
if (name) if (name)
xfree(name); xfree(name);
if (xkb_rules)
xfree(xkb_rules);
if (xkb_model)
xfree(xkb_model);
if (xkb_layout)
xfree(xkb_layout);
if (xkb_variant)
xfree(xkb_variant);
if (xkb_options)
xfree(xkb_options);
if (config_info) if (config_info)
xfree(config_info); xfree(config_info);
while (!dev && (tmpo = options)) { while (!dev && (tmpo = options)) {
@ -270,7 +304,6 @@ unwind:
xfree(tmpo); xfree(tmpo);
} }
out_error:
dbus_error_free(&error); dbus_error_free(&error);
return; return;
@ -286,7 +319,7 @@ disconnect_hook(void *data)
if (dbus_connection_get_is_connected(info->system_bus)) { if (dbus_connection_get_is_connected(info->system_bus)) {
dbus_error_init(&error); dbus_error_init(&error);
if (!libhal_ctx_shutdown(info->hal_ctx, &error)) if (!libhal_ctx_shutdown(info->hal_ctx, &error))
DebugF("[config/hal] couldn't shut down context: %s (%s)\n", LogMessage(X_WARNING, "config/hal: disconnect_hook couldn't shut down context: %s (%s)\n",
error.name, error.message); error.name, error.message);
dbus_error_free(&error); dbus_error_free(&error);
} }
@ -312,21 +345,21 @@ connect_hook(DBusConnection *connection, void *data)
if (!info->hal_ctx) if (!info->hal_ctx)
info->hal_ctx = libhal_ctx_new(); info->hal_ctx = libhal_ctx_new();
if (!info->hal_ctx) { if (!info->hal_ctx) {
ErrorF("[config/hal] couldn't create HAL context\n"); LogMessage(X_ERROR, "config/hal: couldn't create HAL context\n");
goto out_err; goto out_err;
} }
if (!libhal_ctx_set_dbus_connection(info->hal_ctx, info->system_bus)) { if (!libhal_ctx_set_dbus_connection(info->hal_ctx, info->system_bus)) {
ErrorF("[config/hal] couldn't associate HAL context with bus\n"); LogMessage(X_ERROR, "config/hal: couldn't associate HAL context with bus\n");
goto out_ctx; goto out_ctx;
} }
if (!libhal_ctx_init(info->hal_ctx, &error)) { if (!libhal_ctx_init(info->hal_ctx, &error)) {
ErrorF("[config/hal] couldn't initialise context: %s (%s)\n", LogMessage(X_ERROR, "config/hal: couldn't initialise context: %s (%s)\n",
error.name, error.message); error.name, error.message);
goto out_ctx; goto out_ctx;
} }
if (!libhal_device_property_watch_all(info->hal_ctx, &error)) { if (!libhal_device_property_watch_all(info->hal_ctx, &error)) {
ErrorF("[config/hal] couldn't watch all properties: %s (%s)\n", LogMessage(X_ERROR, "config/hal: couldn't watch all properties: %s (%s)\n",
error.name, error.message); error.name, error.message);
goto out_ctx2; goto out_ctx2;
} }
@ -346,7 +379,7 @@ connect_hook(DBusConnection *connection, void *data)
out_ctx2: out_ctx2:
if (!libhal_ctx_shutdown(info->hal_ctx, &error)) if (!libhal_ctx_shutdown(info->hal_ctx, &error))
DebugF("[config/hal] couldn't shut down context: %s (%s)\n", LogMessage(X_WARNING, "config/hal: couldn't shut down context: %s (%s)\n",
error.name, error.message); error.name, error.message);
out_ctx: out_ctx:
libhal_ctx_free(info->hal_ctx); libhal_ctx_free(info->hal_ctx);
@ -374,10 +407,13 @@ config_hal_init(void)
hal_info.hal_ctx = NULL; hal_info.hal_ctx = NULL;
if (!config_dbus_core_add_hook(&hook)) { if (!config_dbus_core_add_hook(&hook)) {
ErrorF("[config/hal] failed to add D-Bus hook\n"); LogMessage(X_ERROR, "config/hal: failed to add D-Bus hook\n");
return 0; return 0;
} }
/* verbose message */
LogMessageVerb(X_INFO,7,"config/hal: initialized");
return 1; return 1;
} }

View File

@ -1,7 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<deviceinfo version="0.2"> <deviceinfo version="0.2">
<device> <device>
<!-- FIXME: Support tablets too. -->
<!-- The way this works:
Match against some input device (see the HAL specification for more
information), and then merge in keys, which you can use to specify
the configuration similar to the way you would in xorg.conf. You will
need to restart HAL after making changes. If you are having issues,
starting X with the -logverbose 7 flag may yield useful information.
Keys Supported:
Key "input.x11_driver" (string)
This specifies the driver to use. You MUST specify this option,
or a driver will not be loaded and the rest will be ignored by
Xorg
Key "input.x11_options.<option name>" (string)
This allows you to specify arbitrary options to pass to the driver.
Anything you would normally specify in xorg.conf goes here. So, for
option "Mode" in xorg.conf, you would specify the key name of
"input.x11_options.Mode".
Do not specify "input.x11_options.Device" since "input.device"
will be used automatically.
Legacy Keys
"input.xkb.rules"
"input.xkb.model"
"input.xkb.layout"
"input.xkb.variant"
"input.xkb.options"
These keys are deprecated. Use these instead:
"input.x11_options.XkbRules"
"input.x11_options.XkbModel"
"input.x11_options.XkbLayout"
"input.x11_options.XkbVariant"
"input.x11_options.XkbOptions"
See the evdev documentation for more information.
You will probably want to add the following option to the ServerFlags of
your xorg.conf:
Option "AllowEmptyInput" "True"
FIXME: Support tablets too.
TODO: I think its fixed, can't test
-->
<match key="info.capabilities" contains="input.mouse"> <match key="info.capabilities" contains="input.mouse">
<merge key="input.x11_driver" type="string">mouse</merge> <merge key="input.x11_driver" type="string">mouse</merge>
<match key="/org/freedesktop/Hal/devices/computer:system.kernel.name" <match key="/org/freedesktop/Hal/devices/computer:system.kernel.name"
@ -11,21 +61,21 @@
</match> </match>
<match key="info.capabilities" contains="input.keys"> <match key="info.capabilities" contains="input.keys">
<merge key="input.xkb.rules" type="string">base</merge> <merge key="input.x11_options.XkbRules" type="string">base</merge>
<!-- If we're using Linux, we use evdev by default (falling back to <!-- If we're using Linux, we use evdev by default (falling back to
keyboard otherwise). --> keyboard otherwise). -->
<merge key="input.x11_driver" type="string">keyboard</merge> <merge key="input.x11_driver" type="string">keyboard</merge>
<merge key="input.xkb.model" type="string">pc105</merge> <merge key="input.x11_options.XkbModel" type="string">pc105</merge>
<match key="/org/freedesktop/Hal/devices/computer:system.kernel.name" <match key="/org/freedesktop/Hal/devices/computer:system.kernel.name"
string="Linux"> string="Linux">
<merge key="input.x11_driver" type="string">evdev</merge> <merge key="input.x11_driver" type="string">evdev</merge>
<merge key="input.xkb.model" type="string">evdev</merge> <merge key="input.x11_options.XkbModel" type="string">evdev</merge>
</match> </match>
<merge key="input.xkb.layout" type="string">us</merge> <merge key="input.x11_options.XkbLayout" type="string">us</merge>
<merge key="input.xkb.variant" type="string" /> <merge key="input.x11_options.XkbVariant" type="string" />
</match> </match>
</device> </device>
</deviceinfo> </deviceinfo>

View File

@ -1009,6 +1009,8 @@ XKB_STUB_LIB='$(top_builddir)/xkb/libxkbstubs.la'
AC_CHECK_FUNC(strcasecmp, [], AC_DEFINE([NEED_STRCASECMP], 1, AC_CHECK_FUNC(strcasecmp, [], AC_DEFINE([NEED_STRCASECMP], 1,
[Do not have 'strcasecmp'.])) [Do not have 'strcasecmp'.]))
AC_CHECK_FUNC(strncasecmp, [], AC_DEFINE([NEED_STRNCASECMP], 1,
[Do not have 'strncasecmp'.]))
if test "x$NULL_ROOT_CURSOR" = xyes; then if test "x$NULL_ROOT_CURSOR" = xyes; then
AC_DEFINE(NULL_ROOT_CURSOR, 1, [Use an empty root cursor]) AC_DEFINE(NULL_ROOT_CURSOR, 1, [Use an empty root cursor])