📄 joystick_linux.c
字号:
/* DirectInput Joystick device
*
* Copyright 1998 Marcus Meissner
* Copyright 1998,1999 Lionel Ulmer
* Copyright 2000-2001 TransGaming Technologies Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* To Do:
* dead zone
* force feedback
*/
#include "config.h"
#include "wine/port.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <sys/fcntl.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include <errno.h>
#ifdef HAVE_SYS_ERRNO_H
# include <sys/errno.h>
#endif
#ifdef HAVE_LINUX_IOCTL_H
# include <linux/ioctl.h>
#endif
#ifdef HAVE_LINUX_JOYSTICK_H
# include <linux/joystick.h>
#endif
#include "wine/debug.h"
#include "wine/unicode.h"
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winreg.h"
#include "dinput.h"
#include "dinput_private.h"
#include "device_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(dinput);
#ifdef HAVE_LINUX_22_JOYSTICK_API
#define JOYDEV "/dev/js"
typedef struct {
LONG lMin;
LONG lMax;
LONG lDeadZone;
LONG lSaturation;
} ObjProps;
typedef struct {
LONG lX;
LONG lY;
} POV;
typedef struct JoystickImpl JoystickImpl;
static const IDirectInputDevice8AVtbl JoystickAvt;
static const IDirectInputDevice8WVtbl JoystickWvt;
struct JoystickImpl
{
const void *lpVtbl;
LONG ref;
GUID guid;
char dev[32];
/* The 'parent' DInput */
IDirectInputImpl *dinput;
/* joystick private */
int joyfd;
DIJOYSTATE2 js; /* wine data */
LPDIDATAFORMAT user_df; /* user defined format */
DataFormat *transform; /* wine to user format converter */
int *offsets; /* object offsets */
ObjProps *props;
HANDLE hEvent;
LPDIDEVICEOBJECTDATA data_queue;
int queue_head, queue_tail, queue_len;
BOOL acquired;
char *name;
DIDEVCAPS devcaps;
LONG deadzone;
int *axis_map;
int axes;
int buttons;
POV povs[4];
CRITICAL_SECTION crit;
BOOL overflow;
};
static GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
0x9e573ed9,
0x7734,
0x11d2,
{0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
};
static void _dump_DIDEVCAPS(LPDIDEVCAPS lpDIDevCaps)
{
TRACE("dwSize: %ld\n", lpDIDevCaps->dwSize);
TRACE("dwFlags: %08lx\n",lpDIDevCaps->dwFlags);
TRACE("dwDevType: %08lx %s\n", lpDIDevCaps->dwDevType,
lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
lpDIDevCaps->dwDevType == DIDEVTYPE_MOUSE ? "DIDEVTYPE_MOUSE" :
lpDIDevCaps->dwDevType == DIDEVTYPE_KEYBOARD ? "DIDEVTYPE_KEYBOARD" :
lpDIDevCaps->dwDevType == DIDEVTYPE_JOYSTICK ? "DIDEVTYPE_JOYSTICK" :
lpDIDevCaps->dwDevType == DIDEVTYPE_HID ? "DIDEVTYPE_HID" : "UNKNOWN");
TRACE("dwAxes: %ld\n",lpDIDevCaps->dwAxes);
TRACE("dwButtons: %ld\n",lpDIDevCaps->dwButtons);
TRACE("dwPOVs: %ld\n",lpDIDevCaps->dwPOVs);
if (lpDIDevCaps->dwSize > sizeof(DIDEVCAPS_DX3)) {
TRACE("dwFFSamplePeriod: %ld\n",lpDIDevCaps->dwFFSamplePeriod);
TRACE("dwFFMinTimeResolution: %ld\n",lpDIDevCaps->dwFFMinTimeResolution);
TRACE("dwFirmwareRevision: %ld\n",lpDIDevCaps->dwFirmwareRevision);
TRACE("dwHardwareRevision: %ld\n",lpDIDevCaps->dwHardwareRevision);
TRACE("dwFFDriverVersion: %ld\n",lpDIDevCaps->dwFFDriverVersion);
}
}
static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
{
int fd = -1;
char dev[32];
if (dwFlags & DIEDFL_FORCEFEEDBACK) {
WARN("force feedback not supported\n");
return FALSE;
}
if ((dwDevType == 0) ||
((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
(((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
/* check whether we have a joystick */
sprintf(dev, "%s%d", JOYDEV, id);
if ((fd = open(dev,O_RDONLY)) < 0) {
WARN("open(%s,O_RDONLY) failed: %s\n", dev, strerror(errno));
return FALSE;
}
/* Return joystick */
lpddi->guidInstance = DInput_Wine_Joystick_GUID;
lpddi->guidInstance.Data3 = id;
lpddi->guidProduct = DInput_Wine_Joystick_GUID;
/* we only support traditional joysticks for now */
if (version >= 0x0800)
lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
else
lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
sprintf(lpddi->tszInstanceName, "Joystick %d", id);
#if defined(JSIOCGNAME)
if (ioctl(fd,JSIOCGNAME(sizeof(lpddi->tszProductName)),lpddi->tszProductName) < 0) {
WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", dev, strerror(errno));
strcpy(lpddi->tszProductName, "Wine Joystick");
}
#else
strcpy(lpddi->tszProductName, "Wine Joystick");
#endif
lpddi->guidFFDriver = GUID_NULL;
close(fd);
TRACE("Enumerating the linux Joystick device: %s (%s)\n", dev, lpddi->tszProductName);
return TRUE;
}
return FALSE;
}
static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
{
int fd = -1;
char name[MAX_PATH];
char dev[32];
char friendly[32];
if (dwFlags & DIEDFL_FORCEFEEDBACK) {
WARN("force feedback not supported\n");
return FALSE;
}
if ((dwDevType == 0) ||
((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
(((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
/* check whether we have a joystick */
sprintf(dev, "%s%d", JOYDEV, id);
if ((fd = open(dev,O_RDONLY)) < 0) {
WARN("open(%s,O_RDONLY) failed: %s\n", dev, strerror(errno));
return FALSE;
}
/* Return joystick */
lpddi->guidInstance = DInput_Wine_Joystick_GUID;
lpddi->guidInstance.Data3 = id;
lpddi->guidProduct = DInput_Wine_Joystick_GUID;
/* we only support traditional joysticks for now */
if (version >= 0x0800)
lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
else
lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
sprintf(friendly, "Joystick %d", id);
MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
#if defined(JSIOCGNAME)
if (ioctl(fd,JSIOCGNAME(sizeof(name)),name) < 0) {
WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", dev, strerror(errno));
strcpy(name, "Wine Joystick");
}
#else
strcpy(name, "Wine Joystick");
#endif
MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
lpddi->guidFFDriver = GUID_NULL;
close(fd);
TRACE("Enumerating the linux Joystick device: %s (%s)\n",dev,name);
return TRUE;
}
return FALSE;
}
/*
* Get a config key from either the app-specific or the default config
*/
inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
char *buffer, DWORD size )
{
if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size ))
return 0;
if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size ))
return 0;
return ERROR_FILE_NOT_FOUND;
}
/*
* Setup the dinput options.
*/
static HRESULT setup_dinput_options(JoystickImpl * device)
{
char buffer[MAX_PATH+16];
HKEY hkey, appkey = 0;
DWORD len;
buffer[MAX_PATH]='\0';
/* @@ Wine registry key: HKCU\Software\Wine\DirectInput */
if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\DirectInput", &hkey)) hkey = 0;
len = GetModuleFileNameA( 0, buffer, MAX_PATH );
if (len && len < MAX_PATH) {
HKEY tmpkey;
/* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DirectInput */
if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
{
char *p, *appname = buffer;
if ((p = strrchr( appname, '/' ))) appname = p + 1;
if ((p = strrchr( appname, '\\' ))) appname = p + 1;
strcat( appname, "\\DirectInput" );
if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
RegCloseKey( tmpkey );
}
}
/* get options */
if (!get_config_key( hkey, appkey, "DefaultDeadZone", buffer, MAX_PATH )) {
device->deadzone = atoi(buffer);
TRACE("setting default deadzone to: \"%s\" %ld\n", buffer, device->deadzone);
}
if (!get_config_key( hkey, appkey, device->name, buffer, MAX_PATH )) {
int tokens = 0;
int axis = 0;
int pov = 0;
const char *delim = ",";
char * ptr;
TRACE("\"%s\" = \"%s\"\n", device->name, buffer);
device->axis_map = HeapAlloc(GetProcessHeap(), 0, device->axes * sizeof(int));
if (device->axis_map == 0)
return DIERR_OUTOFMEMORY;
if ((ptr = strtok(buffer, delim)) != NULL) {
do {
if (strcmp(ptr, "X") == 0) {
device->axis_map[tokens] = 0;
axis++;
} else if (strcmp(ptr, "Y") == 0) {
device->axis_map[tokens] = 1;
axis++;
} else if (strcmp(ptr, "Z") == 0) {
device->axis_map[tokens] = 2;
axis++;
} else if (strcmp(ptr, "Rx") == 0) {
device->axis_map[tokens] = 3;
axis++;
} else if (strcmp(ptr, "Ry") == 0) {
device->axis_map[tokens] = 4;
axis++;
} else if (strcmp(ptr, "Rz") == 0) {
device->axis_map[tokens] = 5;
axis++;
} else if (strcmp(ptr, "Slider1") == 0) {
device->axis_map[tokens] = 6;
axis++;
} else if (strcmp(ptr, "Slider2") == 0) {
device->axis_map[tokens] = 7;
axis++;
} else if (strcmp(ptr, "POV1") == 0) {
device->axis_map[tokens++] = 8;
device->axis_map[tokens] = 8;
pov++;
} else if (strcmp(ptr, "POV2") == 0) {
device->axis_map[tokens++] = 9;
device->axis_map[tokens] = 9;
pov++;
} else if (strcmp(ptr, "POV3") == 0) {
device->axis_map[tokens++] = 10;
device->axis_map[tokens] = 10;
pov++;
} else if (strcmp(ptr, "POV4") == 0) {
device->axis_map[tokens++] = 11;
device->axis_map[tokens] = 11;
pov++;
} else {
ERR("invalid joystick axis type: %s\n", ptr);
device->axis_map[tokens] = tokens;
axis++;
}
tokens++;
} while ((ptr = strtok(NULL, delim)) != NULL);
if (tokens != device->devcaps.dwAxes) {
ERR("not all joystick axes mapped: %d axes(%d,%d), %d arguments\n", device->axes, axis, pov,tokens);
while (tokens < device->axes) {
device->axis_map[tokens] = tokens;
tokens++;
}
}
}
device->devcaps.dwAxes = axis;
device->devcaps.dwPOVs = pov;
}
if (appkey)
RegCloseKey( appkey );
if (hkey)
RegCloseKey( hkey );
return DI_OK;
}
static void calculate_ids(JoystickImpl* device)
{
int i;
int axis = 0;
int button = 0;
int pov = 0;
int axis_base;
int pov_base;
int button_base;
/* Make two passes over the format. The first counts the number
* for each type and the second sets the id */
for (i = 0; i < device->user_df->dwNumObjs; i++) {
if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_AXIS)
axis++;
else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_POV)
pov++;
else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_BUTTON)
button++;
}
axis_base = 0;
pov_base = axis;
button_base = axis + pov;
axis = 0;
button = 0;
pov = 0;
for (i = 0; i < device->user_df->dwNumObjs; i++) {
DWORD type = 0;
if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_AXIS) {
axis++;
type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
DIDFT_MAKEINSTANCE(axis + axis_base);
TRACE("axis type = 0x%08lx\n", type);
} else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_POV) {
pov++;
type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
DIDFT_MAKEINSTANCE(pov + pov_base);
TRACE("POV type = 0x%08lx\n", type);
} else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_BUTTON) {
button++;
type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
DIDFT_MAKEINSTANCE(button + button_base);
TRACE("button type = 0x%08lx\n", type);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -