/* * Copyright 1995-1999 by Frederic Lepied, France. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Frederic Lepied not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Frederic Lepied makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * FREDERIC LEPIED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL FREDERIC LEPIED BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * */ /* * Copyright (c) 2000-2002 by The XFree86 Project, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of the copyright holder(s) * and author(s) shall not be used in advertising or otherwise to promote * the sale, use or other dealings in this Software without prior written * authorization from the copyright holder(s) and author(s). */ #ifdef HAVE_XORG_CONFIG_H #include #endif #include #include #include #include #include #include "xf86.h" #include "xf86Priv.h" #include "xf86Config.h" #include "xf86Xinput.h" #include "xf86Optrec.h" #include "mipointer.h" #include "extinit.h" #include "loaderProcs.h" #include "systemd-logind.h" #include "exevents.h" /* AddInputDevice */ #include "exglobals.h" #include "eventstr.h" #include "inpututils.h" #include "optionstr.h" #include /* InputClassMatches */ #ifdef HAVE_FNMATCH_H #include #endif #ifdef HAVE_SYS_UTSNAME_H #include #endif #include #include /* for int64_t */ #include #include #include #ifdef HAVE_SYS_SYSMACROS_H #include #endif #ifdef HAVE_SYS_MKDEV_H #include /* for major() & minor() on Solaris */ #endif #include "mi.h" #include /* dix pointer acceleration */ #include #ifdef XFreeXDGA #include "dgaproc.h" #endif #include "xkbsrv.h" /* Valuator verification macro */ #define XI_VERIFY_VALUATORS(num_valuators) \ if (num_valuators > MAX_VALUATORS) { \ xf86Msg(X_ERROR, "%s: num_valuator %d is greater than" \ " MAX_VALUATORS\n", __FUNCTION__, num_valuators); \ return; \ } static int xf86InputDevicePostInit(DeviceIntPtr dev); typedef struct { struct xorg_list node; InputInfoPtr pInfo; } PausedInputDeviceRec; typedef PausedInputDeviceRec *PausedInputDevicePtr; static struct xorg_list new_input_devices_list = { .next = &new_input_devices_list, .prev = &new_input_devices_list, }; /** * Eval config and modify DeviceVelocityRec accordingly */ static void ProcessVelocityConfiguration(DeviceIntPtr pDev, const char *devname, void *list, DeviceVelocityPtr s) { int tempi; float tempf; Atom float_prop = XIGetKnownProperty(XATOM_FLOAT); Atom prop; if (!s) return; /* common settings (available via device properties) */ tempf = xf86SetRealOption(list, "ConstantDeceleration", 1.0); if (tempf != 1.0) { xf86Msg(X_CONFIG, "%s: (accel) constant deceleration by %.1f\n", devname, tempf); prop = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION); XIChangeDeviceProperty(pDev, prop, float_prop, 32, PropModeReplace, 1, &tempf, FALSE); } tempf = xf86SetRealOption(list, "AdaptiveDeceleration", 1.0); if (tempf > 1.0) { xf86Msg(X_CONFIG, "%s: (accel) adaptive deceleration by %.1f\n", devname, tempf); prop = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION); XIChangeDeviceProperty(pDev, prop, float_prop, 32, PropModeReplace, 1, &tempf, FALSE); } /* select profile by number */ tempi = xf86SetIntOption(list, "AccelerationProfile", s->statistics.profile_number); prop = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER); if (XIChangeDeviceProperty(pDev, prop, XA_INTEGER, 32, PropModeReplace, 1, &tempi, FALSE) == Success) { xf86Msg(X_CONFIG, "%s: (accel) acceleration profile %i\n", devname, tempi); } else { xf86Msg(X_CONFIG, "%s: (accel) acceleration profile %i is unknown\n", devname, tempi); } /* set scaling */ tempf = xf86SetRealOption(list, "ExpectedRate", 0); prop = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING); if (tempf > 0) { tempf = 1000.0 / tempf; XIChangeDeviceProperty(pDev, prop, float_prop, 32, PropModeReplace, 1, &tempf, FALSE); } else { tempf = xf86SetRealOption(list, "VelocityScale", s->corr_mul); XIChangeDeviceProperty(pDev, prop, float_prop, 32, PropModeReplace, 1, &tempf, FALSE); } tempi = xf86SetIntOption(list, "VelocityTrackerCount", -1); if (tempi > 1) InitTrackers(s, tempi); s->initial_range = xf86SetIntOption(list, "VelocityInitialRange", s->initial_range); s->max_diff = xf86SetRealOption(list, "VelocityAbsDiff", s->max_diff); tempf = xf86SetRealOption(list, "VelocityRelDiff", -1); if (tempf >= 0) { xf86Msg(X_CONFIG, "%s: (accel) max rel. velocity difference: %.1f%%\n", devname, tempf * 100.0); s->max_rel_diff = tempf; } /* Configure softening. If const deceleration is used, this is expected * to provide better subpixel information so we enable * softening by default only if ConstantDeceleration is not used */ s->use_softening = xf86SetBoolOption(list, "Softening", s->const_acceleration == 1.0); s->average_accel = xf86SetBoolOption(list, "AccelerationProfileAveraging", s->average_accel); s->reset_time = xf86SetIntOption(list, "VelocityReset", s->reset_time); } static void ApplyAccelerationSettings(DeviceIntPtr dev) { int scheme, i; DeviceVelocityPtr pVel; InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate; char *schemeStr; if (dev->valuator && dev->ptrfeed) { schemeStr = xf86SetStrOption(pInfo->options, "AccelerationScheme", ""); scheme = dev->valuator->accelScheme.number; if (!xf86NameCmp(schemeStr, "predictable")) scheme = PtrAccelPredictable; if (!xf86NameCmp(schemeStr, "lightweight")) scheme = PtrAccelLightweight; if (!xf86NameCmp(schemeStr, "none")) scheme = PtrAccelNoOp; /* reinit scheme if needed */ if (dev->valuator->accelScheme.number != scheme) { if (dev->valuator->accelScheme.AccelCleanupProc) { dev->valuator->accelScheme.AccelCleanupProc(dev); } if (InitPointerAccelerationScheme(dev, scheme)) { xf86Msg(X_CONFIG, "%s: (accel) selected scheme %s/%i\n", pInfo->name, schemeStr, scheme); } else { xf86Msg(X_CONFIG, "%s: (accel) could not init scheme %s\n", pInfo->name, schemeStr); scheme = dev->valuator->accelScheme.number; } } else { xf86Msg(X_CONFIG, "%s: (accel) keeping acceleration scheme %i\n", pInfo->name, scheme); } free(schemeStr); /* process special configuration */ switch (scheme) { case PtrAccelPredictable: pVel = GetDevicePredictableAccelData(dev); ProcessVelocityConfiguration(dev, pInfo->name, pInfo->options, pVel); break; } i = xf86SetIntOption(pInfo->options, "AccelerationNumerator", dev->ptrfeed->ctrl.num); if (i >= 0) dev->ptrfeed->ctrl.num = i; i = xf86SetIntOption(pInfo->options, "AccelerationDenominator", dev->ptrfeed->ctrl.den); if (i > 0) dev->ptrfeed->ctrl.den = i; i = xf86SetIntOption(pInfo->options, "AccelerationThreshold", dev->ptrfeed->ctrl.threshold); if (i >= 0) dev->ptrfeed->ctrl.threshold = i; xf86Msg(X_CONFIG, "%s: (accel) acceleration factor: %.3f\n", pInfo->name, ((float) dev->ptrfeed->ctrl.num) / ((float) dev->ptrfeed->ctrl.den)); xf86Msg(X_CONFIG, "%s: (accel) acceleration threshold: %i\n", pInfo->name, dev->ptrfeed->ctrl.threshold); } } static void ApplyTransformationMatrix(DeviceIntPtr dev) { InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate; char *str; int rc; float matrix[9] = { 0 }; if (!dev->valuator) return; str = xf86SetStrOption(pInfo->options, "TransformationMatrix", NULL); if (!str) return; rc = sscanf(str, "%f %f %f %f %f %f %f %f %f", &matrix[0], &matrix[1], &matrix[2], &matrix[3], &matrix[4], &matrix[5], &matrix[6], &matrix[7], &matrix[8]); if (rc != 9) { xf86Msg(X_ERROR, "%s: invalid format for transformation matrix. Ignoring configuration.\n", pInfo->name); return; } XIChangeDeviceProperty(dev, XIGetKnownProperty(XI_PROP_TRANSFORM), XIGetKnownProperty(XATOM_FLOAT), 32, PropModeReplace, 9, matrix, FALSE); } /*********************************************************************** * * xf86ProcessCommonOptions -- * * Process global options. * *********************************************************************** */ void xf86ProcessCommonOptions(InputInfoPtr pInfo, XF86OptionPtr list) { if (xf86SetBoolOption(list, "Floating", 0) || !xf86SetBoolOption(list, "AlwaysCore", 1) || !xf86SetBoolOption(list, "SendCoreEvents", 1) || !xf86SetBoolOption(list, "CorePointer", 1) || !xf86SetBoolOption(list, "CoreKeyboard", 1)) { xf86Msg(X_CONFIG, "%s: doesn't report core events\n", pInfo->name); } else { pInfo->flags |= XI86_ALWAYS_CORE; xf86Msg(X_CONFIG, "%s: always reports core events\n", pInfo->name); } } /*********************************************************************** * * xf86ActivateDevice -- * * Initialize an input device. * * Returns TRUE on success, or FALSE otherwise. *********************************************************************** */ static DeviceIntPtr xf86ActivateDevice(InputInfoPtr pInfo) { DeviceIntPtr dev; Atom atom; dev = AddInputDevice(serverClient, pInfo->device_control, TRUE); if (dev == NULL) { xf86Msg(X_ERROR, "Too many input devices. Ignoring %s\n", pInfo->name); pInfo->dev = NULL; return NULL; } atom = MakeAtom(pInfo->type_name, strlen(pInfo->type_name), TRUE); AssignTypeAndName(dev, atom, pInfo->name); dev->public.devicePrivate = pInfo; pInfo->dev = dev; dev->coreEvents = pInfo->flags & XI86_ALWAYS_CORE; dev->type = SLAVE; dev->spriteInfo->spriteOwner = FALSE; dev->config_info = xf86SetStrOption(pInfo->options, "config_info", NULL); if (serverGeneration == 1) xf86Msg(X_INFO, "XINPUT: Adding extended input device \"%s\" (type: %s, id %d)\n", pInfo->name, pInfo->type_name, dev->id); return dev; } /**************************************************************************** * * Caller: ProcXSetDeviceMode * * Change the mode of an extension device. * This function is used to change the mode of a device from reporting * relative motion to reporting absolute positional information, and * vice versa. * The default implementation below is that no such devices are supported. * *********************************************************************** */ int SetDeviceMode(ClientPtr client, DeviceIntPtr dev, int mode) { InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate; if (pInfo->switch_mode) { return (*pInfo->switch_mode) (client, dev, mode); } else return BadMatch; } /*********************************************************************** * * Caller: ProcXSetDeviceValuators * * Set the value of valuators on an extension input device. * This function is used to set the initial value of valuators on * those input devices that are capable of reporting either relative * motion or an absolute position, and allow an initial position to be set. * The default implementation below is that no such devices are supported. * *********************************************************************** */ int SetDeviceValuators(ClientPtr client, DeviceIntPtr dev, int *valuators, int first_valuator, int num_valuators) { InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate; if (pInfo->set_device_valuators) return (*pInfo->set_device_valuators) (pInfo, valuators, first_valuator, num_valuators); return BadMatch; } /*********************************************************************** * * Caller: ProcXChangeDeviceControl * * Change the specified device controls on an extension input device. * *********************************************************************** */ int ChangeDeviceControl(ClientPtr client, DeviceIntPtr dev, xDeviceCtl * control) { InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate; if (!pInfo->control_proc) { switch (control->control) { case DEVICE_CORE: case DEVICE_ABS_CALIB: case DEVICE_ABS_AREA: return BadMatch; case DEVICE_RESOLUTION: case DEVICE_ENABLE: return Success; default: return BadMatch; } } else { return (*pInfo->control_proc) (pInfo, control); } } /* * Get the operating system name from uname and store it statically to avoid * repeating the system call each time MatchOS is checked. */ static const char * HostOS(void) { #ifdef HAVE_SYS_UTSNAME_H struct utsname name; static char host_os[sizeof(name.sysname)] = ""; if (*host_os == '\0') { if (uname(&name) >= 0) strlcpy(host_os, name.sysname, sizeof(host_os)); else { strlcpy(host_os, "unknown", sizeof(host_os)); } } return host_os; #else return ""; #endif } static int match_substring(const char *attr, const char *pattern) { return (strstr(attr, pattern)) ? 0 : -1; } #ifdef HAVE_FNMATCH_H static int match_pattern(const char *attr, const char *pattern) { return fnmatch(pattern, attr, 0); } #else #define match_pattern match_substring #endif #ifdef HAVE_FNMATCH_H static int match_path_pattern(const char *attr, const char *pattern) { return fnmatch(pattern, attr, FNM_PATHNAME); } #else #define match_path_pattern match_substring #endif /* * If no Layout section is found, xf86ServerLayout.id becomes "(implicit)" * It is convenient that "" in patterns means "no explicit layout" */ static int match_string_implicit(const char *attr, const char *pattern) { if (strlen(pattern)) { return strcmp(attr, pattern); } else { return strcmp(attr, "(implicit)"); } } /* * Match an attribute against a list of NULL terminated arrays of patterns. * If a pattern in each list entry is matched, return TRUE. */ static Bool MatchAttrToken(const char *attr, struct xorg_list *patterns, int (*compare) (const char *attr, const char *pattern)) { const xf86MatchGroup *group; /* If there are no patterns, accept the match */ if (xorg_list_is_empty(patterns)) return TRUE; /* * Iterate the list of patterns ensuring each entry has a * match. Each list entry is a separate Match line of the same type. */ xorg_list_for_each_entry(group, patterns, entry) { char *const *cur; Bool is_negated = group->is_negated; Bool match = is_negated; /* If there's a pattern but no attribute, we reject the match for a * MatchFoo directive, and accept it for a NoMatchFoo directive */ if (!attr) return is_negated; for (cur = group->values; *cur; cur++) if ((*compare) (attr, *cur) == 0) { match = !is_negated; break; } if (!match) return FALSE; } /* All the entries in the list matched the attribute */ return TRUE; } /* * Classes without any Match statements match all devices. Otherwise, all * statements must match. */ static Bool InputClassMatches(const XF86ConfInputClassPtr iclass, const InputInfoPtr idev, const InputAttributes * attrs) { /* MatchProduct substring */ if (!MatchAttrToken (attrs->product, &iclass->match_product, match_substring)) return FALSE; /* MatchVendor substring */ if (!MatchAttrToken(attrs->vendor, &iclass->match_vendor, match_substring)) return FALSE; /* MatchDevicePath pattern */ if (!MatchAttrToken (attrs->device, &iclass->match_device, match_path_pattern)) return FALSE; /* MatchOS case-insensitive string */ if (!MatchAttrToken(HostOS(), &iclass->match_os, strcasecmp)) return FALSE; /* MatchPnPID pattern */ if (!MatchAttrToken(attrs->pnp_id, &iclass->match_pnpid, match_pattern)) return FALSE; /* MatchUSBID pattern */ if (!MatchAttrToken(attrs->usb_id, &iclass->match_usbid, match_pattern)) return FALSE; /* MatchDriver string */ if (!MatchAttrToken(idev->driver, &iclass->match_driver, strcmp)) return FALSE; /* * MatchTag string * See if any of the device's tags match any of the MatchTag tokens. */ if (!xorg_list_is_empty(&iclass->match_tag)) { char *const *tag; Bool match; if (!attrs->tags) return FALSE; for (tag = attrs->tags, match = FALSE; *tag; tag++) { if (MatchAttrToken(*tag, &iclass->match_tag, strcmp)) { match = TRUE; break; } } if (!match) return FALSE; } /* MatchLayout string */ if (!xorg_list_is_empty(&iclass->match_layout)) { if (!MatchAttrToken(xf86ConfigLayout.id, &iclass->match_layout, match_string_implicit)) return FALSE; } /* MatchIs* booleans */ if (iclass->is_keyboard.set && iclass->is_keyboard.val != ! !(attrs->flags & (ATTR_KEY|ATTR_KEYBOARD))) return FALSE; if (iclass->is_pointer.set && iclass->is_pointer.val != ! !(attrs->flags & ATTR_POINTER)) return FALSE; if (iclass->is_joystick.set && iclass->is_joystick.val != ! !(attrs->flags & ATTR_JOYSTICK)) return FALSE; if (iclass->is_tablet.set && iclass->is_tablet.val != ! !(attrs->flags & ATTR_TABLET)) return FALSE; if (iclass->is_tablet_pad.set && iclass->is_tablet_pad.val != ! !(attrs->flags & ATTR_TABLET_PAD)) return FALSE; if (iclass->is_touchpad.set && iclass->is_touchpad.val != ! !(attrs->flags & ATTR_TOUCHPAD)) return FALSE; if (iclass->is_touchscreen.set && iclass->is_touchscreen.val != ! !(attrs->flags & ATTR_TOUCHSCREEN)) return FALSE; return TRUE; } /* * Merge in any InputClass configurations. Options in each InputClass * section have more priority than the original device configuration as * well as any previous InputClass sections. */ static int MergeInputClasses(const InputInfoPtr idev, const InputAttributes * attrs) { XF86ConfInputClassPtr cl; XF86OptionPtr classopts; for (cl = xf86configptr->conf_inputclass_lst; cl; cl = cl->list.next) { if (!InputClassMatches(cl, idev, attrs)) continue; /* Collect class options and driver settings */ classopts = xf86optionListDup(cl->option_lst); if (cl->driver) { free((void *) idev->driver); idev->driver = xstrdup(cl->driver); if (!idev->driver) { xf86Msg(X_ERROR, "Failed to allocate memory while merging " "InputClass configuration"); return BadAlloc; } classopts = xf86ReplaceStrOption(classopts, "driver", idev->driver); } /* Apply options to device with InputClass settings preferred. */ xf86Msg(X_CONFIG, "%s: Applying InputClass \"%s\"\n", idev->name, cl->identifier); idev->options = xf86optionListMerge(idev->options, classopts); } return Success; } /* * Iterate the list of classes and look for Option "Ignore". Return the * value of the last matching class and holler when returning TRUE. */ static Bool IgnoreInputClass(const InputInfoPtr idev, const InputAttributes * attrs) { XF86ConfInputClassPtr cl; Bool ignore = FALSE; const char *ignore_class; for (cl = xf86configptr->conf_inputclass_lst; cl; cl = cl->list.next) { if (!InputClassMatches(cl, idev, attrs)) continue; if (xf86findOption(cl->option_lst, "Ignore")) { ignore = xf86CheckBoolOption(cl->option_lst, "Ignore", FALSE); ignore_class = cl->identifier; } } if (ignore) xf86Msg(X_CONFIG, "%s: Ignoring device from InputClass \"%s\"\n", idev->name, ignore_class); return ignore; } InputInfoPtr xf86AllocateInput(void) { InputInfoPtr pInfo; pInfo = calloc(sizeof(*pInfo), 1); if (!pInfo) return NULL; pInfo->fd = -1; pInfo->type_name = "UNKNOWN"; return pInfo; } /* Append InputInfoRec to the tail of xf86InputDevs. */ static void xf86AddInput(InputDriverPtr drv, InputInfoPtr pInfo) { InputInfoPtr *prev = NULL; pInfo->drv = drv; pInfo->module = DuplicateModule(drv->module, NULL); for (prev = &xf86InputDevs; *prev; prev = &(*prev)->next); *prev = pInfo; pInfo->next = NULL; xf86CollectInputOptions(pInfo, (const char **) drv->default_options); xf86OptionListReport(pInfo->options); xf86ProcessCommonOptions(pInfo, pInfo->options); } /* * Remove an entry from xf86InputDevs and free all the device's information. */ void xf86DeleteInput(InputInfoPtr pInp, int flags) { /* First check if the inputdev is valid. */ if (pInp == NULL) return; if (pInp->module) UnloadModule(pInp->module); /* This should *really* be handled in drv->UnInit(dev) call instead, but * if the driver forgets about it make sure we free it or at least crash * with flying colors */ free(pInp->private); FreeInputAttributes(pInp->attrs); if (pInp->flags & XI86_SERVER_FD) systemd_logind_release_fd(pInp->major, pInp->minor, pInp->fd); /* Remove the entry from the list. */ if (pInp == xf86InputDevs) xf86InputDevs = pInp->next; else { InputInfoPtr p = xf86InputDevs; while (p && p->next != pInp) p = p->next; if (p) p->next = pInp->next; /* Else the entry wasn't in the xf86InputDevs list (ignore this). */ } free((void *) pInp->driver); free((void *) pInp->name); xf86optionListFree(pInp->options); free(pInp); } /* * Apply backend-specific initialization. Invoked after ActivateDevice(), * i.e. after the driver successfully completed DEVICE_INIT and the device * is advertised. * @param dev the device * @return Success or an error code */ static int xf86InputDevicePostInit(DeviceIntPtr dev) { ApplyAccelerationSettings(dev); ApplyTransformationMatrix(dev); return Success; } static void xf86stat(const char *path, int *maj, int *min) { struct stat st; if (stat(path, &st) == -1) return; *maj = major(st.st_rdev); *min = minor(st.st_rdev); } static inline InputDriverPtr xf86LoadInputDriver(const char *driver_name) { InputDriverPtr drv = NULL; /* Memory leak for every attached device if we don't * test if the module is already loaded first */ drv = xf86LookupInputDriver(driver_name); if (!drv) { if (xf86LoadOneModule(driver_name, NULL)) drv = xf86LookupInputDriver(driver_name); } return drv; } /** * Create a new input device, activate and enable it. * * Possible return codes: * BadName .. a bad driver name was supplied. * BadImplementation ... The driver does not have a PreInit function. This * is a driver bug. * BadMatch .. device initialization failed. * BadAlloc .. too many input devices * * @param idev The device, already set up with identifier, driver, and the * options. * @param pdev Pointer to the new device, if Success was reported. * @param enable Enable the device after activating it. * * @return Success or an error code */ _X_INTERNAL int xf86NewInputDevice(InputInfoPtr pInfo, DeviceIntPtr *pdev, BOOL enable) { InputDriverPtr drv = NULL; DeviceIntPtr dev = NULL; Bool paused; int rval; char *path = NULL; drv = xf86LoadInputDriver(pInfo->driver); if (!drv) { xf86Msg(X_ERROR, "No input driver matching `%s'\n", pInfo->driver); if (strlen(FALLBACK_INPUT_DRIVER) > 0) { xf86Msg(X_INFO, "Falling back to input driver `%s'\n", FALLBACK_INPUT_DRIVER); drv = xf86LoadInputDriver(FALLBACK_INPUT_DRIVER); if (drv) { free(pInfo->driver); pInfo->driver = strdup(FALLBACK_INPUT_DRIVER); } } if (!drv) { rval = BadName; goto unwind; } } xf86Msg(X_INFO, "Using input driver '%s' for '%s'\n", drv->driverName, pInfo->name); if (!drv->PreInit) { xf86Msg(X_ERROR, "Input driver `%s' has no PreInit function (ignoring)\n", drv->driverName); rval = BadImplementation; goto unwind; } path = xf86CheckStrOption(pInfo->options, "Device", NULL); if (path && pInfo->major == 0 && pInfo->minor == 0) xf86stat(path, &pInfo->major, &pInfo->minor); if (path && (drv->capabilities & XI86_DRV_CAP_SERVER_FD)){ int fd = systemd_logind_take_fd(pInfo->major, pInfo->minor, path, &paused); if (fd != -1) { if (paused) { /* Put on new_input_devices list for delayed probe */ PausedInputDevicePtr new_device = xnfalloc(sizeof *new_device); new_device->pInfo = pInfo; xorg_list_append(&new_device->node, &new_input_devices_list); systemd_logind_release_fd(pInfo->major, pInfo->minor, fd); free(path); return BadMatch; } pInfo->fd = fd; pInfo->flags |= XI86_SERVER_FD; pInfo->options = xf86ReplaceIntOption(pInfo->options, "fd", fd); } } free(path); xf86AddInput(drv, pInfo); input_lock(); rval = drv->PreInit(drv, pInfo, 0); input_unlock(); if (rval != Success) { xf86Msg(X_ERROR, "PreInit returned %d for \"%s\"\n", rval, pInfo->name); goto unwind; } if (!(dev = xf86ActivateDevice(pInfo))) { rval = BadAlloc; goto unwind; } rval = ActivateDevice(dev, TRUE); if (rval != Success) { xf86Msg(X_ERROR, "Couldn't init device \"%s\"\n", pInfo->name); RemoveDevice(dev, TRUE); goto unwind; } rval = xf86InputDevicePostInit(dev); if (rval != Success) { xf86Msg(X_ERROR, "Couldn't post-init device \"%s\"\n", pInfo->name); RemoveDevice(dev, TRUE); goto unwind; } /* Enable it if it's properly initialised and we're currently in the VT */ if (enable && dev->inited && dev->startup && xf86VTOwner()) { input_lock(); EnableDevice(dev, TRUE); if (!dev->enabled) { xf86Msg(X_ERROR, "Couldn't init device \"%s\"\n", pInfo->name); RemoveDevice(dev, TRUE); rval = BadMatch; input_unlock(); goto unwind; } /* send enter/leave event, update sprite window */ CheckMotion(NULL, dev); input_unlock(); } *pdev = dev; return Success; unwind: if (pInfo) { if (drv && drv->UnInit) drv->UnInit(drv, pInfo, 0); else xf86DeleteInput(pInfo, 0); } return rval; } int NewInputDeviceRequest(InputOption *options, InputAttributes * attrs, DeviceIntPtr *pdev) { InputInfoPtr pInfo = NULL; InputOption *option = NULL; int rval = Success; int is_auto = 0; pInfo = xf86AllocateInput(); if (!pInfo) return BadAlloc; nt_list_for_each_entry(option, options, list.next) { const char *key = input_option_get_key(option); const char *value = input_option_get_value(option); if (strcasecmp(key, "driver") == 0) { if (pInfo->driver) { rval = BadRequest; goto unwind; } pInfo->driver = xstrdup(value); if (!pInfo->driver) { rval = BadAlloc; goto unwind; } } if (strcasecmp(key, "name") == 0 || strcasecmp(key, "identifier") == 0) { if (pInfo->name) { rval = BadRequest; goto unwind; } pInfo->name = xstrdup(value); if (!pInfo->name) { rval = BadAlloc; goto unwind; } } if (strcmp(key, "_source") == 0 && (strcmp(value, "server/hal") == 0 || strcmp(value, "server/udev") == 0 || strcmp(value, "server/wscons") == 0)) { is_auto = 1; if (!xf86Info.autoAddDevices) { rval = BadMatch; goto unwind; } } if (strcmp(key, "major") == 0) pInfo->major = atoi(value); if (strcmp(key, "minor") == 0) pInfo->minor = atoi(value); } nt_list_for_each_entry(option, options, list.next) { /* Copy option key/value strings from the provided list */ pInfo->options = xf86AddNewOption(pInfo->options, input_option_get_key(option), input_option_get_value(option)); } /* Apply InputClass settings */ if (attrs) { if (IgnoreInputClass(pInfo, attrs)) { rval = BadIDChoice; goto unwind; } rval = MergeInputClasses(pInfo, attrs); if (rval != Success) goto unwind; pInfo->attrs = DuplicateInputAttributes(attrs); } if (!pInfo->name) { xf86Msg(X_INFO, "No identifier specified, ignoring this device.\n"); rval = BadRequest; goto unwind; } if (!pInfo->driver) { xf86Msg(X_INFO, "No input driver specified, ignoring this device.\n"); xf86Msg(X_INFO, "This device may have been added with another device file.\n"); rval = BadRequest; goto unwind; } rval = xf86NewInputDevice(pInfo, pdev, (!is_auto || (is_auto && xf86Info.autoEnableDevices))); return rval; unwind: if (is_auto && !xf86Info.autoAddDevices) xf86Msg(X_INFO, "AutoAddDevices is off - not adding device.\n"); xf86DeleteInput(pInfo, 0); return rval; } void DeleteInputDeviceRequest(DeviceIntPtr pDev) { InputInfoPtr pInfo = (InputInfoPtr) pDev->public.devicePrivate; InputDriverPtr drv = NULL; Bool isMaster = IsMaster(pDev); if (pInfo) /* need to get these before RemoveDevice */ drv = pInfo->drv; input_lock(); RemoveDevice(pDev, TRUE); if (!isMaster && pInfo != NULL) { if (drv->UnInit) drv->UnInit(drv, pInfo, 0); else xf86DeleteInput(pInfo, 0); } input_unlock(); } void RemoveInputDeviceTraces(const char *config_info) { PausedInputDevicePtr d, tmp; xorg_list_for_each_entry_safe(d, tmp, &new_input_devices_list, node) { const char *ci = xf86findOptionValue(d->pInfo->options, "config_info"); if (!ci || strcmp(ci, config_info) != 0) continue; xorg_list_del(&d->node); free(d); } } /* * convenient functions to post events */ void xf86PostMotionEvent(DeviceIntPtr device, int is_absolute, int first_valuator, int num_valuators, ...) { va_list var; int i = 0; ValuatorMask mask; XI_VERIFY_VALUATORS(num_valuators); valuator_mask_zero(&mask); va_start(var, num_valuators); for (i = 0; i < num_valuators; i++) valuator_mask_set(&mask, first_valuator + i, va_arg(var, int)); va_end(var); xf86PostMotionEventM(device, is_absolute, &mask); } void xf86PostMotionEventP(DeviceIntPtr device, int is_absolute, int first_valuator, int num_valuators, const int *valuators) { ValuatorMask mask; XI_VERIFY_VALUATORS(num_valuators); valuator_mask_set_range(&mask, first_valuator, num_valuators, valuators); xf86PostMotionEventM(device, is_absolute, &mask); } static int xf86CheckMotionEvent4DGA(DeviceIntPtr device, int is_absolute, const ValuatorMask *mask) { int stolen = 0; #ifdef XFreeXDGA ScreenPtr scr = NULL; int idx = 0, i; /* The evdev driver may not always send all axes across. */ if (valuator_mask_isset(mask, 0) || valuator_mask_isset(mask, 1)) { scr = miPointerGetScreen(device); if (scr) { int dx = 0, dy = 0; idx = scr->myNum; if (valuator_mask_isset(mask, 0)) { dx = valuator_mask_get(mask, 0); if (is_absolute) dx -= device->last.valuators[0]; else if (valuator_mask_has_unaccelerated(mask)) dx = valuator_mask_get_unaccelerated(mask, 0); } if (valuator_mask_isset(mask, 1)) { dy = valuator_mask_get(mask, 1); if (is_absolute) dy -= device->last.valuators[1]; else if (valuator_mask_has_unaccelerated(mask)) dy = valuator_mask_get_unaccelerated(mask, 1); } if (DGAStealMotionEvent(device, idx, dx, dy)) stolen = 1; } } for (i = 2; i < valuator_mask_size(mask); i++) { AxisInfoPtr ax; double incr; int val, button; if (i >= device->valuator->numAxes) break; if (!valuator_mask_isset(mask, i)) continue; ax = &device->valuator->axes[i]; if (ax->scroll.type == SCROLL_TYPE_NONE) continue; if (!scr) { scr = miPointerGetScreen(device); if (!scr) break; idx = scr->myNum; } incr = ax->scroll.increment; val = valuator_mask_get(mask, i); if (ax->scroll.type == SCROLL_TYPE_VERTICAL) { if (incr * val < 0) button = 4; /* up */ else button = 5; /* down */ } else { /* SCROLL_TYPE_HORIZONTAL */ if (incr * val < 0) button = 6; /* left */ else button = 7; /* right */ } if (DGAStealButtonEvent(device, idx, button, 1) && DGAStealButtonEvent(device, idx, button, 0)) stolen = 1; } #endif return stolen; } void xf86PostMotionEventM(DeviceIntPtr device, int is_absolute, const ValuatorMask *mask) { int flags = 0; if (xf86CheckMotionEvent4DGA(device, is_absolute, mask)) return; if (valuator_mask_num_valuators(mask) > 0) { if (is_absolute) flags = POINTER_ABSOLUTE; else flags = POINTER_RELATIVE | POINTER_ACCELERATE; } QueuePointerEvents(device, MotionNotify, 0, flags, mask); } void xf86PostProximityEvent(DeviceIntPtr device, int is_in, int first_valuator, int num_valuators, ...) { va_list var; int i; ValuatorMask mask; XI_VERIFY_VALUATORS(num_valuators); valuator_mask_zero(&mask); va_start(var, num_valuators); for (i = 0; i < num_valuators; i++) valuator_mask_set(&mask, first_valuator + i, va_arg(var, int)); va_end(var); xf86PostProximityEventM(device, is_in, &mask); } void xf86PostProximityEventP(DeviceIntPtr device, int is_in, int first_valuator, int num_valuators, const int *valuators) { ValuatorMask mask; XI_VERIFY_VALUATORS(num_valuators); valuator_mask_set_range(&mask, first_valuator, num_valuators, valuators); xf86PostProximityEventM(device, is_in, &mask); } void xf86PostProximityEventM(DeviceIntPtr device, int is_in, const ValuatorMask *mask) { QueueProximityEvents(device, is_in ? ProximityIn : ProximityOut, mask); } void xf86PostButtonEvent(DeviceIntPtr device, int is_absolute, int button, int is_down, int first_valuator, int num_valuators, ...) { va_list var; ValuatorMask mask; int i = 0; XI_VERIFY_VALUATORS(num_valuators); valuator_mask_zero(&mask); va_start(var, num_valuators); for (i = 0; i < num_valuators; i++) valuator_mask_set(&mask, first_valuator + i, va_arg(var, int)); va_end(var); xf86PostButtonEventM(device, is_absolute, button, is_down, &mask); } void xf86PostButtonEventP(DeviceIntPtr device, int is_absolute, int button, int is_down, int first_valuator, int num_valuators, const int *valuators) { ValuatorMask mask; XI_VERIFY_VALUATORS(num_valuators); valuator_mask_set_range(&mask, first_valuator, num_valuators, valuators); xf86PostButtonEventM(device, is_absolute, button, is_down, &mask); } void xf86PostButtonEventM(DeviceIntPtr device, int is_absolute, int button, int is_down, const ValuatorMask *mask) { int flags = 0; if (valuator_mask_num_valuators(mask) > 0) { if (is_absolute) flags = POINTER_ABSOLUTE; else flags = POINTER_RELATIVE | POINTER_ACCELERATE; } #ifdef XFreeXDGA if (miPointerGetScreen(device)) { int index = miPointerGetScreen(device)->myNum; if (DGAStealButtonEvent(device, index, button, is_down)) return; } #endif QueuePointerEvents(device, is_down ? ButtonPress : ButtonRelease, button, flags, mask); } void xf86PostKeyEvent(DeviceIntPtr device, unsigned int key_code, int is_down) { xf86PostKeyEventM(device, key_code, is_down); } void xf86PostKeyEventP(DeviceIntPtr device, unsigned int key_code, int is_down) { xf86PostKeyEventM(device, key_code, is_down); } void xf86PostKeyEventM(DeviceIntPtr device, unsigned int key_code, int is_down) { #ifdef XFreeXDGA DeviceIntPtr pointer; /* Some pointers send key events, paired device is wrong then. */ pointer = GetMaster(device, POINTER_OR_FLOAT); if (miPointerGetScreen(pointer)) { int index = miPointerGetScreen(pointer)->myNum; if (DGAStealKeyEvent(device, index, key_code, is_down)) return; } #endif QueueKeyboardEvents(device, is_down ? KeyPress : KeyRelease, key_code); } void xf86PostKeyboardEvent(DeviceIntPtr device, unsigned int key_code, int is_down) { ValuatorMask mask; valuator_mask_zero(&mask); xf86PostKeyEventM(device, key_code, is_down); } InputInfoPtr xf86FirstLocalDevice(void) { return xf86InputDevs; } /* * Cx - raw data from touch screen * to_max - scaled highest dimension * (remember, this is of rows - 1 because of 0 origin) * to_min - scaled lowest dimension * from_max - highest raw value from touch screen calibration * from_min - lowest raw value from touch screen calibration * * This function is the same for X or Y coordinates. * You may have to reverse the high and low values to compensate for * different orgins on the touch screen vs X. * * e.g. to scale from device coordinates into screen coordinates, call * xf86ScaleAxis(x, 0, screen_width, dev_min, dev_max); */ int xf86ScaleAxis(int Cx, int to_max, int to_min, int from_max, int from_min) { int X; int64_t to_width = to_max - to_min; int64_t from_width = from_max - from_min; if (from_width) { X = (int) (((to_width * (Cx - from_min)) / from_width) + to_min); } else { X = 0; ErrorF("Divide by Zero in xf86ScaleAxis\n"); } if (X > to_max) X = to_max; if (X < to_min) X = to_min; return X; } Bool xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval, int maxval, int resolution, int min_res, int max_res, int mode) { if (!dev || !dev->valuator) return FALSE; return InitValuatorAxisStruct(dev, axnum, label, minval, maxval, resolution, min_res, max_res, mode); } /* * Set the valuator values to be in sync with dix/event.c * DefineInitialRootWindow(). */ void xf86InitValuatorDefaults(DeviceIntPtr dev, int axnum) { if (axnum == 0) { dev->valuator->axisVal[0] = screenInfo.screens[0]->width / 2; dev->last.valuators[0] = dev->valuator->axisVal[0]; } else if (axnum == 1) { dev->valuator->axisVal[1] = screenInfo.screens[0]->height / 2; dev->last.valuators[1] = dev->valuator->axisVal[1]; } } /** * Deactivate a device. Call this function from the driver if you receive a * read error or something else that spoils your day. * Device will be moved to the off_devices list, but it will still be there * until you really clean up after it. * Notifies the client about an inactive device. * * @param panic True if device is unrecoverable and needs to be removed. */ void xf86DisableDevice(DeviceIntPtr dev, Bool panic) { if (!panic) { DisableDevice(dev, TRUE); } else { SendDevicePresenceEvent(dev->id, DeviceUnrecoverable); DeleteInputDeviceRequest(dev); } } /** * Reactivate a device. Call this function from the driver if you just found * out that the read error wasn't quite that bad after all. * Device will be re-activated, and an event sent to the client. */ void xf86EnableDevice(DeviceIntPtr dev) { EnableDevice(dev, TRUE); } /** * Post a touch event with optional valuators. If this is the first touch in * the sequence, at least x & y valuators must be provided. The driver is * responsible for maintaining the correct event sequence (TouchBegin, TouchUpdate, * TouchEnd). Submitting an update or end event for a unregistered touchid will * result in errors. * Touch IDs may be reused by the driver but only after a TouchEnd has been * submitted for that touch ID. * * @param dev The device to post the event for * @param touchid The touchid of the current touch event. Must be an * existing ID for TouchUpdate or TouchEnd events * @param type One of XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd * @param flags Flags for this event * @param The valuator mask with all valuators set for this event. */ void xf86PostTouchEvent(DeviceIntPtr dev, uint32_t touchid, uint16_t type, uint32_t flags, const ValuatorMask *mask) { QueueTouchEvents(dev, type, touchid, flags, mask); } void xf86InputEnableVTProbe(void) { int is_auto = 0; DeviceIntPtr pdev; PausedInputDevicePtr d, tmp; xorg_list_for_each_entry_safe(d, tmp, &new_input_devices_list, node) { InputInfoPtr pInfo = d->pInfo; const char *value = xf86findOptionValue(pInfo->options, "_source"); is_auto = 0; if (value && (strcmp(value, "server/hal") == 0 || strcmp(value, "server/udev") == 0 || strcmp(value, "server/wscons") == 0)) is_auto = 1; xf86NewInputDevice(pInfo, &pdev, (!is_auto || (is_auto && xf86Info.autoEnableDevices))); xorg_list_del(&d->node); free(d); } } /* end of xf86Xinput.c */