hal.c revision 6747b715
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 666747b715Smrg value = malloc(strlen(udi) + 5); /* "hal:" + NULL */ 6705b261ecSmrg if (!value) 6805b261ecSmrg return; 6905b261ecSmrg sprintf(value, "hal:%s", udi); 7005b261ecSmrg 716747b715Smrg remove_devices("hal", value); 7205b261ecSmrg 736747b715Smrg free(value); 7405b261ecSmrg} 7505b261ecSmrg 7605b261ecSmrgstatic char * 7705b261ecSmrgget_prop_string(LibHalContext *hal_ctx, const char *udi, const char *name) 7805b261ecSmrg{ 7905b261ecSmrg char *prop, *ret; 8005b261ecSmrg 8105b261ecSmrg prop = libhal_device_get_property_string(hal_ctx, udi, name, NULL); 824642e01fSmrg LogMessageVerb(X_INFO, 10, "config/hal: getting %s on %s returned %s\n", name, udi, prop ? prop : "(null)"); 8305b261ecSmrg if (prop) { 846747b715Smrg ret = strdup(prop); 8505b261ecSmrg libhal_free_string(prop); 8605b261ecSmrg } 8705b261ecSmrg else { 8805b261ecSmrg return NULL; 8905b261ecSmrg } 9005b261ecSmrg 9105b261ecSmrg return ret; 9205b261ecSmrg} 9305b261ecSmrg 9405b261ecSmrgstatic char * 9505b261ecSmrgget_prop_string_array(LibHalContext *hal_ctx, const char *udi, const char *prop) 9605b261ecSmrg{ 9705b261ecSmrg char **props, *ret, *str; 9805b261ecSmrg int i, len = 0; 9905b261ecSmrg 10005b261ecSmrg props = libhal_device_get_property_strlist(hal_ctx, udi, prop, NULL); 10105b261ecSmrg if (props) { 10205b261ecSmrg for (i = 0; props[i]; i++) 10305b261ecSmrg len += strlen(props[i]); 10405b261ecSmrg 1056747b715Smrg ret = calloc(sizeof(char), len + i); /* i - 1 commas, 1 NULL */ 10605b261ecSmrg if (!ret) { 10705b261ecSmrg libhal_free_string_array(props); 10805b261ecSmrg return NULL; 10905b261ecSmrg } 11005b261ecSmrg 11105b261ecSmrg str = ret; 11205b261ecSmrg for (i = 0; props[i]; i++) { 11305b261ecSmrg strcpy(str, props[i]); 11405b261ecSmrg str += strlen(props[i]); 11505b261ecSmrg *str++ = ','; 11605b261ecSmrg } 11705b261ecSmrg *(str-1) = '\0'; 11805b261ecSmrg 11905b261ecSmrg libhal_free_string_array(props); 12005b261ecSmrg } 12105b261ecSmrg else { 12205b261ecSmrg return NULL; 12305b261ecSmrg } 12405b261ecSmrg 12505b261ecSmrg return ret; 12605b261ecSmrg} 12705b261ecSmrg 12805b261ecSmrgstatic void 12905b261ecSmrgdevice_added(LibHalContext *hal_ctx, const char *udi) 13005b261ecSmrg{ 1314642e01fSmrg char *path = NULL, *driver = NULL, *name = NULL, *config_info = NULL; 1326747b715Smrg char *hal_tags, *parent; 13305b261ecSmrg InputOption *options = NULL, *tmpo = NULL; 1346747b715Smrg InputAttributes attrs = {0}; 1354642e01fSmrg DeviceIntPtr dev = NULL; 13605b261ecSmrg DBusError error; 1374642e01fSmrg struct xkb_options xkb_opts = {0}; 1384642e01fSmrg int rc; 13905b261ecSmrg 1404642e01fSmrg LibHalPropertySet *set = NULL; 1414642e01fSmrg LibHalPropertySetIterator set_iter; 1424642e01fSmrg char *psi_key = NULL, *tmp_val; 14305b261ecSmrg 14405b261ecSmrg 1454642e01fSmrg dbus_error_init(&error); 14605b261ecSmrg 14705b261ecSmrg driver = get_prop_string(hal_ctx, udi, "input.x11_driver"); 1484642e01fSmrg if (!driver){ 1494642e01fSmrg /* verbose, don't tell the user unless they _want_ to see it */ 1504642e01fSmrg LogMessageVerb(X_INFO,7,"config/hal: no driver specified for device %s\n", udi); 1514642e01fSmrg goto unwind; 1524642e01fSmrg } 1534642e01fSmrg 15405b261ecSmrg path = get_prop_string(hal_ctx, udi, "input.device"); 1554642e01fSmrg if (!path) { 1564642e01fSmrg LogMessage(X_WARNING,"config/hal: no driver or path specified for %s\n", udi); 15705b261ecSmrg goto unwind; 15805b261ecSmrg } 1596747b715Smrg attrs.device = strdup(path); 1604642e01fSmrg 16105b261ecSmrg name = get_prop_string(hal_ctx, udi, "info.product"); 16205b261ecSmrg if (!name) 1636747b715Smrg name = strdup("(unnamed)"); 1646747b715Smrg else 1656747b715Smrg attrs.product = strdup(name); 1666747b715Smrg 1676747b715Smrg attrs.vendor = get_prop_string(hal_ctx, udi, "info.vendor"); 1686747b715Smrg hal_tags = get_prop_string(hal_ctx, udi, "input.tags"); 1696747b715Smrg attrs.tags = xstrtokenize(hal_tags, ","); 1706747b715Smrg free(hal_tags); 1716747b715Smrg 1726747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.keys", NULL)) 1736747b715Smrg attrs.flags |= ATTR_KEYBOARD; 1746747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.mouse", NULL)) 1756747b715Smrg attrs.flags |= ATTR_POINTER; 1766747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.joystick", NULL)) 1776747b715Smrg attrs.flags |= ATTR_JOYSTICK; 1786747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.tablet", NULL)) 1796747b715Smrg attrs.flags |= ATTR_TABLET; 1806747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.touchpad", NULL)) 1816747b715Smrg attrs.flags |= ATTR_TOUCHPAD; 1826747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.touchscreen", NULL)) 1836747b715Smrg attrs.flags |= ATTR_TOUCHSCREEN; 1846747b715Smrg 1856747b715Smrg parent = get_prop_string(hal_ctx, udi, "info.parent"); 1866747b715Smrg if (parent) { 1876747b715Smrg int usb_vendor, usb_product; 1886747b715Smrg 1896747b715Smrg attrs.pnp_id = get_prop_string(hal_ctx, parent, "pnp.id"); 1906747b715Smrg 1916747b715Smrg /* construct USB ID in lowercase - "0000:ffff" */ 1926747b715Smrg usb_vendor = libhal_device_get_property_int(hal_ctx, parent, 1936747b715Smrg "usb.vendor_id", NULL); 1946747b715Smrg LogMessageVerb(X_INFO, 10, 1956747b715Smrg "config/hal: getting usb.vendor_id on %s " 1966747b715Smrg "returned %04x\n", parent, usb_vendor); 1976747b715Smrg usb_product = libhal_device_get_property_int(hal_ctx, parent, 1986747b715Smrg "usb.product_id", NULL); 1996747b715Smrg LogMessageVerb(X_INFO, 10, 2006747b715Smrg "config/hal: getting usb.product_id on %s " 2016747b715Smrg "returned %04x\n", parent, usb_product); 2026747b715Smrg if (usb_vendor && usb_product) 2036747b715Smrg attrs.usb_id = Xprintf("%04x:%04x", usb_vendor, usb_product); 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 2296747b715Smrg config_info = malloc(strlen(udi) + 5); /* "hal:" and NULL */ 2304642e01fSmrg if (!config_info) { 2314642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't allocate name\n"); 23205b261ecSmrg goto unwind; 2334642e01fSmrg } 23405b261ecSmrg sprintf(config_info, "hal:%s", udi); 23505b261ecSmrg 2364642e01fSmrg /* Check for duplicate devices */ 2374642e01fSmrg if (device_is_duplicate(config_info)) 2384642e01fSmrg { 2394642e01fSmrg LogMessage(X_WARNING, "config/hal: device %s already added. Ignoring.\n", name); 2404642e01fSmrg goto unwind; 2414642e01fSmrg } 2424642e01fSmrg 2434642e01fSmrg /* ok, grab options from hal.. iterate through all properties 2444642e01fSmrg * and lets see if any of them are options that we can add */ 2454642e01fSmrg set = libhal_device_get_all_properties(hal_ctx, udi, &error); 2464642e01fSmrg 2474642e01fSmrg if (!set) { 2484642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't get property list for %s: %s (%s)\n", 2494642e01fSmrg udi, error.name, error.message); 2504642e01fSmrg goto unwind; 2514642e01fSmrg } 2524642e01fSmrg 2534642e01fSmrg libhal_psi_init(&set_iter,set); 2544642e01fSmrg while (libhal_psi_has_more(&set_iter)) { 2554642e01fSmrg /* we are looking for supported keys.. extract and add to options */ 2564642e01fSmrg psi_key = libhal_psi_get_key(&set_iter); 2574642e01fSmrg 2584642e01fSmrg if (psi_key){ 2594642e01fSmrg 2604642e01fSmrg /* normal options first (input.x11_options.<propname>) */ 2614642e01fSmrg if (!strncasecmp(psi_key, LIBHAL_PROP_KEY, sizeof(LIBHAL_PROP_KEY)-1)){ 2624642e01fSmrg char* tmp; 2634642e01fSmrg 2644642e01fSmrg /* only support strings for all values */ 2654642e01fSmrg tmp_val = get_prop_string(hal_ctx, udi, psi_key); 2664642e01fSmrg 2674642e01fSmrg if (tmp_val){ 2684642e01fSmrg 2694642e01fSmrg /* xkb needs special handling. HAL specs include 2704642e01fSmrg * input.xkb.xyz options, but the x11-input.fdi specifies 2714642e01fSmrg * input.x11_options.Xkbxyz options. By default, we use 2724642e01fSmrg * the former, unless the specific X11 ones are specified. 2734642e01fSmrg * Since we can't predict the order in which the keys 2744642e01fSmrg * arrive, we need to store them. 2754642e01fSmrg */ 2764642e01fSmrg if ((tmp = strcasestr(psi_key, "xkb")) && strlen(tmp) >= 4) 2774642e01fSmrg { 2784642e01fSmrg if (!strcasecmp(&tmp[3], "layout")) 2794642e01fSmrg { 2806747b715Smrg free(xkb_opts.layout); 2814642e01fSmrg xkb_opts.layout = strdup(tmp_val); 2824642e01fSmrg } else if (!strcasecmp(&tmp[3], "model")) 2834642e01fSmrg { 2846747b715Smrg free(xkb_opts.model); 2854642e01fSmrg xkb_opts.model = strdup(tmp_val); 2864642e01fSmrg } else if (!strcasecmp(&tmp[3], "rules")) 2874642e01fSmrg { 2886747b715Smrg free(xkb_opts.rules); 2894642e01fSmrg xkb_opts.rules = strdup(tmp_val); 2904642e01fSmrg } else if (!strcasecmp(&tmp[3], "variant")) 2914642e01fSmrg { 2926747b715Smrg free(xkb_opts.variant); 2934642e01fSmrg xkb_opts.variant = strdup(tmp_val); 2944642e01fSmrg } else if (!strcasecmp(&tmp[3], "options")) 2954642e01fSmrg { 2966747b715Smrg free(xkb_opts.options); 2974642e01fSmrg xkb_opts.options = strdup(tmp_val); 2984642e01fSmrg } 2994642e01fSmrg } else 3004642e01fSmrg { 3014642e01fSmrg /* all others */ 3024642e01fSmrg add_option(&options, psi_key + sizeof(LIBHAL_PROP_KEY)-1, tmp_val); 3036747b715Smrg free(tmp_val); 3044642e01fSmrg } 3054642e01fSmrg } else 3064642e01fSmrg { 3074642e01fSmrg /* server 1.4 had xkb_options as strlist. */ 3084642e01fSmrg if ((tmp = strcasestr(psi_key, "xkb")) && 3094642e01fSmrg (strlen(tmp) >= 4) && 3104642e01fSmrg (!strcasecmp(&tmp[3], "options")) && 3114642e01fSmrg (tmp_val = get_prop_string_array(hal_ctx, udi, psi_key))) 3124642e01fSmrg { 3136747b715Smrg free(xkb_opts.options); 3144642e01fSmrg xkb_opts.options = strdup(tmp_val); 3154642e01fSmrg } 3164642e01fSmrg } 3174642e01fSmrg } else if (!strncasecmp(psi_key, LIBHAL_XKB_PROP_KEY, sizeof(LIBHAL_XKB_PROP_KEY)-1)){ 3184642e01fSmrg char* tmp; 3194642e01fSmrg 3204642e01fSmrg /* only support strings for all values */ 3214642e01fSmrg tmp_val = get_prop_string(hal_ctx, udi, psi_key); 3224642e01fSmrg 3234642e01fSmrg if (tmp_val && strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) { 3244642e01fSmrg 3254642e01fSmrg tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1]; 3264642e01fSmrg 3274642e01fSmrg if (!strcasecmp(tmp, "layout")) 3284642e01fSmrg { 3294642e01fSmrg if (!xkb_opts.layout) 3304642e01fSmrg xkb_opts.layout = strdup(tmp_val); 3314642e01fSmrg } else if (!strcasecmp(tmp, "rules")) 3324642e01fSmrg { 3334642e01fSmrg if (!xkb_opts.rules) 3344642e01fSmrg xkb_opts.rules = strdup(tmp_val); 3354642e01fSmrg } else if (!strcasecmp(tmp, "variant")) 3364642e01fSmrg { 3374642e01fSmrg if (!xkb_opts.variant) 3384642e01fSmrg xkb_opts.variant = strdup(tmp_val); 3394642e01fSmrg } else if (!strcasecmp(tmp, "model")) 3404642e01fSmrg { 3414642e01fSmrg if (!xkb_opts.model) 3424642e01fSmrg xkb_opts.model = strdup(tmp_val); 3434642e01fSmrg } else if (!strcasecmp(tmp, "options")) 3444642e01fSmrg { 3454642e01fSmrg if (!xkb_opts.options) 3464642e01fSmrg xkb_opts.options = strdup(tmp_val); 3474642e01fSmrg } 3486747b715Smrg free(tmp_val); 3494642e01fSmrg } else 3504642e01fSmrg { 3514642e01fSmrg /* server 1.4 had xkb options as strlist */ 3524642e01fSmrg tmp_val = get_prop_string_array(hal_ctx, udi, psi_key); 3534642e01fSmrg if (tmp_val && strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) 3544642e01fSmrg { 3554642e01fSmrg tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1]; 3564642e01fSmrg if (!strcasecmp(tmp, ".options") && (!xkb_opts.options)) 3574642e01fSmrg xkb_opts.options = strdup(tmp_val); 3584642e01fSmrg } 3594642e01fSmrg } 3604642e01fSmrg } 3614642e01fSmrg } 3624642e01fSmrg 3634642e01fSmrg /* psi_key doesn't need to be freed */ 3644642e01fSmrg libhal_psi_next(&set_iter); 3654642e01fSmrg } 3664642e01fSmrg 3674642e01fSmrg 3684642e01fSmrg /* Now add xkb options */ 3694642e01fSmrg if (xkb_opts.layout) 3704642e01fSmrg add_option(&options, "xkb_layout", xkb_opts.layout); 3714642e01fSmrg if (xkb_opts.rules) 3724642e01fSmrg add_option(&options, "xkb_rules", xkb_opts.rules); 3734642e01fSmrg if (xkb_opts.variant) 3744642e01fSmrg add_option(&options, "xkb_variant", xkb_opts.variant); 3754642e01fSmrg if (xkb_opts.model) 3764642e01fSmrg add_option(&options, "xkb_model", xkb_opts.model); 3774642e01fSmrg if (xkb_opts.options) 3784642e01fSmrg add_option(&options, "xkb_options", xkb_opts.options); 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 3884642e01fSmrg for (; dev; dev = dev->next){ 3896747b715Smrg free(dev->config_info); 3906747b715Smrg dev->config_info = strdup(config_info); 3914642e01fSmrg } 39205b261ecSmrg 39305b261ecSmrgunwind: 3944642e01fSmrg if (set) 3954642e01fSmrg libhal_free_property_set(set); 3966747b715Smrg free(path); 3976747b715Smrg free(driver); 3986747b715Smrg free(name); 3996747b715Smrg free(config_info); 40005b261ecSmrg while (!dev && (tmpo = options)) { 40105b261ecSmrg options = tmpo->next; 4026747b715Smrg free(tmpo->key); 4036747b715Smrg free(tmpo->value); 4046747b715Smrg free(tmpo); 40505b261ecSmrg } 40605b261ecSmrg 4076747b715Smrg free(attrs.product); 4086747b715Smrg free(attrs.vendor); 4096747b715Smrg free(attrs.device); 4106747b715Smrg free(attrs.pnp_id); 4116747b715Smrg free(attrs.usb_id); 4126747b715Smrg if (attrs.tags) { 4136747b715Smrg char **tag = attrs.tags; 4146747b715Smrg while (*tag) { 4156747b715Smrg free(*tag); 4166747b715Smrg tag++; 4176747b715Smrg } 4186747b715Smrg free(attrs.tags); 4196747b715Smrg } 4206747b715Smrg 4216747b715Smrg free(xkb_opts.layout); 4226747b715Smrg free(xkb_opts.rules); 4236747b715Smrg free(xkb_opts.model); 4246747b715Smrg free(xkb_opts.variant); 4256747b715Smrg free(xkb_opts.options); 4264642e01fSmrg 42705b261ecSmrg dbus_error_free(&error); 42805b261ecSmrg 42905b261ecSmrg return; 43005b261ecSmrg} 43105b261ecSmrg 43205b261ecSmrgstatic void 43305b261ecSmrgdisconnect_hook(void *data) 43405b261ecSmrg{ 43505b261ecSmrg DBusError error; 43605b261ecSmrg struct config_hal_info *info = data; 43705b261ecSmrg 43805b261ecSmrg if (info->hal_ctx) { 4394642e01fSmrg if (dbus_connection_get_is_connected(info->system_bus)) { 4404642e01fSmrg dbus_error_init(&error); 4414642e01fSmrg if (!libhal_ctx_shutdown(info->hal_ctx, &error)) 4424642e01fSmrg LogMessage(X_WARNING, "config/hal: disconnect_hook couldn't shut down context: %s (%s)\n", 4434642e01fSmrg error.name, error.message); 4444642e01fSmrg dbus_error_free(&error); 4454642e01fSmrg } 44605b261ecSmrg libhal_ctx_free(info->hal_ctx); 44705b261ecSmrg } 44805b261ecSmrg 44905b261ecSmrg info->hal_ctx = NULL; 45005b261ecSmrg info->system_bus = NULL; 45105b261ecSmrg} 45205b261ecSmrg 4534642e01fSmrgstatic BOOL 4544642e01fSmrgconnect_and_register(DBusConnection *connection, struct config_hal_info *info) 45505b261ecSmrg{ 45605b261ecSmrg DBusError error; 45705b261ecSmrg char **devices; 45805b261ecSmrg int num_devices, i; 45905b261ecSmrg 460b86d567bSmrg if (info->hal_ctx) 461b86d567bSmrg return TRUE; /* already registered, pretend we did something */ 462b86d567bSmrg 46305b261ecSmrg info->system_bus = connection; 46405b261ecSmrg 46505b261ecSmrg dbus_error_init(&error); 46605b261ecSmrg 4674642e01fSmrg info->hal_ctx = libhal_ctx_new(); 46805b261ecSmrg if (!info->hal_ctx) { 4694642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't create HAL context\n"); 47005b261ecSmrg goto out_err; 47105b261ecSmrg } 47205b261ecSmrg 47305b261ecSmrg if (!libhal_ctx_set_dbus_connection(info->hal_ctx, info->system_bus)) { 4744642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't associate HAL context with bus\n"); 475b1d344b3Smrg goto out_err; 47605b261ecSmrg } 47705b261ecSmrg if (!libhal_ctx_init(info->hal_ctx, &error)) { 4784642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't initialise context: %s (%s)\n", 479b86d567bSmrg error.name ? error.name : "unknown error", 480b86d567bSmrg error.message ? error.message : "null"); 481b1d344b3Smrg goto out_err; 48205b261ecSmrg } 48305b261ecSmrg if (!libhal_device_property_watch_all(info->hal_ctx, &error)) { 4844642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't watch all properties: %s (%s)\n", 485b86d567bSmrg error.name ? error.name : "unknown error", 486b86d567bSmrg error.message ? error.message : "null"); 487b86d567bSmrg goto out_ctx; 48805b261ecSmrg } 48905b261ecSmrg libhal_ctx_set_device_added(info->hal_ctx, device_added); 49005b261ecSmrg libhal_ctx_set_device_removed(info->hal_ctx, device_removed); 49105b261ecSmrg 49205b261ecSmrg devices = libhal_find_device_by_capability(info->hal_ctx, "input", 49305b261ecSmrg &num_devices, &error); 49405b261ecSmrg /* FIXME: Get default devices if error is set. */ 495b86d567bSmrg if (dbus_error_is_set(&error)) { 496b86d567bSmrg LogMessage(X_ERROR, "config/hal: couldn't find input device: %s (%s)\n", 497b86d567bSmrg error.name ? error.name : "unknown error", 498b86d567bSmrg error.message ? error.message : "null"); 499b86d567bSmrg goto out_ctx; 500b86d567bSmrg } 50105b261ecSmrg for (i = 0; i < num_devices; i++) 50205b261ecSmrg device_added(info->hal_ctx, devices[i]); 50305b261ecSmrg libhal_free_string_array(devices); 50405b261ecSmrg 50505b261ecSmrg dbus_error_free(&error); 50605b261ecSmrg 5074642e01fSmrg return TRUE; 50805b261ecSmrg 50905b261ecSmrgout_ctx: 510b86d567bSmrg dbus_error_free(&error); 511b86d567bSmrg 512b1d344b3Smrg if (!libhal_ctx_shutdown(info->hal_ctx, &error)) { 513b1d344b3Smrg LogMessage(X_WARNING, "config/hal: couldn't shut down context: %s (%s)\n", 514b1d344b3Smrg error.name ? error.name : "unknown error", 515b1d344b3Smrg error.message ? error.message : "null"); 516b1d344b3Smrg dbus_error_free(&error); 517b86d567bSmrg } 518b86d567bSmrg 51905b261ecSmrgout_err: 52005b261ecSmrg dbus_error_free(&error); 52105b261ecSmrg 522b1d344b3Smrg if (info->hal_ctx) { 523b1d344b3Smrg libhal_ctx_free(info->hal_ctx); 524b1d344b3Smrg } 525b1d344b3Smrg 52605b261ecSmrg info->hal_ctx = NULL; 52705b261ecSmrg info->system_bus = NULL; 52805b261ecSmrg 5294642e01fSmrg return FALSE; 5304642e01fSmrg} 5314642e01fSmrg 5324642e01fSmrg 5334642e01fSmrg/** 5344642e01fSmrg * Handle NewOwnerChanged signals to deal with HAL startup at X server runtime. 5354642e01fSmrg * 5364642e01fSmrg * NewOwnerChanged is send once when HAL shuts down, and once again when it 5374642e01fSmrg * comes back up. Message has three arguments, first is the name 5384642e01fSmrg * (org.freedesktop.Hal), the second one is the old owner, third one is new 5394642e01fSmrg * owner. 5404642e01fSmrg */ 5414642e01fSmrgstatic DBusHandlerResult 5424642e01fSmrgownerchanged_handler(DBusConnection *connection, DBusMessage *message, void *data) 5434642e01fSmrg{ 5444642e01fSmrg int ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 5454642e01fSmrg 5464642e01fSmrg if (dbus_message_is_signal(message, 5474642e01fSmrg "org.freedesktop.DBus", 5484642e01fSmrg "NameOwnerChanged")) { 5494642e01fSmrg DBusError error; 5504642e01fSmrg char *name, *old_owner, *new_owner; 5514642e01fSmrg 5524642e01fSmrg dbus_error_init(&error); 5534642e01fSmrg dbus_message_get_args(message, &error, 5544642e01fSmrg DBUS_TYPE_STRING, &name, 5554642e01fSmrg DBUS_TYPE_STRING, &old_owner, 5564642e01fSmrg DBUS_TYPE_STRING, &new_owner, 5574642e01fSmrg DBUS_TYPE_INVALID); 5584642e01fSmrg 5594642e01fSmrg if (dbus_error_is_set(&error)) { 5604642e01fSmrg ErrorF("[config/hal] failed to get NameOwnerChanged args: %s (%s)\n", 5614642e01fSmrg error.name, error.message); 5624642e01fSmrg } else if (name && strcmp(name, "org.freedesktop.Hal") == 0) { 5634642e01fSmrg 5644642e01fSmrg if (!old_owner || !strlen(old_owner)) { 5654642e01fSmrg DebugF("[config/hal] HAL startup detected.\n"); 5664642e01fSmrg if (connect_and_register(connection, (struct config_hal_info*)data)) 5674642e01fSmrg dbus_connection_unregister_object_path(connection, 5684642e01fSmrg "/org/freedesktop/DBus"); 5694642e01fSmrg else 5704642e01fSmrg ErrorF("[config/hal] Failed to connect to HAL bus.\n"); 5714642e01fSmrg } 5724642e01fSmrg 5734642e01fSmrg ret = DBUS_HANDLER_RESULT_HANDLED; 5744642e01fSmrg } 5754642e01fSmrg dbus_error_free(&error); 5764642e01fSmrg } 5774642e01fSmrg 5784642e01fSmrg return ret; 5794642e01fSmrg} 5804642e01fSmrg 5814642e01fSmrg/** 5824642e01fSmrg * Register a handler for the NameOwnerChanged signal. 5834642e01fSmrg */ 5844642e01fSmrgstatic BOOL 5854642e01fSmrglisten_for_startup(DBusConnection *connection, void *data) 5864642e01fSmrg{ 5874642e01fSmrg DBusObjectPathVTable vtable = { .message_function = ownerchanged_handler, }; 5884642e01fSmrg DBusError error; 5894642e01fSmrg const char MATCH_RULE[] = "sender='org.freedesktop.DBus'," 5904642e01fSmrg "interface='org.freedesktop.DBus'," 5914642e01fSmrg "type='signal'," 5924642e01fSmrg "path='/org/freedesktop/DBus'," 5934642e01fSmrg "member='NameOwnerChanged'"; 5944642e01fSmrg int rc = FALSE; 5954642e01fSmrg 5964642e01fSmrg dbus_error_init(&error); 5974642e01fSmrg dbus_bus_add_match(connection, MATCH_RULE, &error); 5984642e01fSmrg if (!dbus_error_is_set(&error)) { 5994642e01fSmrg if (dbus_connection_register_object_path(connection, 6004642e01fSmrg "/org/freedesktop/DBus", 6014642e01fSmrg &vtable, 6024642e01fSmrg data)) 6034642e01fSmrg rc = TRUE; 6044642e01fSmrg else 6054642e01fSmrg ErrorF("[config/hal] cannot register object path.\n"); 6064642e01fSmrg } else { 6074642e01fSmrg ErrorF("[config/hal] couldn't add match rule: %s (%s)\n", error.name, 6084642e01fSmrg error.message); 6094642e01fSmrg ErrorF("[config/hal] cannot detect a HAL startup.\n"); 6104642e01fSmrg } 6114642e01fSmrg 6124642e01fSmrg dbus_error_free(&error); 6134642e01fSmrg 6144642e01fSmrg return rc; 6154642e01fSmrg} 6164642e01fSmrg 6174642e01fSmrgstatic void 6184642e01fSmrgconnect_hook(DBusConnection *connection, void *data) 6194642e01fSmrg{ 6204642e01fSmrg struct config_hal_info *info = data; 6214642e01fSmrg 6224642e01fSmrg if (listen_for_startup(connection, data) && 6234642e01fSmrg connect_and_register(connection, info)) 6244642e01fSmrg dbus_connection_unregister_object_path(connection, 6254642e01fSmrg "/org/freedesktop/DBus"); 6264642e01fSmrg 62705b261ecSmrg return; 62805b261ecSmrg} 62905b261ecSmrg 63005b261ecSmrgstatic struct config_hal_info hal_info; 63105b261ecSmrgstatic struct config_dbus_core_hook hook = { 63205b261ecSmrg .connect = connect_hook, 63305b261ecSmrg .disconnect = disconnect_hook, 63405b261ecSmrg .data = &hal_info, 63505b261ecSmrg}; 63605b261ecSmrg 63705b261ecSmrgint 63805b261ecSmrgconfig_hal_init(void) 63905b261ecSmrg{ 64005b261ecSmrg memset(&hal_info, 0, sizeof(hal_info)); 64105b261ecSmrg hal_info.system_bus = NULL; 64205b261ecSmrg hal_info.hal_ctx = NULL; 64305b261ecSmrg 64405b261ecSmrg if (!config_dbus_core_add_hook(&hook)) { 6454642e01fSmrg LogMessage(X_ERROR, "config/hal: failed to add D-Bus hook\n"); 64605b261ecSmrg return 0; 64705b261ecSmrg } 64805b261ecSmrg 6494642e01fSmrg /* verbose message */ 6506747b715Smrg LogMessageVerb(X_INFO,7,"config/hal: initialized\n"); 6514642e01fSmrg 65205b261ecSmrg return 1; 65305b261ecSmrg} 65405b261ecSmrg 65505b261ecSmrgvoid 65605b261ecSmrgconfig_hal_fini(void) 65705b261ecSmrg{ 65805b261ecSmrg config_dbus_core_remove_hook(&hook); 65905b261ecSmrg} 660