hal.c revision 9ace9065
105b261ecSmrg/* 205b261ecSmrg * Copyright © 2007 Daniel Stone 34642e01fSmrg * Copyright © 2007 Red Hat, Inc. 405b261ecSmrg * 505b261ecSmrg * Permission is hereby granted, free of charge, to any person obtaining a 605b261ecSmrg * copy of this software and associated documentation files (the "Software"), 705b261ecSmrg * to deal in the Software without restriction, including without limitation 805b261ecSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 905b261ecSmrg * and/or sell copies of the Software, and to permit persons to whom the 1005b261ecSmrg * Software is furnished to do so, subject to the following conditions: 1105b261ecSmrg * 1205b261ecSmrg * The above copyright notice and this permission notice (including the next 1305b261ecSmrg * paragraph) shall be included in all copies or substantial portions of the 1405b261ecSmrg * Software. 1505b261ecSmrg * 1605b261ecSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1705b261ecSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1805b261ecSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1905b261ecSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2005b261ecSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2105b261ecSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 2205b261ecSmrg * DEALINGS IN THE SOFTWARE. 2305b261ecSmrg * 2405b261ecSmrg * Author: Daniel Stone <daniel@fooishbar.org> 2505b261ecSmrg */ 2605b261ecSmrg 2705b261ecSmrg#ifdef HAVE_DIX_CONFIG_H 2805b261ecSmrg#include <dix-config.h> 2905b261ecSmrg#endif 3005b261ecSmrg 3105b261ecSmrg#include <dbus/dbus.h> 3205b261ecSmrg#include <hal/libhal.h> 3305b261ecSmrg#include <string.h> 3405b261ecSmrg#include <sys/select.h> 3505b261ecSmrg 3605b261ecSmrg#include "input.h" 3705b261ecSmrg#include "inputstr.h" 3805b261ecSmrg#include "hotplug.h" 3905b261ecSmrg#include "config-backends.h" 4005b261ecSmrg#include "os.h" 4105b261ecSmrg 424642e01fSmrg 434642e01fSmrg#define LIBHAL_PROP_KEY "input.x11_options." 444642e01fSmrg#define LIBHAL_XKB_PROP_KEY "input.xkb." 454642e01fSmrg 4605b261ecSmrg 4705b261ecSmrgstruct config_hal_info { 4805b261ecSmrg DBusConnection *system_bus; 4905b261ecSmrg LibHalContext *hal_ctx; 5005b261ecSmrg}; 5105b261ecSmrg 524642e01fSmrg/* Used for special handling of xkb options. */ 534642e01fSmrgstruct xkb_options { 544642e01fSmrg char* layout; 554642e01fSmrg char* model; 564642e01fSmrg char* rules; 574642e01fSmrg char* variant; 584642e01fSmrg char* options; 594642e01fSmrg}; 604642e01fSmrg 6105b261ecSmrgstatic void 6205b261ecSmrgdevice_removed(LibHalContext *ctx, const char *udi) 6305b261ecSmrg{ 6405b261ecSmrg char *value; 6505b261ecSmrg 669ace9065Smrg if (asprintf (&value, "hal:%s", udi) == -1) 6705b261ecSmrg return; 6805b261ecSmrg 696747b715Smrg remove_devices("hal", value); 7005b261ecSmrg 716747b715Smrg free(value); 7205b261ecSmrg} 7305b261ecSmrg 7405b261ecSmrgstatic char * 7505b261ecSmrgget_prop_string(LibHalContext *hal_ctx, const char *udi, const char *name) 7605b261ecSmrg{ 7705b261ecSmrg char *prop, *ret; 7805b261ecSmrg 7905b261ecSmrg prop = libhal_device_get_property_string(hal_ctx, udi, name, NULL); 804642e01fSmrg LogMessageVerb(X_INFO, 10, "config/hal: getting %s on %s returned %s\n", name, udi, prop ? prop : "(null)"); 8105b261ecSmrg if (prop) { 826747b715Smrg ret = strdup(prop); 8305b261ecSmrg libhal_free_string(prop); 8405b261ecSmrg } 8505b261ecSmrg else { 8605b261ecSmrg return NULL; 8705b261ecSmrg } 8805b261ecSmrg 8905b261ecSmrg return ret; 9005b261ecSmrg} 9105b261ecSmrg 9205b261ecSmrgstatic char * 9305b261ecSmrgget_prop_string_array(LibHalContext *hal_ctx, const char *udi, const char *prop) 9405b261ecSmrg{ 9505b261ecSmrg char **props, *ret, *str; 9605b261ecSmrg int i, len = 0; 9705b261ecSmrg 9805b261ecSmrg props = libhal_device_get_property_strlist(hal_ctx, udi, prop, NULL); 9905b261ecSmrg if (props) { 10005b261ecSmrg for (i = 0; props[i]; i++) 10105b261ecSmrg len += strlen(props[i]); 10205b261ecSmrg 1036747b715Smrg ret = calloc(sizeof(char), len + i); /* i - 1 commas, 1 NULL */ 10405b261ecSmrg if (!ret) { 10505b261ecSmrg libhal_free_string_array(props); 10605b261ecSmrg return NULL; 10705b261ecSmrg } 10805b261ecSmrg 10905b261ecSmrg str = ret; 11005b261ecSmrg for (i = 0; props[i]; i++) { 11105b261ecSmrg strcpy(str, props[i]); 11205b261ecSmrg str += strlen(props[i]); 11305b261ecSmrg *str++ = ','; 11405b261ecSmrg } 11505b261ecSmrg *(str-1) = '\0'; 11605b261ecSmrg 11705b261ecSmrg libhal_free_string_array(props); 11805b261ecSmrg } 11905b261ecSmrg else { 12005b261ecSmrg return NULL; 12105b261ecSmrg } 12205b261ecSmrg 12305b261ecSmrg return ret; 12405b261ecSmrg} 12505b261ecSmrg 12605b261ecSmrgstatic void 12705b261ecSmrgdevice_added(LibHalContext *hal_ctx, const char *udi) 12805b261ecSmrg{ 1294642e01fSmrg char *path = NULL, *driver = NULL, *name = NULL, *config_info = NULL; 1306747b715Smrg char *hal_tags, *parent; 13105b261ecSmrg InputOption *options = NULL, *tmpo = NULL; 1326747b715Smrg InputAttributes attrs = {0}; 1334642e01fSmrg DeviceIntPtr dev = NULL; 13405b261ecSmrg DBusError error; 1354642e01fSmrg struct xkb_options xkb_opts = {0}; 1364642e01fSmrg int rc; 13705b261ecSmrg 1384642e01fSmrg LibHalPropertySet *set = NULL; 1394642e01fSmrg LibHalPropertySetIterator set_iter; 1404642e01fSmrg char *psi_key = NULL, *tmp_val; 14105b261ecSmrg 14205b261ecSmrg 1434642e01fSmrg dbus_error_init(&error); 14405b261ecSmrg 14505b261ecSmrg driver = get_prop_string(hal_ctx, udi, "input.x11_driver"); 1464642e01fSmrg if (!driver){ 1474642e01fSmrg /* verbose, don't tell the user unless they _want_ to see it */ 1484642e01fSmrg LogMessageVerb(X_INFO,7,"config/hal: no driver specified for device %s\n", udi); 1494642e01fSmrg goto unwind; 1504642e01fSmrg } 1514642e01fSmrg 15205b261ecSmrg path = get_prop_string(hal_ctx, udi, "input.device"); 1534642e01fSmrg if (!path) { 1544642e01fSmrg LogMessage(X_WARNING,"config/hal: no driver or path specified for %s\n", udi); 15505b261ecSmrg goto unwind; 15605b261ecSmrg } 1576747b715Smrg attrs.device = strdup(path); 1584642e01fSmrg 15905b261ecSmrg name = get_prop_string(hal_ctx, udi, "info.product"); 16005b261ecSmrg if (!name) 1616747b715Smrg name = strdup("(unnamed)"); 1626747b715Smrg else 1636747b715Smrg attrs.product = strdup(name); 1646747b715Smrg 1656747b715Smrg attrs.vendor = get_prop_string(hal_ctx, udi, "info.vendor"); 1666747b715Smrg hal_tags = get_prop_string(hal_ctx, udi, "input.tags"); 1676747b715Smrg attrs.tags = xstrtokenize(hal_tags, ","); 1686747b715Smrg free(hal_tags); 1696747b715Smrg 1706747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.keys", NULL)) 1716747b715Smrg attrs.flags |= ATTR_KEYBOARD; 1726747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.mouse", NULL)) 1736747b715Smrg attrs.flags |= ATTR_POINTER; 1746747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.joystick", NULL)) 1756747b715Smrg attrs.flags |= ATTR_JOYSTICK; 1766747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.tablet", NULL)) 1776747b715Smrg attrs.flags |= ATTR_TABLET; 1786747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.touchpad", NULL)) 1796747b715Smrg attrs.flags |= ATTR_TOUCHPAD; 1806747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.touchscreen", NULL)) 1816747b715Smrg attrs.flags |= ATTR_TOUCHSCREEN; 1826747b715Smrg 1836747b715Smrg parent = get_prop_string(hal_ctx, udi, "info.parent"); 1846747b715Smrg if (parent) { 1856747b715Smrg int usb_vendor, usb_product; 1866747b715Smrg 1876747b715Smrg attrs.pnp_id = get_prop_string(hal_ctx, parent, "pnp.id"); 1886747b715Smrg 1896747b715Smrg /* construct USB ID in lowercase - "0000:ffff" */ 1906747b715Smrg usb_vendor = libhal_device_get_property_int(hal_ctx, parent, 1916747b715Smrg "usb.vendor_id", NULL); 1926747b715Smrg LogMessageVerb(X_INFO, 10, 1936747b715Smrg "config/hal: getting usb.vendor_id on %s " 1946747b715Smrg "returned %04x\n", parent, usb_vendor); 1956747b715Smrg usb_product = libhal_device_get_property_int(hal_ctx, parent, 1966747b715Smrg "usb.product_id", NULL); 1976747b715Smrg LogMessageVerb(X_INFO, 10, 1986747b715Smrg "config/hal: getting usb.product_id on %s " 1996747b715Smrg "returned %04x\n", parent, usb_product); 2006747b715Smrg if (usb_vendor && usb_product) 2019ace9065Smrg if (asprintf(&attrs.usb_id, "%04x:%04x", usb_vendor, usb_product) 2029ace9065Smrg == -1) 2039ace9065Smrg attrs.usb_id = NULL; 2046747b715Smrg 2056747b715Smrg free(parent); 2066747b715Smrg } 20705b261ecSmrg 2086747b715Smrg options = calloc(sizeof(*options), 1); 2094642e01fSmrg if (!options){ 2104642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't allocate space for input options!\n"); 2114642e01fSmrg goto unwind; 21205b261ecSmrg } 21305b261ecSmrg 2146747b715Smrg options->key = strdup("_source"); 2156747b715Smrg options->value = strdup("server/hal"); 21605b261ecSmrg if (!options->key || !options->value) { 2174642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't allocate first key/value pair\n"); 21805b261ecSmrg goto unwind; 21905b261ecSmrg } 22005b261ecSmrg 2214642e01fSmrg /* most drivers use device.. not path. evdev uses both however, but the 2224642e01fSmrg * path version isn't documented apparently. support both for now. */ 22305b261ecSmrg add_option(&options, "path", path); 2244642e01fSmrg add_option(&options, "device", path); 2254642e01fSmrg 22605b261ecSmrg add_option(&options, "driver", driver); 22705b261ecSmrg add_option(&options, "name", name); 2284642e01fSmrg 2299ace9065Smrg if (asprintf (&config_info, "hal:%s", udi) == -1) { 2309ace9065Smrg config_info = NULL; 2314642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't allocate name\n"); 23205b261ecSmrg goto unwind; 2334642e01fSmrg } 23405b261ecSmrg 2354642e01fSmrg /* Check for duplicate devices */ 2364642e01fSmrg if (device_is_duplicate(config_info)) 2374642e01fSmrg { 2384642e01fSmrg LogMessage(X_WARNING, "config/hal: device %s already added. Ignoring.\n", name); 2394642e01fSmrg goto unwind; 2404642e01fSmrg } 2414642e01fSmrg 2424642e01fSmrg /* ok, grab options from hal.. iterate through all properties 2434642e01fSmrg * and lets see if any of them are options that we can add */ 2444642e01fSmrg set = libhal_device_get_all_properties(hal_ctx, udi, &error); 2454642e01fSmrg 2464642e01fSmrg if (!set) { 2474642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't get property list for %s: %s (%s)\n", 2484642e01fSmrg udi, error.name, error.message); 2494642e01fSmrg goto unwind; 2504642e01fSmrg } 2514642e01fSmrg 2524642e01fSmrg libhal_psi_init(&set_iter,set); 2534642e01fSmrg while (libhal_psi_has_more(&set_iter)) { 2544642e01fSmrg /* we are looking for supported keys.. extract and add to options */ 2554642e01fSmrg psi_key = libhal_psi_get_key(&set_iter); 2564642e01fSmrg 2574642e01fSmrg if (psi_key){ 2584642e01fSmrg 2594642e01fSmrg /* normal options first (input.x11_options.<propname>) */ 2604642e01fSmrg if (!strncasecmp(psi_key, LIBHAL_PROP_KEY, sizeof(LIBHAL_PROP_KEY)-1)){ 2614642e01fSmrg char* tmp; 2624642e01fSmrg 2634642e01fSmrg /* only support strings for all values */ 2644642e01fSmrg tmp_val = get_prop_string(hal_ctx, udi, psi_key); 2654642e01fSmrg 2664642e01fSmrg if (tmp_val){ 2674642e01fSmrg 2684642e01fSmrg /* xkb needs special handling. HAL specs include 2694642e01fSmrg * input.xkb.xyz options, but the x11-input.fdi specifies 2704642e01fSmrg * input.x11_options.Xkbxyz options. By default, we use 2714642e01fSmrg * the former, unless the specific X11 ones are specified. 2724642e01fSmrg * Since we can't predict the order in which the keys 2734642e01fSmrg * arrive, we need to store them. 2744642e01fSmrg */ 2754642e01fSmrg if ((tmp = strcasestr(psi_key, "xkb")) && strlen(tmp) >= 4) 2764642e01fSmrg { 2774642e01fSmrg if (!strcasecmp(&tmp[3], "layout")) 2784642e01fSmrg { 2796747b715Smrg free(xkb_opts.layout); 2804642e01fSmrg xkb_opts.layout = strdup(tmp_val); 2814642e01fSmrg } else if (!strcasecmp(&tmp[3], "model")) 2824642e01fSmrg { 2836747b715Smrg free(xkb_opts.model); 2844642e01fSmrg xkb_opts.model = strdup(tmp_val); 2854642e01fSmrg } else if (!strcasecmp(&tmp[3], "rules")) 2864642e01fSmrg { 2876747b715Smrg free(xkb_opts.rules); 2884642e01fSmrg xkb_opts.rules = strdup(tmp_val); 2894642e01fSmrg } else if (!strcasecmp(&tmp[3], "variant")) 2904642e01fSmrg { 2916747b715Smrg free(xkb_opts.variant); 2924642e01fSmrg xkb_opts.variant = strdup(tmp_val); 2934642e01fSmrg } else if (!strcasecmp(&tmp[3], "options")) 2944642e01fSmrg { 2956747b715Smrg free(xkb_opts.options); 2964642e01fSmrg xkb_opts.options = strdup(tmp_val); 2974642e01fSmrg } 2984642e01fSmrg } else 2994642e01fSmrg { 3004642e01fSmrg /* all others */ 3014642e01fSmrg add_option(&options, psi_key + sizeof(LIBHAL_PROP_KEY)-1, tmp_val); 3026747b715Smrg free(tmp_val); 3034642e01fSmrg } 3044642e01fSmrg } else 3054642e01fSmrg { 3064642e01fSmrg /* server 1.4 had xkb_options as strlist. */ 3074642e01fSmrg if ((tmp = strcasestr(psi_key, "xkb")) && 3084642e01fSmrg (strlen(tmp) >= 4) && 3094642e01fSmrg (!strcasecmp(&tmp[3], "options")) && 3104642e01fSmrg (tmp_val = get_prop_string_array(hal_ctx, udi, psi_key))) 3114642e01fSmrg { 3126747b715Smrg free(xkb_opts.options); 3134642e01fSmrg xkb_opts.options = strdup(tmp_val); 3144642e01fSmrg } 3154642e01fSmrg } 3164642e01fSmrg } else if (!strncasecmp(psi_key, LIBHAL_XKB_PROP_KEY, sizeof(LIBHAL_XKB_PROP_KEY)-1)){ 3174642e01fSmrg char* tmp; 3184642e01fSmrg 3194642e01fSmrg /* only support strings for all values */ 3204642e01fSmrg tmp_val = get_prop_string(hal_ctx, udi, psi_key); 3214642e01fSmrg 3224642e01fSmrg if (tmp_val && strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) { 3234642e01fSmrg 3244642e01fSmrg tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1]; 3254642e01fSmrg 3264642e01fSmrg if (!strcasecmp(tmp, "layout")) 3274642e01fSmrg { 3284642e01fSmrg if (!xkb_opts.layout) 3294642e01fSmrg xkb_opts.layout = strdup(tmp_val); 3304642e01fSmrg } else if (!strcasecmp(tmp, "rules")) 3314642e01fSmrg { 3324642e01fSmrg if (!xkb_opts.rules) 3334642e01fSmrg xkb_opts.rules = strdup(tmp_val); 3344642e01fSmrg } else if (!strcasecmp(tmp, "variant")) 3354642e01fSmrg { 3364642e01fSmrg if (!xkb_opts.variant) 3374642e01fSmrg xkb_opts.variant = strdup(tmp_val); 3384642e01fSmrg } else if (!strcasecmp(tmp, "model")) 3394642e01fSmrg { 3404642e01fSmrg if (!xkb_opts.model) 3414642e01fSmrg xkb_opts.model = strdup(tmp_val); 3424642e01fSmrg } else if (!strcasecmp(tmp, "options")) 3434642e01fSmrg { 3444642e01fSmrg if (!xkb_opts.options) 3454642e01fSmrg xkb_opts.options = strdup(tmp_val); 3464642e01fSmrg } 3476747b715Smrg free(tmp_val); 3484642e01fSmrg } else 3494642e01fSmrg { 3504642e01fSmrg /* server 1.4 had xkb options as strlist */ 3514642e01fSmrg tmp_val = get_prop_string_array(hal_ctx, udi, psi_key); 3524642e01fSmrg if (tmp_val && strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) 3534642e01fSmrg { 3544642e01fSmrg tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1]; 3554642e01fSmrg if (!strcasecmp(tmp, ".options") && (!xkb_opts.options)) 3564642e01fSmrg xkb_opts.options = strdup(tmp_val); 3574642e01fSmrg } 3584642e01fSmrg } 3594642e01fSmrg } 3604642e01fSmrg } 3614642e01fSmrg 3624642e01fSmrg /* psi_key doesn't need to be freed */ 3634642e01fSmrg libhal_psi_next(&set_iter); 3644642e01fSmrg } 3654642e01fSmrg 3664642e01fSmrg 3674642e01fSmrg /* Now add xkb options */ 3684642e01fSmrg if (xkb_opts.layout) 3694642e01fSmrg add_option(&options, "xkb_layout", xkb_opts.layout); 3704642e01fSmrg if (xkb_opts.rules) 3714642e01fSmrg add_option(&options, "xkb_rules", xkb_opts.rules); 3724642e01fSmrg if (xkb_opts.variant) 3734642e01fSmrg add_option(&options, "xkb_variant", xkb_opts.variant); 3744642e01fSmrg if (xkb_opts.model) 3754642e01fSmrg add_option(&options, "xkb_model", xkb_opts.model); 3764642e01fSmrg if (xkb_opts.options) 3774642e01fSmrg add_option(&options, "xkb_options", xkb_opts.options); 3789ace9065Smrg add_option(&options, "config_info", config_info); 3794642e01fSmrg 3804642e01fSmrg /* this isn't an error, but how else do you output something that the user can see? */ 3814642e01fSmrg LogMessage(X_INFO, "config/hal: Adding input device %s\n", name); 3826747b715Smrg if ((rc = NewInputDeviceRequest(options, &attrs, &dev)) != Success) { 3834642e01fSmrg LogMessage(X_ERROR, "config/hal: NewInputDeviceRequest failed (%d)\n", rc); 38405b261ecSmrg dev = NULL; 38505b261ecSmrg goto unwind; 38605b261ecSmrg } 38705b261ecSmrg 38805b261ecSmrgunwind: 3894642e01fSmrg if (set) 3904642e01fSmrg libhal_free_property_set(set); 3916747b715Smrg free(path); 3926747b715Smrg free(driver); 3936747b715Smrg free(name); 3946747b715Smrg free(config_info); 3959ace9065Smrg while ((tmpo = options)) { 39605b261ecSmrg options = tmpo->next; 3979ace9065Smrg free(tmpo->key); /* NULL if dev != NULL */ 3989ace9065Smrg free(tmpo->value); /* NULL if dev != NULL */ 3996747b715Smrg free(tmpo); 40005b261ecSmrg } 40105b261ecSmrg 4026747b715Smrg free(attrs.product); 4036747b715Smrg free(attrs.vendor); 4046747b715Smrg free(attrs.device); 4056747b715Smrg free(attrs.pnp_id); 4066747b715Smrg free(attrs.usb_id); 4076747b715Smrg if (attrs.tags) { 4086747b715Smrg char **tag = attrs.tags; 4096747b715Smrg while (*tag) { 4106747b715Smrg free(*tag); 4116747b715Smrg tag++; 4126747b715Smrg } 4136747b715Smrg free(attrs.tags); 4146747b715Smrg } 4156747b715Smrg 4166747b715Smrg free(xkb_opts.layout); 4176747b715Smrg free(xkb_opts.rules); 4186747b715Smrg free(xkb_opts.model); 4196747b715Smrg free(xkb_opts.variant); 4206747b715Smrg free(xkb_opts.options); 4214642e01fSmrg 42205b261ecSmrg dbus_error_free(&error); 42305b261ecSmrg 42405b261ecSmrg return; 42505b261ecSmrg} 42605b261ecSmrg 42705b261ecSmrgstatic void 42805b261ecSmrgdisconnect_hook(void *data) 42905b261ecSmrg{ 43005b261ecSmrg DBusError error; 43105b261ecSmrg struct config_hal_info *info = data; 43205b261ecSmrg 43305b261ecSmrg if (info->hal_ctx) { 4344642e01fSmrg if (dbus_connection_get_is_connected(info->system_bus)) { 4354642e01fSmrg dbus_error_init(&error); 4364642e01fSmrg if (!libhal_ctx_shutdown(info->hal_ctx, &error)) 4374642e01fSmrg LogMessage(X_WARNING, "config/hal: disconnect_hook couldn't shut down context: %s (%s)\n", 4384642e01fSmrg error.name, error.message); 4394642e01fSmrg dbus_error_free(&error); 4404642e01fSmrg } 44105b261ecSmrg libhal_ctx_free(info->hal_ctx); 44205b261ecSmrg } 44305b261ecSmrg 44405b261ecSmrg info->hal_ctx = NULL; 44505b261ecSmrg info->system_bus = NULL; 44605b261ecSmrg} 44705b261ecSmrg 4484642e01fSmrgstatic BOOL 4494642e01fSmrgconnect_and_register(DBusConnection *connection, struct config_hal_info *info) 45005b261ecSmrg{ 45105b261ecSmrg DBusError error; 45205b261ecSmrg char **devices; 45305b261ecSmrg int num_devices, i; 45405b261ecSmrg 455b86d567bSmrg if (info->hal_ctx) 456b86d567bSmrg return TRUE; /* already registered, pretend we did something */ 457b86d567bSmrg 45805b261ecSmrg info->system_bus = connection; 45905b261ecSmrg 46005b261ecSmrg dbus_error_init(&error); 46105b261ecSmrg 4624642e01fSmrg info->hal_ctx = libhal_ctx_new(); 46305b261ecSmrg if (!info->hal_ctx) { 4644642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't create HAL context\n"); 46505b261ecSmrg goto out_err; 46605b261ecSmrg } 46705b261ecSmrg 46805b261ecSmrg if (!libhal_ctx_set_dbus_connection(info->hal_ctx, info->system_bus)) { 4694642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't associate HAL context with bus\n"); 470b1d344b3Smrg goto out_err; 47105b261ecSmrg } 47205b261ecSmrg if (!libhal_ctx_init(info->hal_ctx, &error)) { 4734642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't initialise context: %s (%s)\n", 474b86d567bSmrg error.name ? error.name : "unknown error", 475b86d567bSmrg error.message ? error.message : "null"); 476b1d344b3Smrg goto out_err; 47705b261ecSmrg } 47805b261ecSmrg if (!libhal_device_property_watch_all(info->hal_ctx, &error)) { 4794642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't watch all properties: %s (%s)\n", 480b86d567bSmrg error.name ? error.name : "unknown error", 481b86d567bSmrg error.message ? error.message : "null"); 482b86d567bSmrg goto out_ctx; 48305b261ecSmrg } 48405b261ecSmrg libhal_ctx_set_device_added(info->hal_ctx, device_added); 48505b261ecSmrg libhal_ctx_set_device_removed(info->hal_ctx, device_removed); 48605b261ecSmrg 48705b261ecSmrg devices = libhal_find_device_by_capability(info->hal_ctx, "input", 48805b261ecSmrg &num_devices, &error); 48905b261ecSmrg /* FIXME: Get default devices if error is set. */ 490b86d567bSmrg if (dbus_error_is_set(&error)) { 491b86d567bSmrg LogMessage(X_ERROR, "config/hal: couldn't find input device: %s (%s)\n", 492b86d567bSmrg error.name ? error.name : "unknown error", 493b86d567bSmrg error.message ? error.message : "null"); 494b86d567bSmrg goto out_ctx; 495b86d567bSmrg } 49605b261ecSmrg for (i = 0; i < num_devices; i++) 49705b261ecSmrg device_added(info->hal_ctx, devices[i]); 49805b261ecSmrg libhal_free_string_array(devices); 49905b261ecSmrg 50005b261ecSmrg dbus_error_free(&error); 50105b261ecSmrg 5024642e01fSmrg return TRUE; 50305b261ecSmrg 50405b261ecSmrgout_ctx: 505b86d567bSmrg dbus_error_free(&error); 506b86d567bSmrg 507b1d344b3Smrg if (!libhal_ctx_shutdown(info->hal_ctx, &error)) { 508b1d344b3Smrg LogMessage(X_WARNING, "config/hal: couldn't shut down context: %s (%s)\n", 509b1d344b3Smrg error.name ? error.name : "unknown error", 510b1d344b3Smrg error.message ? error.message : "null"); 511b1d344b3Smrg dbus_error_free(&error); 512b86d567bSmrg } 513b86d567bSmrg 51405b261ecSmrgout_err: 51505b261ecSmrg dbus_error_free(&error); 51605b261ecSmrg 517b1d344b3Smrg if (info->hal_ctx) { 518b1d344b3Smrg libhal_ctx_free(info->hal_ctx); 519b1d344b3Smrg } 520b1d344b3Smrg 52105b261ecSmrg info->hal_ctx = NULL; 52205b261ecSmrg info->system_bus = NULL; 52305b261ecSmrg 5244642e01fSmrg return FALSE; 5254642e01fSmrg} 5264642e01fSmrg 5274642e01fSmrg 5284642e01fSmrg/** 5294642e01fSmrg * Handle NewOwnerChanged signals to deal with HAL startup at X server runtime. 5304642e01fSmrg * 5314642e01fSmrg * NewOwnerChanged is send once when HAL shuts down, and once again when it 5324642e01fSmrg * comes back up. Message has three arguments, first is the name 5334642e01fSmrg * (org.freedesktop.Hal), the second one is the old owner, third one is new 5344642e01fSmrg * owner. 5354642e01fSmrg */ 5364642e01fSmrgstatic DBusHandlerResult 5374642e01fSmrgownerchanged_handler(DBusConnection *connection, DBusMessage *message, void *data) 5384642e01fSmrg{ 5394642e01fSmrg int ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 5404642e01fSmrg 5414642e01fSmrg if (dbus_message_is_signal(message, 5424642e01fSmrg "org.freedesktop.DBus", 5434642e01fSmrg "NameOwnerChanged")) { 5444642e01fSmrg DBusError error; 5454642e01fSmrg char *name, *old_owner, *new_owner; 5464642e01fSmrg 5474642e01fSmrg dbus_error_init(&error); 5484642e01fSmrg dbus_message_get_args(message, &error, 5494642e01fSmrg DBUS_TYPE_STRING, &name, 5504642e01fSmrg DBUS_TYPE_STRING, &old_owner, 5514642e01fSmrg DBUS_TYPE_STRING, &new_owner, 5524642e01fSmrg DBUS_TYPE_INVALID); 5534642e01fSmrg 5544642e01fSmrg if (dbus_error_is_set(&error)) { 5554642e01fSmrg ErrorF("[config/hal] failed to get NameOwnerChanged args: %s (%s)\n", 5564642e01fSmrg error.name, error.message); 5574642e01fSmrg } else if (name && strcmp(name, "org.freedesktop.Hal") == 0) { 5584642e01fSmrg 5594642e01fSmrg if (!old_owner || !strlen(old_owner)) { 5604642e01fSmrg DebugF("[config/hal] HAL startup detected.\n"); 5614642e01fSmrg if (connect_and_register(connection, (struct config_hal_info*)data)) 5624642e01fSmrg dbus_connection_unregister_object_path(connection, 5634642e01fSmrg "/org/freedesktop/DBus"); 5644642e01fSmrg else 5654642e01fSmrg ErrorF("[config/hal] Failed to connect to HAL bus.\n"); 5664642e01fSmrg } 5674642e01fSmrg 5684642e01fSmrg ret = DBUS_HANDLER_RESULT_HANDLED; 5694642e01fSmrg } 5704642e01fSmrg dbus_error_free(&error); 5714642e01fSmrg } 5724642e01fSmrg 5734642e01fSmrg return ret; 5744642e01fSmrg} 5754642e01fSmrg 5764642e01fSmrg/** 5774642e01fSmrg * Register a handler for the NameOwnerChanged signal. 5784642e01fSmrg */ 5794642e01fSmrgstatic BOOL 5804642e01fSmrglisten_for_startup(DBusConnection *connection, void *data) 5814642e01fSmrg{ 5824642e01fSmrg DBusObjectPathVTable vtable = { .message_function = ownerchanged_handler, }; 5834642e01fSmrg DBusError error; 5844642e01fSmrg const char MATCH_RULE[] = "sender='org.freedesktop.DBus'," 5854642e01fSmrg "interface='org.freedesktop.DBus'," 5864642e01fSmrg "type='signal'," 5874642e01fSmrg "path='/org/freedesktop/DBus'," 5884642e01fSmrg "member='NameOwnerChanged'"; 5894642e01fSmrg int rc = FALSE; 5904642e01fSmrg 5914642e01fSmrg dbus_error_init(&error); 5924642e01fSmrg dbus_bus_add_match(connection, MATCH_RULE, &error); 5934642e01fSmrg if (!dbus_error_is_set(&error)) { 5944642e01fSmrg if (dbus_connection_register_object_path(connection, 5954642e01fSmrg "/org/freedesktop/DBus", 5964642e01fSmrg &vtable, 5974642e01fSmrg data)) 5984642e01fSmrg rc = TRUE; 5994642e01fSmrg else 6004642e01fSmrg ErrorF("[config/hal] cannot register object path.\n"); 6014642e01fSmrg } else { 6024642e01fSmrg ErrorF("[config/hal] couldn't add match rule: %s (%s)\n", error.name, 6034642e01fSmrg error.message); 6044642e01fSmrg ErrorF("[config/hal] cannot detect a HAL startup.\n"); 6054642e01fSmrg } 6064642e01fSmrg 6074642e01fSmrg dbus_error_free(&error); 6084642e01fSmrg 6094642e01fSmrg return rc; 6104642e01fSmrg} 6114642e01fSmrg 6124642e01fSmrgstatic void 6134642e01fSmrgconnect_hook(DBusConnection *connection, void *data) 6144642e01fSmrg{ 6154642e01fSmrg struct config_hal_info *info = data; 6164642e01fSmrg 6174642e01fSmrg if (listen_for_startup(connection, data) && 6184642e01fSmrg connect_and_register(connection, info)) 6194642e01fSmrg dbus_connection_unregister_object_path(connection, 6204642e01fSmrg "/org/freedesktop/DBus"); 6214642e01fSmrg 62205b261ecSmrg return; 62305b261ecSmrg} 62405b261ecSmrg 62505b261ecSmrgstatic struct config_hal_info hal_info; 62605b261ecSmrgstatic struct config_dbus_core_hook hook = { 62705b261ecSmrg .connect = connect_hook, 62805b261ecSmrg .disconnect = disconnect_hook, 62905b261ecSmrg .data = &hal_info, 63005b261ecSmrg}; 63105b261ecSmrg 63205b261ecSmrgint 63305b261ecSmrgconfig_hal_init(void) 63405b261ecSmrg{ 63505b261ecSmrg memset(&hal_info, 0, sizeof(hal_info)); 63605b261ecSmrg hal_info.system_bus = NULL; 63705b261ecSmrg hal_info.hal_ctx = NULL; 63805b261ecSmrg 63905b261ecSmrg if (!config_dbus_core_add_hook(&hook)) { 6404642e01fSmrg LogMessage(X_ERROR, "config/hal: failed to add D-Bus hook\n"); 64105b261ecSmrg return 0; 64205b261ecSmrg } 64305b261ecSmrg 6444642e01fSmrg /* verbose message */ 6456747b715Smrg LogMessageVerb(X_INFO,7,"config/hal: initialized\n"); 6464642e01fSmrg 64705b261ecSmrg return 1; 64805b261ecSmrg} 64905b261ecSmrg 65005b261ecSmrgvoid 65105b261ecSmrgconfig_hal_fini(void) 65205b261ecSmrg{ 65305b261ecSmrg config_dbus_core_remove_hook(&hook); 65405b261ecSmrg} 655