📄 joystick_linuxinput.c
字号:
/* DirectInput Joystick device
*
* Copyright 1998,2000 Marcus Meissner
* Copyright 1998,1999 Lionel Ulmer
* Copyright 2000-2001 TransGaming Technologies Inc.
* Copyright 2005 Daniel Remenak
*
* 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
*/
#include "config.h"
#include "wine/port.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.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_INPUT_H
# include <linux/input.h>
# if defined(EVIOCGBIT) && defined(EV_ABS) && defined(BTN_PINKIE)
# define HAVE_CORRECT_LINUXINPUT_H
# endif
#endif
#include "wine/debug.h"
#include "wine/unicode.h"
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "dinput.h"
#include "dinput_private.h"
#include "device_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(dinput);
#ifdef HAVE_CORRECT_LINUXINPUT_H
#define EVDEVPREFIX "/dev/input/event"
/* Wine joystick driver object instances */
#define WINE_JOYSTICK_AXIS_BASE 0
#define WINE_JOYSTICK_BUTTON_BASE 8
typedef struct EffectListItem EffectListItem;
struct EffectListItem
{
LPDIRECTINPUTEFFECT ref;
struct EffectListItem* next;
};
/* implemented in effect_linuxinput.c */
HRESULT linuxinput_create_effect(int* fd, REFGUID rguid, LPDIRECTINPUTEFFECT* peff);
HRESULT linuxinput_get_info_A(int fd, REFGUID rguid, LPDIEFFECTINFOA info);
HRESULT linuxinput_get_info_W(int fd, REFGUID rguid, LPDIEFFECTINFOW info);
typedef struct JoystickImpl JoystickImpl;
static const IDirectInputDevice8AVtbl JoystickAvt;
static const IDirectInputDevice8WVtbl JoystickWvt;
struct JoystickImpl
{
const void *lpVtbl;
LONG ref;
GUID guid;
/* The 'parent' DInput */
IDirectInputImpl *dinput;
/* joystick private */
/* what range and deadzone the game wants */
LONG wantmin[ABS_MAX];
LONG wantmax[ABS_MAX];
LONG deadz[ABS_MAX];
/* autodetecting ranges per axe by following movement */
LONG havemax[ABS_MAX];
LONG havemin[ABS_MAX];
int joyfd;
LPDIDATAFORMAT df;
HANDLE hEvent;
LPDIDEVICEOBJECTDATA data_queue;
int queue_head, queue_tail, queue_len;
BOOL overflow;
DIJOYSTATE2 js;
/* Force feedback variables */
BOOL has_ff;
int num_effects;
EffectListItem* top_effect;
int ff_state;
/* data returned by the EVIOCGABS() ioctl */
int axes[ABS_MAX+1][5];
#define AXE_ABS 0
#define AXE_ABSMIN 1
#define AXE_ABSMAX 2
#define AXE_ABSFUZZ 3
#define AXE_ABSFLAT 4
/* data returned by EVIOCGBIT for caps, EV_ABS, EV_KEY, and EV_FF */
BYTE evbits[(EV_MAX+7)/8];
BYTE absbits[(ABS_MAX+7)/8];
BYTE keybits[(KEY_MAX+7)/8];
BYTE ffbits[(FF_MAX+7)/8];
};
/* This GUID is slightly different from the linux joystick one. Take note. */
static GUID DInput_Wine_Joystick_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
0x9e573eda,
0x7734,
0x11d2,
{0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
};
static void fake_current_js_state(JoystickImpl *ji);
static int find_property_offset(JoystickImpl *This, LPCDIPROPHEADER ph);
#define test_bit(arr,bit) (((BYTE*)arr)[bit>>3]&(1<<(bit&7)))
static int joydev_have(BOOL require_ff)
{
int i, fd, flags, num_effects;
int havejoy = 0;
for (i=0;i<64;i++) {
char buf[200];
BYTE absbits[(ABS_MAX+7)/8],keybits[(KEY_MAX+7)/8];
BYTE evbits[(EV_MAX+7)/8],ffbits[(FF_MAX+7)/8];
sprintf(buf,EVDEVPREFIX"%d",i);
if (require_ff)
flags = O_RDWR;
else
flags = O_RDONLY;
if (-1!=(fd=open(buf,flags))) {
if (-1==ioctl(fd,EVIOCGBIT(EV_ABS,sizeof(absbits)),absbits)) {
perror("EVIOCGBIT EV_ABS");
close(fd);
continue;
}
if (-1==ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(keybits)),keybits)) {
perror("EVIOCGBIT EV_KEY");
close(fd);
continue;
}
/* test for force feedback if it's required */
if (require_ff) {
if ((-1==ioctl(fd,EVIOCGBIT(0,sizeof(evbits)),evbits))) {
perror("EVIOCGBIT 0");
close(fd);
continue;
}
if ( (!test_bit(evbits,EV_FF))
|| (-1==ioctl(fd,EVIOCGBIT(EV_FF,sizeof(ffbits)),ffbits))
|| (-1==ioctl(fd,EVIOCGEFFECTS,&num_effects))
|| (num_effects <= 0)) {
close(fd);
continue;
}
}
/* A true joystick has at least axis X and Y, and at least 1
* button. copied from linux/drivers/input/joydev.c */
if (test_bit(absbits,ABS_X) && test_bit(absbits,ABS_Y) &&
( test_bit(keybits,BTN_TRIGGER) ||
test_bit(keybits,BTN_A) ||
test_bit(keybits,BTN_1)
)
) {
FIXME("found a joystick at %s!\n",buf);
havejoy = 1;
}
close(fd);
}
if (havejoy || (errno==ENODEV))
break;
}
return havejoy;
}
static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
{
int havejoy = 0;
if (id != 0)
return FALSE;
if (!((dwDevType == 0) ||
((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
(((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
return FALSE;
#ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
if (dwFlags & DIEDFL_FORCEFEEDBACK)
return FALSE;
#endif
havejoy = joydev_have(dwFlags & DIEDFL_FORCEFEEDBACK);
if (!havejoy)
return FALSE;
TRACE("Enumerating the linuxinput Joystick device\n");
/* Return joystick */
lpddi->guidInstance = GUID_Joystick;
lpddi->guidProduct = DInput_Wine_Joystick_GUID;
lpddi->guidFFDriver = GUID_NULL;
if (version >= 0x0800)
lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
else
lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
strcpy(lpddi->tszInstanceName, "Joystick");
/* ioctl JSIOCGNAME(len) */
strcpy(lpddi->tszProductName, "Wine Joystick");
return TRUE;
}
static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
{
int havejoy = 0;
if (id != 0)
return FALSE;
if (!((dwDevType == 0) ||
((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
(((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
return FALSE;
#ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
if (dwFlags & DIEDFL_FORCEFEEDBACK)
return FALSE;
#endif
havejoy = joydev_have(dwFlags & DIEDFL_FORCEFEEDBACK);
if (!havejoy)
return FALSE;
TRACE("Enumerating the linuxinput Joystick device\n");
/* Return joystick */
lpddi->guidInstance = GUID_Joystick;
lpddi->guidProduct = DInput_Wine_Joystick_GUID;
lpddi->guidFFDriver = GUID_NULL;
if (version >= 0x0800)
lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
else
lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
MultiByteToWideChar(CP_ACP, 0, "Joystick", -1, lpddi->tszInstanceName, MAX_PATH);
/* ioctl JSIOCGNAME(len) */
MultiByteToWideChar(CP_ACP, 0, "Wine Joystick", -1, lpddi->tszProductName, MAX_PATH);
return TRUE;
}
static JoystickImpl *alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput)
{
JoystickImpl* newDevice;
int i;
newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
newDevice->lpVtbl = jvt;
newDevice->ref = 1;
newDevice->joyfd = -1;
newDevice->dinput = dinput;
#ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
newDevice->ff_state = FF_STATUS_STOPPED;
#endif
memcpy(&(newDevice->guid),rguid,sizeof(*rguid));
for (i=0;i<ABS_MAX;i++) {
newDevice->wantmin[i] = -32768;
newDevice->wantmax[i] = 32767;
/* TODO:
* direct input defines a default for the deadzone somewhere; but as long
* as in map_axis the code for the dead zone is commented out its no
* problem
*/
newDevice->deadz[i] = 0;
}
fake_current_js_state(newDevice);
return newDevice;
}
static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
{
int havejoy = 0;
havejoy = joydev_have(FALSE);
if (!havejoy)
return DIERR_DEVICENOTREG;
if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
(IsEqualGUID(&DInput_Wine_Joystick_GUID,rguid))) {
if ((riid == NULL) ||
IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
*pdev = (IDirectInputDeviceA*) alloc_device(rguid, &JoystickAvt, dinput);
TRACE("Creating a Joystick device (%p)\n", *pdev);
return DI_OK;
} else
return DIERR_NOINTERFACE;
}
return DIERR_DEVICENOTREG;
}
static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
{
int havejoy = 0;
havejoy = joydev_have(FALSE);
if (!havejoy)
return DIERR_DEVICENOTREG;
if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
(IsEqualGUID(&DInput_Wine_Joystick_GUID,rguid))) {
if ((riid == NULL) ||
IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
*pdev = (IDirectInputDeviceW*) alloc_device(rguid, &JoystickWvt, dinput);
TRACE("Creating a Joystick device (%p)\n", *pdev);
return DI_OK;
} else
return DIERR_NOINTERFACE;
}
return DIERR_DEVICENOTREG;
}
const struct dinput_device joystick_linuxinput_device = {
"Wine Linux-input joystick driver",
joydev_enum_deviceA,
joydev_enum_deviceW,
joydev_create_deviceA,
joydev_create_deviceW
};
/******************************************************************************
* Joystick
*/
static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
{
JoystickImpl *This = (JoystickImpl *)iface;
ULONG ref;
ref = InterlockedDecrement(&(This->ref));
if (ref)
return ref;
/* Reset the FF state, free all effects, etc */
IDirectInputDevice8_SendForceFeedbackCommand(iface, DISFFC_RESET);
/* Free the data queue */
HeapFree(GetProcessHeap(),0,This->data_queue);
/* Free the DataFormat */
HeapFree(GetProcessHeap(), 0, This->df);
HeapFree(GetProcessHeap(),0,This);
return 0;
}
/******************************************************************************
* SetDataFormat : the application can choose the format of the data
* the device driver sends back with GetDeviceState.
*/
static HRESULT WINAPI JoystickAImpl_SetDataFormat(
LPDIRECTINPUTDEVICE8A iface,LPCDIDATAFORMAT df
)
{
JoystickImpl *This = (JoystickImpl *)iface;
TRACE("(this=%p,%p)\n",This,df);
if (df == NULL) {
WARN("invalid pointer\n");
return E_POINTER;
}
if (df->dwSize != sizeof(*df)) {
WARN("invalid argument\n");
return DIERR_INVALIDPARAM;
}
_dump_DIDATAFORMAT(df);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -