/* * Copyright 2007-2011 by Sascha Hlusiak. * 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 names of copyright holders not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. The copyright holders make no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include /* Needed for InitValuator/Proximity stuff */ #include #include #include #include #include "jstk.h" #include "jstk_axis.h" #include "jstk_key.h" #include "jstk_options.h" #include "jstk_properties.h" #include #ifdef LINUX_BACKEND #include "backend_joystick.h" #endif #ifdef BSD_BACKEND #include "backend_bsd.h" #endif #ifdef EVDEV_BACKEND #include "backend_evdev.h" #endif #if DEBUG char debug_level = 0; #endif /* *************************************************************************** * * jstkOpenDevice -- * * Called to open the device specified in priv * The compiled backends are tried one by one and return the first matching * * Returns the filedescriptor or -1 in case of error * *************************************************************************** */ static int jstkOpenDevice(JoystickDevPtr priv, BOOL probe) { int fd; fd = -1; if (priv->joystick_device->flags & XI86_SERVER_FD) priv->fd = priv->joystick_device->fd; if (probe == FALSE && priv->open_proc) return priv->open_proc(priv, probe); #ifdef EVDEV_BACKEND if (fd == -1) fd = jstkOpenDevice_evdev(priv, probe); #endif #ifdef LINUX_BACKEND if (fd == -1) fd = jstkOpenDevice_joystick(priv, probe); #endif #ifdef BSD_BACKEND if (fd == -1) fd = jstkOpenDevice_bsd(priv, probe); #endif return fd; } /* *************************************************************************** * * jstkCloseDevice -- * * Called to close the device specified in priv, this is a helper for * backend proc_close functions * *************************************************************************** */ void jstkCloseDevice(JoystickDevPtr priv) { if ((priv->fd >= 0)) { if (!(priv->joystick_device->flags & XI86_SERVER_FD)) xf86CloseSerial(priv->fd); priv->fd = -1; } } /* *************************************************************************** * * jstkReadProc -- * * Called when data is available to read from the device * Reads the data and process the events * *************************************************************************** */ static void jstkReadProc(InputInfoPtr pInfo) { JOYSTICKEVENT event; int number; int i, r; JoystickDevPtr priv = pInfo->private; do { if ((priv->read_proc == NULL) || ((r=priv->read_proc(priv, &event, &number))==0)) { xf86Msg(X_WARNING, "JOYSTICK: Read failed. Deactivating device.\n"); if (pInfo->fd >= 0) xf86RemoveEnabledDevice(pInfo); return; } /* A button's status changed */ if (event == EVENT_BUTTON) { DBG(4, ErrorF("Button %d %s. Mapping: %d\n", number, (priv->button[number].pressed == 0) ? "released" : "pressed", priv->button[number].mapping)); switch (priv->button[number].mapping) { case JSTK_MAPPING_BUTTON: if (priv->mouse_enabled == TRUE) { xf86PostButtonEvent(pInfo->dev, 0, priv->button[number].buttonnumber, priv->button[number].pressed, 0, 0); } break; case JSTK_MAPPING_X: case JSTK_MAPPING_Y: case JSTK_MAPPING_ZX: case JSTK_MAPPING_ZY: if (priv->button[number].pressed == 0) priv->button[number].currentspeed = 1.0; else if (priv->mouse_enabled == TRUE) jstkStartButtonAxisTimer(pInfo, number); break; case JSTK_MAPPING_KEY: if (priv->keys_enabled == TRUE) jstkGenerateKeys(priv->keyboard_device, priv->button[number].keys, priv->button[number].pressed); break; case JSTK_MAPPING_SPEED_MULTIPLY: priv->amplify = 1.0; /* Calculate new amplify value by multiplying them all */ for (i=0; ibutton[i].pressed) && (priv->button[i].mapping == JSTK_MAPPING_SPEED_MULTIPLY)) priv->amplify *= priv->button[i].amplify; } DBG(2, ErrorF("Amplify is now %.3f\n", priv->amplify)); break; case JSTK_MAPPING_DISABLE: if (priv->button[number].pressed == 1) { if ((priv->mouse_enabled == TRUE) || (priv->keys_enabled == TRUE)) { priv->mouse_enabled = FALSE; priv->keys_enabled = FALSE; DBG(2, ErrorF("All events disabled\n")); } else { priv->mouse_enabled = TRUE; priv->keys_enabled = TRUE; DBG(2, ErrorF("All events enabled\n")); } } break; case JSTK_MAPPING_DISABLE_MOUSE: if (priv->button[number].pressed == 1) { if (priv->mouse_enabled == TRUE) priv->mouse_enabled = FALSE; else priv->mouse_enabled = TRUE; DBG(2, ErrorF("Mouse events %s\n", priv->mouse_enabled ? "enabled" : "disabled")); } break; case JSTK_MAPPING_DISABLE_KEYS: if (priv->button[number].pressed == 1) { if (priv->keys_enabled == TRUE) priv->keys_enabled = FALSE; else priv->keys_enabled = TRUE; DBG(2, ErrorF("Keyboard events %s\n", priv->mouse_enabled ? "enabled" : "disabled")); } break; default: break; } } /* An axis was moved */ if ((event == EVENT_AXIS) && (priv->axis[number].type != JSTK_TYPE_NONE)) { DBG(5, ErrorF("Axis %d moved to %d. Type: %d, Mapping: %d\n", number, priv->axis[number].value, priv->axis[number].type, priv->axis[number].mapping)); if (priv->axis[number].valuator != -1) xf86PostMotionEvent(pInfo->dev, 1, priv->axis[number].valuator, 1, priv->axis[number].value); switch (priv->axis[number].mapping) { case JSTK_MAPPING_X: case JSTK_MAPPING_Y: case JSTK_MAPPING_ZX: case JSTK_MAPPING_ZY: switch (priv->axis[number].type) { case JSTK_TYPE_BYVALUE: case JSTK_TYPE_ACCELERATED: if (priv->axis[number].value == 0) priv->axis[number].currentspeed = 1.0; if (priv->mouse_enabled == TRUE) jstkStartAxisTimer(pInfo, number); break; case JSTK_TYPE_ABSOLUTE: if (priv->mouse_enabled == TRUE) jstkHandleAbsoluteAxis(pInfo, number); break; default: break; } /* switch (priv->axis[number].type) */ break; /* case JSTK_MAPPING_ZY */ case JSTK_MAPPING_KEY: if (priv->keys_enabled == TRUE) { if (priv->axis[number].type == JSTK_TYPE_ACCELERATED) { jstkHandlePWMAxis(pInfo, number); } else if (priv->axis[number].type == JSTK_TYPE_BYVALUE) { jstkStartAxisTimer(pInfo, number); } break; } case JSTK_MAPPING_NONE: default: break; } /* switch (priv->axis[number].mapping) */ } /* if (event == EVENT_AXIS) */ } while (r == 2); } static void jstkPtrCtrlProc(DeviceIntPtr device, PtrCtrl *ctrl) { /* Nothing to do, dix handles all settings */ } /* *************************************************************************** * * jstkDeviceControlProc -- * * Handles the initialization, etc. of a joystick * *************************************************************************** */ static Bool jstkDeviceControlProc(DeviceIntPtr pJstk, int what) { int i; InputInfoPtr pInfo = (InputInfoPtr)pJstk->public.devicePrivate; JoystickDevPtr priv = pInfo->private; Atom btn_labels[BUTTONMAP_SIZE+1] = {0}; /* TODO: fillme */ Atom axes_labels[MAXAXES] = {0}; /* TODO: fillme */ switch (what) { case DEVICE_INIT: { int m; char str[32]; CARD8 buttonmap[BUTTONMAP_SIZE+1]; DBG(1, ErrorF("jstkDeviceControlProc what=INIT\n")); /* Probe device and return if error */ if (jstkOpenDevice(priv, TRUE) == -1) { return !Success; } else { /* Success. The OpenDevice call already did some initialization like priv->num_buttons, priv->num_axes */ priv->close_proc(priv); } for (m=0; m<=BUTTONMAP_SIZE; m++) { sprintf(str, "Button %d", m); buttonmap[m] = m; btn_labels[m] = MakeAtom(str, strlen(str), TRUE); } if (InitButtonClassDeviceStruct(pJstk, BUTTONMAP_SIZE, btn_labels, buttonmap) == FALSE) { ErrorF("unable to allocate Button class device\n"); return !Success; } if (!InitPtrFeedbackClassDeviceStruct(pJstk, jstkPtrCtrlProc)) return !Success; m = 2; for (i=0; iaxis[i].valuator != -1) { DBG(3, ErrorF("Axis %d will be valuator %d\n", i, m)); sprintf(str, "Axis %d", i + 1); priv->axis[i].valuator = m++; axes_labels[i] = MakeAtom(str, strlen(str), TRUE); } if (InitValuatorClassDeviceStruct(pJstk, m, axes_labels, GetMotionHistorySize(), Relative) == FALSE) { ErrorF("unable to allocate Valuator class device\n"); return !Success; } else { InitValuatorAxisStruct(pJstk, 0, /* valuator num */ XIGetKnownProperty(AXIS_LABEL_PROP_REL_X), 0, /* min val */ screenInfo.screens[0]->width, /* max val */ 1, /* resolution */ 0, /* min_res */ 1, /* max_res */ Absolute); InitValuatorAxisStruct(pJstk, 1, /* valuator num */ XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y), 0, /* min val */ screenInfo.screens[0]->height, /* max val */ 1, /* resolution */ 0, /* min_res */ 1, /* max_res */ Absolute); for (i=0; iaxis[i].valuator != -1) { InitValuatorAxisStruct(pJstk, priv->axis[i].valuator, axes_labels[i], -32768, /* min val */ 32767, /* max val */ 1, /* resolution */ 0, /* min_res */ 1, /* max_res */ Absolute); } /* allocate the motion history buffer if needed */ xf86MotionHistoryAllocate(pInfo); } jstkInitProperties(pJstk, priv); break; } case DEVICE_ON: DBG(1, ErrorF("jstkDeviceControlProc what=ON name=%s\n", priv->device)); if (jstkOpenDevice(priv, FALSE) != -1) { pJstk->public.on = TRUE; pInfo->fd = priv->fd; xf86AddEnabledDevice(pInfo); } else return !Success; break; case DEVICE_OFF: case DEVICE_CLOSE: if (!pJstk->public.on) break; DBG(1, ErrorF("jstkDeviceControlProc what=%s\n", (what == DEVICE_CLOSE) ? "CLOSE" : "OFF")); if (priv->timerrunning == TRUE) { priv->timerrunning = FALSE; TimerCancel(priv->timer); } for (i = 0; i < MAXAXES; i++) if (priv->axis[i].timerrunning) { priv->axis[i].timerrunning = FALSE; TimerCancel(priv->axis[i].timer); } if (pInfo->fd >= 0) xf86RemoveEnabledDevice(pInfo); if (!(pInfo->flags & XI86_SERVER_FD)) pInfo->fd = -1; if (priv->close_proc) priv->close_proc(priv); pJstk->public.on = FALSE; break; #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) * 100 + GET_ABI_MINOR(ABI_XINPUT_VERSION) >= 1901 case DEVICE_ABORT: break; #endif default: ErrorF("unsupported mode=%d\n", what); return BadValue; } /* switch (what) */ return Success; } /* *************************************************************************** * * jstkCorePreInit -- * * Called when a device will be instantiated * * This is a tad complicated. NewInputDeviceRequest(), which we use to * hotplug a keyboard device,. enables the device, so we need to make sure * that all options for the dependent device are set correctly. * * This means that we parse the keyboard-specific options into the * keyboard device's PreInit, and re-use the keyboard's priv field. * *************************************************************************** */ static int jstkCorePreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags) { InputInfoPtr keyboard_device; JoystickDevPtr priv = NULL; char *s; int i, j; s = xf86CheckStrOption(pInfo->options, "_source", NULL); if (s && strcmp(s, "_driver/joystick") == 0) return jstkKeyboardPreInit(drv, pInfo, flags); pInfo->device_control = jstkDeviceControlProc; pInfo->read_input = jstkReadProc; pInfo->control_proc = NULL; pInfo->switch_mode = NULL; if (!(pInfo->flags & XI86_SERVER_FD)) pInfo->fd = -1; pInfo->dev = NULL; pInfo->type_name = XI_JOYSTICK; keyboard_device = jstkKeyboardHotplug(pInfo, flags); if (!keyboard_device) return BadAlloc; pInfo->private = priv = keyboard_device->private; priv->fd = -1; priv->open_proc = NULL; priv->read_proc = NULL; priv->close_proc = NULL; priv->device = NULL; priv->devicedata = NULL; priv->timer = NULL; priv->timerrunning = FALSE; priv->mouse_enabled = TRUE; priv->keys_enabled = TRUE; priv->amplify = 1.0f; priv->joystick_device = pInfo; priv->keyboard_device = keyboard_device; priv->num_axes = MAXAXES; priv->num_buttons = MAXBUTTONS; /* Initialize default mappings */ for (i=0; iaxis[i].value = 0; priv->axis[i].oldvalue = 0; priv->axis[i].deadzone = 5000; priv->axis[i].type = JSTK_TYPE_NONE; priv->axis[i].mapping = JSTK_MAPPING_NONE; priv->axis[i].currentspeed = 0.0f; priv->axis[i].amplify = 1.0f; priv->axis[i].valuator = -1; priv->axis[i].subpixel = 0.0f; priv->axis[i].timer = NULL; priv->axis[i].timerrunning = FALSE; priv->axis[i].key_isdown = 0; for (j=0; jaxis[i].keys_low[j] = priv->axis[i].keys_high[j] = 0; } for (i=0; ibutton[i].pressed = 0; priv->button[i].buttonnumber = 0; priv->button[i].mapping = JSTK_MAPPING_NONE; priv->button[i].currentspeed = 1.0f; priv->button[i].subpixel = 0.0f; priv->button[i].amplify = 1.0; for (j=0; jbutton[i].keys[j] = 0; } /* First three joystick buttons generate mouse clicks */ priv->button[0].mapping = JSTK_MAPPING_BUTTON; priv->button[0].buttonnumber = 1; priv->button[1].mapping = JSTK_MAPPING_BUTTON; priv->button[1].buttonnumber = 2; priv->button[2].mapping = JSTK_MAPPING_BUTTON; priv->button[2].buttonnumber = 3; /* First two axes are a stick for moving */ priv->axis[0].type = JSTK_TYPE_BYVALUE; priv->axis[0].mapping = JSTK_MAPPING_X; priv->axis[1].type = JSTK_TYPE_BYVALUE; priv->axis[1].mapping = JSTK_MAPPING_Y; /* Next two axes are a stick for scrolling */ priv->axis[2].type = JSTK_TYPE_BYVALUE; priv->axis[2].mapping = JSTK_MAPPING_ZX; priv->axis[3].type = JSTK_TYPE_BYVALUE; priv->axis[3].mapping = JSTK_MAPPING_ZY; /* Next two axes are a pad for moving */ priv->axis[4].type = JSTK_TYPE_ACCELERATED; priv->axis[4].mapping = JSTK_MAPPING_X; priv->axis[5].type = JSTK_TYPE_ACCELERATED; priv->axis[5].mapping = JSTK_MAPPING_Y; /* Joystick device is mandatory */ priv->device = xf86SetStrOption(pInfo->options, "Device", NULL); if (!priv->device) priv->device = xf86SetStrOption(pInfo->options, "Path", NULL); if (!priv->device) { xf86Msg (X_ERROR, "%s: No Device specified.\n", pInfo->name); goto SetupProc_fail; } #if DEBUG debug_level = xf86SetIntOption(pInfo->options, "DebugLevel", 0); if (debug_level > 0) { xf86Msg(X_CONFIG, "%s: debug level set to %d\n", pInfo->name, debug_level); } #else if (xf86SetIntOption(pInfo->options, "DebugLevel", 0) != 0) { xf86Msg(X_WARNING, "%s: DebugLevel: Compiled without debugging support!\n", pInfo->name); } #endif priv->mouse_enabled = xf86SetBoolOption(pInfo->options, "StartMouseEnabled", TRUE); priv->keys_enabled = xf86SetBoolOption(pInfo->options, "StartKeysEnabled", TRUE); /* Process button mapping options */ for (i=0; ioptions, p, NULL); if (s != NULL) { jstkParseButtonOption(s, priv, i, pInfo->name); } DBG(1, xf86Msg(X_CONFIG, "Button %d mapped to %d\n", i+1, priv->button[i].mapping)); } /* Process button mapping options */ for (i=0; ioptions, p, NULL); if (s != NULL) { jstkParseAxisOption(s, priv, &priv->axis[i], pInfo->name); } DBG(1, xf86Msg(X_CONFIG, "Axis %d type is %d, mapped to %d, amplify=%.3f\n", i+1, priv->axis[i].type, priv->axis[i].mapping, priv->axis[i].amplify)); } return Success; SetupProc_fail: if (priv) { free(priv); if (keyboard_device) keyboard_device->private = NULL; } if (pInfo) pInfo->private = NULL; return BadValue; } /* *************************************************************************** * * jstkCoreUnInit -- * * Called when a device is unplugged and needs to be removed * This is a bit tricky, because the keyboard device and the main device * share the same private data, which must be freed only once, which is done * by the main device. * * *************************************************************************** */ static void jstkCoreUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags) { if (pInfo->private) { JoystickDevPtr priv = (JoystickDevPtr) pInfo->private; if (priv->keyboard_device == pInfo) { /* this is the keyboard device */ /* Unlink from private data to notify that the * keyboard device is no more, but don't free */ priv->keyboard_device = NULL; } else { /* freeing main device if keyboard still exists, notify keyboard device that it's private data is gone */ if (priv->keyboard_device) priv->keyboard_device->private = NULL; free (priv); } } pInfo->private = NULL; xf86DeleteInput(pInfo, 0); } _X_EXPORT InputDriverRec JOYSTICK = { 1, "joystick", NULL, jstkCorePreInit, jstkCoreUnInit, NULL, NULL, #ifdef XI86_DRV_CAP_SERVER_FD XI86_DRV_CAP_SERVER_FD #endif }; /* *************************************************************************** * * jstkPlug -- * * Called when the driver is loaded * *************************************************************************** */ static pointer jstkDriverPlug(pointer module, pointer options, int *errmaj, int *errmin) { xf86AddInputDriver(&JOYSTICK, module, 0); return module; } /* *************************************************************************** * * jstkDriverUnplug -- * * Called when the driver is unloaded * *************************************************************************** */ static void jstkDriverUnplug(pointer p) { } /* *************************************************************************** * * Module information for X.Org * *************************************************************************** */ static XF86ModuleVersionInfo jstkVersionRec = { "joystick", MODULEVENDORSTRING, MODINFOSTRING1, MODINFOSTRING2, XORG_VERSION_CURRENT, PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL, ABI_CLASS_XINPUT, ABI_XINPUT_VERSION, MOD_CLASS_XINPUT, {0, 0, 0, 0} /* signature, to be patched into the file by */ /* a tool */ }; /* *************************************************************************** * * Exported module Data for X.Org * *************************************************************************** */ _X_EXPORT XF86ModuleData joystickModuleData = { &jstkVersionRec, jstkDriverPlug, jstkDriverUnplug }; /* vim: set filetype=c.doxygen ts=4 et: */