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 3635c4bbdfSmrg#include "dbus-core.h" 3705b261ecSmrg#include "input.h" 3805b261ecSmrg#include "inputstr.h" 3905b261ecSmrg#include "hotplug.h" 4005b261ecSmrg#include "config-backends.h" 4105b261ecSmrg#include "os.h" 4205b261ecSmrg 434642e01fSmrg#define LIBHAL_PROP_KEY "input.x11_options." 444642e01fSmrg#define LIBHAL_XKB_PROP_KEY "input.xkb." 454642e01fSmrg 4605b261ecSmrgstruct config_hal_info { 4705b261ecSmrg DBusConnection *system_bus; 4805b261ecSmrg LibHalContext *hal_ctx; 4905b261ecSmrg}; 5005b261ecSmrg 514642e01fSmrg/* Used for special handling of xkb options. */ 524642e01fSmrgstruct xkb_options { 5335c4bbdfSmrg char *layout; 5435c4bbdfSmrg char *model; 5535c4bbdfSmrg char *rules; 5635c4bbdfSmrg char *variant; 5735c4bbdfSmrg char *options; 584642e01fSmrg}; 594642e01fSmrg 6005b261ecSmrgstatic void 6135c4bbdfSmrgdevice_removed(LibHalContext * ctx, const char *udi) 6205b261ecSmrg{ 6305b261ecSmrg char *value; 6405b261ecSmrg 6535c4bbdfSmrg if (asprintf(&value, "hal:%s", udi) == -1) 6605b261ecSmrg return; 6705b261ecSmrg 686747b715Smrg remove_devices("hal", value); 6905b261ecSmrg 706747b715Smrg free(value); 7105b261ecSmrg} 7205b261ecSmrg 7305b261ecSmrgstatic char * 7435c4bbdfSmrgget_prop_string(LibHalContext * hal_ctx, const char *udi, const char *name) 7505b261ecSmrg{ 7605b261ecSmrg char *prop, *ret; 7705b261ecSmrg 7805b261ecSmrg prop = libhal_device_get_property_string(hal_ctx, udi, name, NULL); 7935c4bbdfSmrg LogMessageVerb(X_INFO, 10, "config/hal: getting %s on %s returned %s\n", 8035c4bbdfSmrg 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 * 9335c4bbdfSmrgget_prop_string_array(LibHalContext * hal_ctx, const char *udi, 9435c4bbdfSmrg const char *prop) 9505b261ecSmrg{ 9605b261ecSmrg char **props, *ret, *str; 9705b261ecSmrg int i, len = 0; 9805b261ecSmrg 9905b261ecSmrg props = libhal_device_get_property_strlist(hal_ctx, udi, prop, NULL); 10005b261ecSmrg if (props) { 10105b261ecSmrg for (i = 0; props[i]; i++) 10205b261ecSmrg len += strlen(props[i]); 10305b261ecSmrg 10435c4bbdfSmrg ret = calloc(sizeof(char), len + i); /* i - 1 commas, 1 NULL */ 10505b261ecSmrg if (!ret) { 10605b261ecSmrg libhal_free_string_array(props); 10705b261ecSmrg return NULL; 10805b261ecSmrg } 10905b261ecSmrg 11005b261ecSmrg str = ret; 11105b261ecSmrg for (i = 0; props[i]; i++) { 11205b261ecSmrg strcpy(str, props[i]); 11305b261ecSmrg str += strlen(props[i]); 11405b261ecSmrg *str++ = ','; 11505b261ecSmrg } 11635c4bbdfSmrg *(str - 1) = '\0'; 11705b261ecSmrg 11805b261ecSmrg libhal_free_string_array(props); 11905b261ecSmrg } 12005b261ecSmrg else { 12105b261ecSmrg return NULL; 12205b261ecSmrg } 12305b261ecSmrg 12405b261ecSmrg return ret; 12505b261ecSmrg} 12605b261ecSmrg 12705b261ecSmrgstatic void 12835c4bbdfSmrgdevice_added(LibHalContext * hal_ctx, const char *udi) 12905b261ecSmrg{ 1304642e01fSmrg char *path = NULL, *driver = NULL, *name = NULL, *config_info = NULL; 1316747b715Smrg char *hal_tags, *parent; 13235c4bbdfSmrg InputOption *input_options = NULL; 13335c4bbdfSmrg InputAttributes attrs = { 0 }; 1344642e01fSmrg DeviceIntPtr dev = NULL; 13505b261ecSmrg DBusError error; 13635c4bbdfSmrg struct xkb_options xkb_opts = { 0 }; 1374642e01fSmrg int rc; 13805b261ecSmrg 1394642e01fSmrg LibHalPropertySet *set = NULL; 14035c4bbdfSmrg LibHalPropertySetIterator set_iter; 1414642e01fSmrg char *psi_key = NULL, *tmp_val; 14205b261ecSmrg 1434642e01fSmrg dbus_error_init(&error); 14405b261ecSmrg 14505b261ecSmrg driver = get_prop_string(hal_ctx, udi, "input.x11_driver"); 14635c4bbdfSmrg if (!driver) { 1474642e01fSmrg /* verbose, don't tell the user unless they _want_ to see it */ 14835c4bbdfSmrg LogMessageVerb(X_INFO, 7, 14935c4bbdfSmrg "config/hal: no driver specified for device %s\n", udi); 1504642e01fSmrg goto unwind; 1514642e01fSmrg } 1524642e01fSmrg 15305b261ecSmrg path = get_prop_string(hal_ctx, udi, "input.device"); 1544642e01fSmrg if (!path) { 15535c4bbdfSmrg LogMessage(X_WARNING, 15635c4bbdfSmrg "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)) 1731b5d61b8Smrg attrs.flags |= ATTR_KEY | 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; 1801b5d61b8Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.tablet_pad", NULL)) 1811b5d61b8Smrg attrs.flags |= ATTR_TABLET_PAD; 1826747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.touchpad", NULL)) 1836747b715Smrg attrs.flags |= ATTR_TOUCHPAD; 1846747b715Smrg if (libhal_device_query_capability(hal_ctx, udi, "input.touchscreen", NULL)) 1856747b715Smrg attrs.flags |= ATTR_TOUCHSCREEN; 1866747b715Smrg 1876747b715Smrg parent = get_prop_string(hal_ctx, udi, "info.parent"); 1886747b715Smrg if (parent) { 1896747b715Smrg int usb_vendor, usb_product; 19035c4bbdfSmrg char *old_parent; 1916747b715Smrg 1926747b715Smrg /* construct USB ID in lowercase - "0000:ffff" */ 1936747b715Smrg usb_vendor = libhal_device_get_property_int(hal_ctx, parent, 1946747b715Smrg "usb.vendor_id", NULL); 1956747b715Smrg LogMessageVerb(X_INFO, 10, 1966747b715Smrg "config/hal: getting usb.vendor_id on %s " 1976747b715Smrg "returned %04x\n", parent, usb_vendor); 1986747b715Smrg usb_product = libhal_device_get_property_int(hal_ctx, parent, 1996747b715Smrg "usb.product_id", NULL); 2006747b715Smrg LogMessageVerb(X_INFO, 10, 2016747b715Smrg "config/hal: getting usb.product_id on %s " 2026747b715Smrg "returned %04x\n", parent, usb_product); 2036747b715Smrg if (usb_vendor && usb_product) 2049ace9065Smrg if (asprintf(&attrs.usb_id, "%04x:%04x", usb_vendor, usb_product) 20535c4bbdfSmrg == -1) 20635c4bbdfSmrg attrs.usb_id = NULL; 2076747b715Smrg 20835c4bbdfSmrg attrs.pnp_id = get_prop_string(hal_ctx, parent, "pnp.id"); 20935c4bbdfSmrg old_parent = parent; 21005b261ecSmrg 21135c4bbdfSmrg while (!attrs.pnp_id && 21235c4bbdfSmrg (parent = get_prop_string(hal_ctx, parent, "info.parent"))) { 21335c4bbdfSmrg attrs.pnp_id = get_prop_string(hal_ctx, parent, "pnp.id"); 21435c4bbdfSmrg 21535c4bbdfSmrg free(old_parent); 21635c4bbdfSmrg old_parent = parent; 21735c4bbdfSmrg } 21835c4bbdfSmrg 21935c4bbdfSmrg free(old_parent); 22005b261ecSmrg } 22105b261ecSmrg 22235c4bbdfSmrg input_options = input_option_new(NULL, "_source", "server/hal"); 22335c4bbdfSmrg if (!input_options) { 22435c4bbdfSmrg LogMessage(X_ERROR, 22535c4bbdfSmrg "config/hal: couldn't allocate first key/value pair\n"); 22605b261ecSmrg goto unwind; 22705b261ecSmrg } 22805b261ecSmrg 2294642e01fSmrg /* most drivers use device.. not path. evdev uses both however, but the 2304642e01fSmrg * path version isn't documented apparently. support both for now. */ 23135c4bbdfSmrg input_options = input_option_new(input_options, "path", path); 23235c4bbdfSmrg input_options = input_option_new(input_options, "device", path); 2334642e01fSmrg 23435c4bbdfSmrg input_options = input_option_new(input_options, "driver", driver); 23535c4bbdfSmrg input_options = input_option_new(input_options, "name", name); 2364642e01fSmrg 23735c4bbdfSmrg if (asprintf(&config_info, "hal:%s", udi) == -1) { 2389ace9065Smrg config_info = NULL; 2394642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't allocate name\n"); 24005b261ecSmrg goto unwind; 2414642e01fSmrg } 24205b261ecSmrg 2434642e01fSmrg /* Check for duplicate devices */ 24435c4bbdfSmrg if (device_is_duplicate(config_info)) { 24535c4bbdfSmrg LogMessage(X_WARNING, 24635c4bbdfSmrg "config/hal: device %s already added. Ignoring.\n", name); 2474642e01fSmrg goto unwind; 2484642e01fSmrg } 2494642e01fSmrg 2504642e01fSmrg /* ok, grab options from hal.. iterate through all properties 25135c4bbdfSmrg * and lets see if any of them are options that we can add */ 2524642e01fSmrg set = libhal_device_get_all_properties(hal_ctx, udi, &error); 2534642e01fSmrg 2544642e01fSmrg if (!set) { 25535c4bbdfSmrg LogMessage(X_ERROR, 25635c4bbdfSmrg "config/hal: couldn't get property list for %s: %s (%s)\n", 25735c4bbdfSmrg udi, error.name, error.message); 2584642e01fSmrg goto unwind; 2594642e01fSmrg } 2604642e01fSmrg 26135c4bbdfSmrg libhal_psi_init(&set_iter, set); 2624642e01fSmrg while (libhal_psi_has_more(&set_iter)) { 2634642e01fSmrg /* we are looking for supported keys.. extract and add to options */ 2644642e01fSmrg psi_key = libhal_psi_get_key(&set_iter); 2654642e01fSmrg 26635c4bbdfSmrg if (psi_key) { 2674642e01fSmrg 2684642e01fSmrg /* normal options first (input.x11_options.<propname>) */ 26935c4bbdfSmrg if (!strncasecmp 27035c4bbdfSmrg (psi_key, LIBHAL_PROP_KEY, sizeof(LIBHAL_PROP_KEY) - 1)) { 27135c4bbdfSmrg char *tmp; 2724642e01fSmrg 2734642e01fSmrg /* only support strings for all values */ 2744642e01fSmrg tmp_val = get_prop_string(hal_ctx, udi, psi_key); 2754642e01fSmrg 27635c4bbdfSmrg if (tmp_val) { 2774642e01fSmrg 2784642e01fSmrg /* xkb needs special handling. HAL specs include 2794642e01fSmrg * input.xkb.xyz options, but the x11-input.fdi specifies 2804642e01fSmrg * input.x11_options.Xkbxyz options. By default, we use 2814642e01fSmrg * the former, unless the specific X11 ones are specified. 2824642e01fSmrg * Since we can't predict the order in which the keys 2834642e01fSmrg * arrive, we need to store them. 2844642e01fSmrg */ 28535c4bbdfSmrg if ((tmp = strcasestr(psi_key, "xkb")) && strlen(tmp) >= 4) { 28635c4bbdfSmrg if (!strcasecmp(&tmp[3], "layout")) { 2876747b715Smrg free(xkb_opts.layout); 2884642e01fSmrg xkb_opts.layout = strdup(tmp_val); 28935c4bbdfSmrg } 29035c4bbdfSmrg else if (!strcasecmp(&tmp[3], "model")) { 2916747b715Smrg free(xkb_opts.model); 2924642e01fSmrg xkb_opts.model = strdup(tmp_val); 29335c4bbdfSmrg } 29435c4bbdfSmrg else if (!strcasecmp(&tmp[3], "rules")) { 2956747b715Smrg free(xkb_opts.rules); 2964642e01fSmrg xkb_opts.rules = strdup(tmp_val); 29735c4bbdfSmrg } 29835c4bbdfSmrg else if (!strcasecmp(&tmp[3], "variant")) { 2996747b715Smrg free(xkb_opts.variant); 3004642e01fSmrg xkb_opts.variant = strdup(tmp_val); 30135c4bbdfSmrg } 30235c4bbdfSmrg else if (!strcasecmp(&tmp[3], "options")) { 3036747b715Smrg free(xkb_opts.options); 3044642e01fSmrg xkb_opts.options = strdup(tmp_val); 3054642e01fSmrg } 30635c4bbdfSmrg } 30735c4bbdfSmrg else { 3084642e01fSmrg /* all others */ 30935c4bbdfSmrg input_options = 31035c4bbdfSmrg input_option_new(input_options, 31135c4bbdfSmrg psi_key + sizeof(LIBHAL_PROP_KEY) - 31235c4bbdfSmrg 1, tmp_val); 3136747b715Smrg free(tmp_val); 3144642e01fSmrg } 31535c4bbdfSmrg } 31635c4bbdfSmrg else { 3174642e01fSmrg /* server 1.4 had xkb_options as strlist. */ 3184642e01fSmrg if ((tmp = strcasestr(psi_key, "xkb")) && 3194642e01fSmrg (strlen(tmp) >= 4) && 3204642e01fSmrg (!strcasecmp(&tmp[3], "options")) && 32135c4bbdfSmrg (tmp_val = 32235c4bbdfSmrg get_prop_string_array(hal_ctx, udi, psi_key))) { 3236747b715Smrg free(xkb_opts.options); 3244642e01fSmrg xkb_opts.options = strdup(tmp_val); 3254642e01fSmrg } 3264642e01fSmrg } 32735c4bbdfSmrg } 32835c4bbdfSmrg else if (!strncasecmp 32935c4bbdfSmrg (psi_key, LIBHAL_XKB_PROP_KEY, 33035c4bbdfSmrg sizeof(LIBHAL_XKB_PROP_KEY) - 1)) { 33135c4bbdfSmrg char *tmp; 3324642e01fSmrg 3334642e01fSmrg /* only support strings for all values */ 3344642e01fSmrg tmp_val = get_prop_string(hal_ctx, udi, psi_key); 3354642e01fSmrg 3364642e01fSmrg if (tmp_val && strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) { 3374642e01fSmrg 3384642e01fSmrg tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1]; 3394642e01fSmrg 34035c4bbdfSmrg if (!strcasecmp(tmp, "layout")) { 3414642e01fSmrg if (!xkb_opts.layout) 3424642e01fSmrg xkb_opts.layout = strdup(tmp_val); 34335c4bbdfSmrg } 34435c4bbdfSmrg else if (!strcasecmp(tmp, "rules")) { 3454642e01fSmrg if (!xkb_opts.rules) 3464642e01fSmrg xkb_opts.rules = strdup(tmp_val); 34735c4bbdfSmrg } 34835c4bbdfSmrg else if (!strcasecmp(tmp, "variant")) { 3494642e01fSmrg if (!xkb_opts.variant) 3504642e01fSmrg xkb_opts.variant = strdup(tmp_val); 35135c4bbdfSmrg } 35235c4bbdfSmrg else if (!strcasecmp(tmp, "model")) { 3534642e01fSmrg if (!xkb_opts.model) 3544642e01fSmrg xkb_opts.model = strdup(tmp_val); 35535c4bbdfSmrg } 35635c4bbdfSmrg else if (!strcasecmp(tmp, "options")) { 3574642e01fSmrg if (!xkb_opts.options) 3584642e01fSmrg xkb_opts.options = strdup(tmp_val); 3594642e01fSmrg } 3606747b715Smrg free(tmp_val); 36135c4bbdfSmrg } 36235c4bbdfSmrg else { 3634642e01fSmrg /* server 1.4 had xkb options as strlist */ 3644642e01fSmrg tmp_val = get_prop_string_array(hal_ctx, udi, psi_key); 36535c4bbdfSmrg if (tmp_val && 36635c4bbdfSmrg strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) { 3674642e01fSmrg tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1]; 3684642e01fSmrg if (!strcasecmp(tmp, ".options") && (!xkb_opts.options)) 3694642e01fSmrg xkb_opts.options = strdup(tmp_val); 3704642e01fSmrg } 371475c125cSmrg free(tmp_val); 3724642e01fSmrg } 3734642e01fSmrg } 3744642e01fSmrg } 3754642e01fSmrg 3764642e01fSmrg /* psi_key doesn't need to be freed */ 3774642e01fSmrg libhal_psi_next(&set_iter); 3784642e01fSmrg } 3794642e01fSmrg 3804642e01fSmrg /* Now add xkb options */ 3814642e01fSmrg if (xkb_opts.layout) 38235c4bbdfSmrg input_options = 38335c4bbdfSmrg input_option_new(input_options, "xkb_layout", xkb_opts.layout); 3844642e01fSmrg if (xkb_opts.rules) 38535c4bbdfSmrg input_options = 38635c4bbdfSmrg input_option_new(input_options, "xkb_rules", xkb_opts.rules); 3874642e01fSmrg if (xkb_opts.variant) 38835c4bbdfSmrg input_options = 38935c4bbdfSmrg input_option_new(input_options, "xkb_variant", xkb_opts.variant); 3904642e01fSmrg if (xkb_opts.model) 39135c4bbdfSmrg input_options = 39235c4bbdfSmrg input_option_new(input_options, "xkb_model", xkb_opts.model); 3934642e01fSmrg if (xkb_opts.options) 39435c4bbdfSmrg input_options = 39535c4bbdfSmrg input_option_new(input_options, "xkb_options", xkb_opts.options); 39635c4bbdfSmrg input_options = input_option_new(input_options, "config_info", config_info); 3974642e01fSmrg 3984642e01fSmrg /* this isn't an error, but how else do you output something that the user can see? */ 3994642e01fSmrg LogMessage(X_INFO, "config/hal: Adding input device %s\n", name); 40035c4bbdfSmrg if ((rc = NewInputDeviceRequest(input_options, &attrs, &dev)) != Success) { 40135c4bbdfSmrg LogMessage(X_ERROR, "config/hal: NewInputDeviceRequest failed (%d)\n", 40235c4bbdfSmrg rc); 40305b261ecSmrg dev = NULL; 40405b261ecSmrg goto unwind; 40505b261ecSmrg } 40605b261ecSmrg 40735c4bbdfSmrg unwind: 4084642e01fSmrg if (set) 4094642e01fSmrg libhal_free_property_set(set); 4106747b715Smrg free(path); 4116747b715Smrg free(driver); 4126747b715Smrg free(name); 4136747b715Smrg free(config_info); 41435c4bbdfSmrg input_option_free_list(&input_options); 41505b261ecSmrg 4166747b715Smrg free(attrs.product); 4176747b715Smrg free(attrs.vendor); 4186747b715Smrg free(attrs.device); 4196747b715Smrg free(attrs.pnp_id); 4206747b715Smrg free(attrs.usb_id); 4216747b715Smrg if (attrs.tags) { 4226747b715Smrg char **tag = attrs.tags; 42335c4bbdfSmrg 4246747b715Smrg while (*tag) { 4256747b715Smrg free(*tag); 4266747b715Smrg tag++; 4276747b715Smrg } 4286747b715Smrg free(attrs.tags); 4296747b715Smrg } 4306747b715Smrg 4316747b715Smrg free(xkb_opts.layout); 4326747b715Smrg free(xkb_opts.rules); 4336747b715Smrg free(xkb_opts.model); 4346747b715Smrg free(xkb_opts.variant); 4356747b715Smrg free(xkb_opts.options); 4364642e01fSmrg 43705b261ecSmrg dbus_error_free(&error); 43805b261ecSmrg 43905b261ecSmrg return; 44005b261ecSmrg} 44105b261ecSmrg 44205b261ecSmrgstatic void 44305b261ecSmrgdisconnect_hook(void *data) 44405b261ecSmrg{ 44505b261ecSmrg DBusError error; 44605b261ecSmrg struct config_hal_info *info = data; 44705b261ecSmrg 44805b261ecSmrg if (info->hal_ctx) { 4494642e01fSmrg if (dbus_connection_get_is_connected(info->system_bus)) { 4504642e01fSmrg dbus_error_init(&error); 4514642e01fSmrg if (!libhal_ctx_shutdown(info->hal_ctx, &error)) 45235c4bbdfSmrg LogMessage(X_WARNING, 45335c4bbdfSmrg "config/hal: disconnect_hook couldn't shut down context: %s (%s)\n", 45435c4bbdfSmrg error.name, error.message); 4554642e01fSmrg dbus_error_free(&error); 4564642e01fSmrg } 45705b261ecSmrg libhal_ctx_free(info->hal_ctx); 45805b261ecSmrg } 45905b261ecSmrg 46005b261ecSmrg info->hal_ctx = NULL; 46105b261ecSmrg info->system_bus = NULL; 46205b261ecSmrg} 46305b261ecSmrg 4644642e01fSmrgstatic BOOL 46535c4bbdfSmrgconnect_and_register(DBusConnection * connection, struct config_hal_info *info) 46605b261ecSmrg{ 46705b261ecSmrg DBusError error; 46805b261ecSmrg char **devices; 46905b261ecSmrg int num_devices, i; 47005b261ecSmrg 471b86d567bSmrg if (info->hal_ctx) 47235c4bbdfSmrg return TRUE; /* already registered, pretend we did something */ 473b86d567bSmrg 47405b261ecSmrg info->system_bus = connection; 47505b261ecSmrg 47605b261ecSmrg dbus_error_init(&error); 47705b261ecSmrg 4784642e01fSmrg info->hal_ctx = libhal_ctx_new(); 47905b261ecSmrg if (!info->hal_ctx) { 4804642e01fSmrg LogMessage(X_ERROR, "config/hal: couldn't create HAL context\n"); 48105b261ecSmrg goto out_err; 48205b261ecSmrg } 48305b261ecSmrg 48405b261ecSmrg if (!libhal_ctx_set_dbus_connection(info->hal_ctx, info->system_bus)) { 48535c4bbdfSmrg LogMessage(X_ERROR, 48635c4bbdfSmrg "config/hal: couldn't associate HAL context with bus\n"); 487b1d344b3Smrg goto out_err; 48805b261ecSmrg } 48905b261ecSmrg if (!libhal_ctx_init(info->hal_ctx, &error)) { 49035c4bbdfSmrg LogMessage(X_ERROR, 49135c4bbdfSmrg "config/hal: couldn't initialise context: %s (%s)\n", 49235c4bbdfSmrg error.name ? error.name : "unknown error", 49335c4bbdfSmrg error.message ? error.message : "null"); 494b1d344b3Smrg goto out_err; 49505b261ecSmrg } 49605b261ecSmrg if (!libhal_device_property_watch_all(info->hal_ctx, &error)) { 49735c4bbdfSmrg LogMessage(X_ERROR, 49835c4bbdfSmrg "config/hal: couldn't watch all properties: %s (%s)\n", 49935c4bbdfSmrg error.name ? error.name : "unknown error", 50035c4bbdfSmrg error.message ? error.message : "null"); 501b86d567bSmrg goto out_ctx; 50205b261ecSmrg } 50305b261ecSmrg libhal_ctx_set_device_added(info->hal_ctx, device_added); 50405b261ecSmrg libhal_ctx_set_device_removed(info->hal_ctx, device_removed); 50505b261ecSmrg 50605b261ecSmrg devices = libhal_find_device_by_capability(info->hal_ctx, "input", 50705b261ecSmrg &num_devices, &error); 50805b261ecSmrg /* FIXME: Get default devices if error is set. */ 509b86d567bSmrg if (dbus_error_is_set(&error)) { 510b86d567bSmrg LogMessage(X_ERROR, "config/hal: couldn't find input device: %s (%s)\n", 51135c4bbdfSmrg error.name ? error.name : "unknown error", 51235c4bbdfSmrg error.message ? error.message : "null"); 513b86d567bSmrg goto out_ctx; 514b86d567bSmrg } 51505b261ecSmrg for (i = 0; i < num_devices; i++) 51605b261ecSmrg device_added(info->hal_ctx, devices[i]); 51705b261ecSmrg libhal_free_string_array(devices); 51805b261ecSmrg 51905b261ecSmrg dbus_error_free(&error); 52005b261ecSmrg 5214642e01fSmrg return TRUE; 52205b261ecSmrg 52335c4bbdfSmrg out_ctx: 524b86d567bSmrg dbus_error_free(&error); 525b86d567bSmrg 526b1d344b3Smrg if (!libhal_ctx_shutdown(info->hal_ctx, &error)) { 52735c4bbdfSmrg LogMessage(X_WARNING, 52835c4bbdfSmrg "config/hal: couldn't shut down context: %s (%s)\n", 52935c4bbdfSmrg error.name ? error.name : "unknown error", 53035c4bbdfSmrg error.message ? error.message : "null"); 531b1d344b3Smrg dbus_error_free(&error); 532b86d567bSmrg } 533b86d567bSmrg 53435c4bbdfSmrg out_err: 53505b261ecSmrg dbus_error_free(&error); 53605b261ecSmrg 537b1d344b3Smrg if (info->hal_ctx) { 538b1d344b3Smrg libhal_ctx_free(info->hal_ctx); 539b1d344b3Smrg } 540b1d344b3Smrg 54105b261ecSmrg info->hal_ctx = NULL; 54205b261ecSmrg info->system_bus = NULL; 54305b261ecSmrg 5444642e01fSmrg return FALSE; 5454642e01fSmrg} 5464642e01fSmrg 5474642e01fSmrg/** 5484642e01fSmrg * Handle NewOwnerChanged signals to deal with HAL startup at X server runtime. 5494642e01fSmrg * 5504642e01fSmrg * NewOwnerChanged is send once when HAL shuts down, and once again when it 5514642e01fSmrg * comes back up. Message has three arguments, first is the name 5524642e01fSmrg * (org.freedesktop.Hal), the second one is the old owner, third one is new 5534642e01fSmrg * owner. 5544642e01fSmrg */ 5554642e01fSmrgstatic DBusHandlerResult 55635c4bbdfSmrgownerchanged_handler(DBusConnection * connection, DBusMessage * message, 55735c4bbdfSmrg void *data) 5584642e01fSmrg{ 5594642e01fSmrg int ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 5604642e01fSmrg 5614642e01fSmrg if (dbus_message_is_signal(message, 56235c4bbdfSmrg "org.freedesktop.DBus", "NameOwnerChanged")) { 5634642e01fSmrg DBusError error; 5644642e01fSmrg char *name, *old_owner, *new_owner; 5654642e01fSmrg 5664642e01fSmrg dbus_error_init(&error); 5674642e01fSmrg dbus_message_get_args(message, &error, 5684642e01fSmrg DBUS_TYPE_STRING, &name, 5694642e01fSmrg DBUS_TYPE_STRING, &old_owner, 57035c4bbdfSmrg DBUS_TYPE_STRING, &new_owner, DBUS_TYPE_INVALID); 5714642e01fSmrg 5724642e01fSmrg if (dbus_error_is_set(&error)) { 57335c4bbdfSmrg ErrorF 57435c4bbdfSmrg ("[config/hal] failed to get NameOwnerChanged args: %s (%s)\n", 57535c4bbdfSmrg error.name, error.message); 57635c4bbdfSmrg } 57735c4bbdfSmrg else if (name && strcmp(name, "org.freedesktop.Hal") == 0) { 5784642e01fSmrg 5794642e01fSmrg if (!old_owner || !strlen(old_owner)) { 5804642e01fSmrg DebugF("[config/hal] HAL startup detected.\n"); 58135c4bbdfSmrg if (connect_and_register 58235c4bbdfSmrg (connection, (struct config_hal_info *) data)) 5834642e01fSmrg dbus_connection_unregister_object_path(connection, 58435c4bbdfSmrg "/org/freedesktop/DBus"); 5854642e01fSmrg else 5864642e01fSmrg ErrorF("[config/hal] Failed to connect to HAL bus.\n"); 5874642e01fSmrg } 5884642e01fSmrg 5894642e01fSmrg ret = DBUS_HANDLER_RESULT_HANDLED; 5904642e01fSmrg } 5914642e01fSmrg dbus_error_free(&error); 5924642e01fSmrg } 5934642e01fSmrg 5944642e01fSmrg return ret; 5954642e01fSmrg} 5964642e01fSmrg 5974642e01fSmrg/** 5984642e01fSmrg * Register a handler for the NameOwnerChanged signal. 5994642e01fSmrg */ 6004642e01fSmrgstatic BOOL 60135c4bbdfSmrglisten_for_startup(DBusConnection * connection, void *data) 6024642e01fSmrg{ 60335c4bbdfSmrg DBusObjectPathVTable vtable = {.message_function = ownerchanged_handler, }; 6044642e01fSmrg DBusError error; 6054642e01fSmrg const char MATCH_RULE[] = "sender='org.freedesktop.DBus'," 60635c4bbdfSmrg "interface='org.freedesktop.DBus'," 60735c4bbdfSmrg "type='signal'," 60835c4bbdfSmrg "path='/org/freedesktop/DBus'," "member='NameOwnerChanged'"; 6094642e01fSmrg int rc = FALSE; 6104642e01fSmrg 6114642e01fSmrg dbus_error_init(&error); 6124642e01fSmrg dbus_bus_add_match(connection, MATCH_RULE, &error); 6134642e01fSmrg if (!dbus_error_is_set(&error)) { 6144642e01fSmrg if (dbus_connection_register_object_path(connection, 61535c4bbdfSmrg "/org/freedesktop/DBus", 61635c4bbdfSmrg &vtable, data)) 6174642e01fSmrg rc = TRUE; 6184642e01fSmrg else 6194642e01fSmrg ErrorF("[config/hal] cannot register object path.\n"); 62035c4bbdfSmrg } 62135c4bbdfSmrg else { 6224642e01fSmrg ErrorF("[config/hal] couldn't add match rule: %s (%s)\n", error.name, 62335c4bbdfSmrg error.message); 6244642e01fSmrg ErrorF("[config/hal] cannot detect a HAL startup.\n"); 6254642e01fSmrg } 6264642e01fSmrg 6274642e01fSmrg dbus_error_free(&error); 6284642e01fSmrg 6294642e01fSmrg return rc; 6304642e01fSmrg} 6314642e01fSmrg 6324642e01fSmrgstatic void 63335c4bbdfSmrgconnect_hook(DBusConnection * connection, void *data) 6344642e01fSmrg{ 6354642e01fSmrg struct config_hal_info *info = data; 6364642e01fSmrg 6374642e01fSmrg if (listen_for_startup(connection, data) && 6384642e01fSmrg connect_and_register(connection, info)) 6394642e01fSmrg dbus_connection_unregister_object_path(connection, 6404642e01fSmrg "/org/freedesktop/DBus"); 6414642e01fSmrg 64205b261ecSmrg return; 64305b261ecSmrg} 64405b261ecSmrg 64505b261ecSmrgstatic struct config_hal_info hal_info; 64635c4bbdfSmrg 64735c4bbdfSmrgstatic struct dbus_core_hook hook = { 64805b261ecSmrg .connect = connect_hook, 64905b261ecSmrg .disconnect = disconnect_hook, 65005b261ecSmrg .data = &hal_info, 65105b261ecSmrg}; 65205b261ecSmrg 65305b261ecSmrgint 65405b261ecSmrgconfig_hal_init(void) 65505b261ecSmrg{ 65605b261ecSmrg memset(&hal_info, 0, sizeof(hal_info)); 65705b261ecSmrg hal_info.system_bus = NULL; 65805b261ecSmrg hal_info.hal_ctx = NULL; 65905b261ecSmrg 66035c4bbdfSmrg if (!dbus_core_add_hook(&hook)) { 6614642e01fSmrg LogMessage(X_ERROR, "config/hal: failed to add D-Bus hook\n"); 66205b261ecSmrg return 0; 66305b261ecSmrg } 66405b261ecSmrg 6654642e01fSmrg /* verbose message */ 66635c4bbdfSmrg LogMessageVerb(X_INFO, 7, "config/hal: initialized\n"); 6674642e01fSmrg 66805b261ecSmrg return 1; 66905b261ecSmrg} 67005b261ecSmrg 67105b261ecSmrgvoid 67205b261ecSmrgconfig_hal_fini(void) 67305b261ecSmrg{ 67435c4bbdfSmrg dbus_core_remove_hook(&hook); 67505b261ecSmrg} 676