>From 64572393c03e475833b8fc0421beffe65fe5cd1f Mon Sep 17 00:00:00 2001
From: Dustin Spicuzza <dustin@virtualroadside.com>
Date: Sat, 15 Mar 2008 17:29:13 -0400
Subject: [PATCH] Added support to pass arbitrary options via HAL hotplugging

The HAL hotplugging still needs some cleanup in regards to messages being transmitted to the user, I've changed it to be more verbose, but a happy
medium needs to be reached. Also modified the FDI file to give it some documentation, since the current documentation is pretty much non-existant.
Need a better place to document it (perhaps in xorg.conf man page)?
---
 config/hal.c         |   80 ++++++++++++++++++++++++++++++++++++++++----------
 config/x11-input.fdi |   44 +++++++++++++++++++++++++++-
 2 files changed, 107 insertions(+), 17 deletions(-)

diff --git a/config/hal.c b/config/hal.c
index 1575422..4044bc9 100644
--- a/config/hal.c
+++ b/config/hal.c
@@ -42,6 +42,8 @@
 #define TYPE_KEYS 1
 #define TYPE_POINTER 2
 
+#define LIBHAL_PROP_KEY "input.x11_options."
+
 struct config_hal_info {
     DBusConnection *system_bus;
     LibHalContext *hal_ctx;
@@ -50,7 +52,8 @@ struct config_hal_info {
 static void
 remove_device(DeviceIntPtr dev)
 {
-    DebugF("[config/hal] removing device %s\n", dev->name);
+    /* this isn't an error, but the user should see this */
+    ErrorF("[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. */
@@ -154,37 +157,42 @@ get_prop_string_array(LibHalContext *hal_ctx, const char *udi, const char *prop)
 static void
 device_added(LibHalContext *hal_ctx, const char *udi)
 {
-    char **props;
+    char **capabilities;
     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;
     DeviceIntPtr dev;
     DBusError error;
+	
+	LibHalPropertySet *set = NULL;
+    char *psi_key = NULL, *tmp_val;
+	LibHalPropertySetIterator set_iter;
+	
     int type = TYPE_NONE;
     int i;
 
     dbus_error_init(&error);
 
-    props = libhal_device_get_property_strlist(hal_ctx, udi,
+    capabilities = 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",
+    if (!capabilities) {
+        ErrorF("[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++) {
+    for (i = 0; capabilities[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)
+        if (strcmp(capabilities[i], "input.keys") == 0 ||
+            strcmp(capabilities[i], "input.keyboard") == 0)
             type |= TYPE_KEYS;
-        if (strcmp(props[i], "input.mouse") == 0 ||
-            strcmp(props[i], "input.touchpad") == 0)
+        if (strcmp(capabilities[i], "input.mouse") == 0 ||
+            strcmp(capabilities[i], "input.touchpad") == 0)
             type |= TYPE_POINTER;
     }
-    libhal_free_string_array(props);
+    libhal_free_string_array(capabilities);
 
     if (type == TYPE_NONE)
         goto out_error;
@@ -192,7 +200,7 @@ device_added(LibHalContext *hal_ctx, const char *udi)
     driver = get_prop_string(hal_ctx, udi, "input.x11_driver");
     path = get_prop_string(hal_ctx, udi, "input.device");
     if (!driver || !path) {
-        DebugF("[config/hal] no driver or path specified for %s\n", udi);
+        ErrorF("[config/hal] no driver or path specified for %s\n", udi);
         goto unwind;
     }
     name = get_prop_string(hal_ctx, udi, "info.product");
@@ -205,17 +213,22 @@ device_added(LibHalContext *hal_ctx, const char *udi)
         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->key = xstrdup("_source");
     options->value = xstrdup("server/hal");
     if (!options->key || !options->value) {
-        ErrorF("[config] couldn't allocate first key/value pair\n");
+        ErrorF("[config/hal] couldn't allocate first key/value pair\n");
         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, "device", path);
+    
     add_option(&options, "driver", driver);
     add_option(&options, "name", name);
     config_info = xalloc(strlen(udi) + 5); /* "hal:" and NULL */
@@ -233,8 +246,41 @@ device_added(LibHalContext *hal_ctx, const char *udi)
         add_option(&options, "xkb_variant", xkb_variant);
     if (xkb_options)
         add_option(&options, "xkb_options", xkb_options);
-
-    DebugF("[config/hal] Adding device %s\n", name);
+    
+    /* ok, grab any other options from hal.. iterate through all properties
+    * and lets see if any of them are options that we can add */
+    set = libhal_device_get_all_properties(hal_ctx, udi, &error);
+    
+    if (!set) {
+        ErrorF("[config/hal] couldn't get properties for %s: %s (%s)\n",
+               udi, error.name, error.message);
+        goto unwind;
+    }
+	
+    libhal_psi_init(&set_iter,set);
+    while (libhal_psi_has_more(&set_iter)) {
+        /* we are looking for keys in the format "input.x11_options.<propname>"
+         * so find the key, extract, and continue */
+        psi_key = libhal_psi_get_key(&set_iter);    
+        
+        /* strncasecmp is GNU only, is this an issue? */
+        if (psi_key && !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);
+            }
+        }
+        
+        /* 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? */
+    ErrorF("[config/hal]: Adding input device %s\n", name);
     if (NewInputDeviceRequest(options, &dev) != Success) {
         ErrorF("[config/hal] NewInputDeviceRequest failed\n");
         dev = NULL;
@@ -245,6 +291,8 @@ device_added(LibHalContext *hal_ctx, const char *udi)
         dev->config_info = xstrdup(config_info);
 
 unwind:
+	if (set)
+		libhal_free_property_set(set);
     if (path)
         xfree(path);
     if (driver)
diff --git a/config/x11-input.fdi b/config/x11-input.fdi
index c390706..2d3c7e1 100644
--- a/config/x11-input.fdi
+++ b/config/x11-input.fdi
@@ -1,7 +1,49 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <deviceinfo version="0.2">
   <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.
+    
+    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 the xorg.conf options that you would use to specify the
+            exact device, the exact device file is already passed in by HAL.
+            
+        Keys "input.xkb.rules"
+             "input.xkb.model"
+             "input.xkb.layout"
+             "input.xkb.variant"
+             "input.xkb.options"
+             
+            These keys map to the Xkb<value> options in the evdev driver. 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">
       <merge key="input.x11_driver" type="string">mouse</merge>
       <match key="/org/freedesktop/Hal/devices/computer:system.kernel.name"
-- 
1.5.4.3

