xenocara/driver/xf86-input-joystick/src/jstk.c

775 lines
24 KiB
C
Raw Normal View History

/*
* Copyright 2007-2011 by Sascha Hlusiak. <saschahlusiak@freedesktop.org>
* Copyright 1995-1999 by Frederic Lepied, France. <Lepied@XFree86.org>
*
* 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 <xorg-server.h>
#include <stdio.h>
#include <xorgVersion.h>
#include <misc.h>
#include <xf86.h>
#include <xf86Xinput.h>
#include <exevents.h> /* Needed for InitValuator/Proximity stuff */
#include <xf86Opt.h>
#include <xf86_OSproc.h>
#include <math.h>
#include <xf86Module.h>
#include "jstk.h"
#include "jstk_axis.h"
#include "jstk_key.h"
#include "jstk_options.h"
#include "jstk_properties.h"
#include <xserver-properties.h>
#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; i<MAXAXES; i++) {
if ((priv->button[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; i<MAXAXES; i++)
if (priv->axis[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; i<MAXAXES; i++)
if (priv->axis[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; i<MAXAXES; i++) {
priv->axis[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; j<MAXKEYSPERBUTTON; j++)
priv->axis[i].keys_low[j] = priv->axis[i].keys_high[j] = 0;
}
for (i=0; i<MAXBUTTONS; i++) {
priv->button[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; j<MAXKEYSPERBUTTON; j++)
priv->button[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; i<MAXBUTTONS; i++) {
char p[64];
sprintf(p,"MapButton%d",i+1);
s = xf86SetStrOption(pInfo->options, 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; i<MAXAXES; i++) {
char p[64];
sprintf(p,"MapAxis%d",i+1);
s = xf86SetStrOption(pInfo->options, 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: */