📄 joystick_linuxinput.c
字号:
if (This->joyfd!=-1) {
WARN("acquired\n");
return DIERR_ACQUIRED;
}
/* Store the new data format */
This->df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
if (This->df==NULL) {
return DIERR_OUTOFMEMORY;
}
memcpy(This->df, df, df->dwSize);
This->df->rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
if (This->df->rgodf==NULL) {
HeapFree(GetProcessHeap(), 0, This->df);
return DIERR_OUTOFMEMORY;
}
memcpy(This->df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
return DI_OK;
}
/******************************************************************************
* Acquire : gets exclusive control of the joystick
*/
static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
{
int i;
JoystickImpl *This = (JoystickImpl *)iface;
char buf[200];
BOOL readonly = TRUE;
TRACE("(this=%p)\n",This);
if (This->joyfd!=-1)
return 0;
for (i=0;i<64;i++) {
sprintf(buf,EVDEVPREFIX"%d",i);
if (-1==(This->joyfd=open(buf,O_RDWR))) {
if (-1==(This->joyfd=open(buf,O_RDONLY))) {
/* Couldn't open the device at all */
if (errno==ENODEV)
return DIERR_NOTFOUND;
perror(buf);
continue;
}
else {
/* Couldn't open in r/w but opened in read-only. */
WARN("Could not open %s in read-write mode. Force feedback will be disabled.\n",buf);
}
}
else {
/* Opened device in read-write */
readonly = FALSE;
}
if ((-1!=ioctl(This->joyfd,EVIOCGBIT(0,sizeof(This->evbits)),This->evbits)) &&
(-1!=ioctl(This->joyfd,EVIOCGBIT(EV_ABS,sizeof(This->absbits)),This->absbits)) &&
(-1!=ioctl(This->joyfd,EVIOCGBIT(EV_KEY,sizeof(This->keybits)),This->keybits)) &&
(test_bit(This->absbits,ABS_X) && test_bit(This->absbits,ABS_Y) &&
(test_bit(This->keybits,BTN_TRIGGER)||
test_bit(This->keybits,BTN_A) ||
test_bit(This->keybits,BTN_1)
)
)
)
break;
close(This->joyfd);
This->joyfd = -1;
}
if (This->joyfd==-1)
return DIERR_NOTFOUND;
This->has_ff = FALSE;
This->num_effects = 0;
#ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
if (!readonly && test_bit(This->evbits, EV_FF)) {
if (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_FF,sizeof(This->ffbits)),This->ffbits)) {
if (-1!=ioctl(This->joyfd,EVIOCGEFFECTS,&This->num_effects)
&& This->num_effects > 0) {
This->has_ff = TRUE;
TRACE("Joystick seems to be capable of force feedback.\n");
}
else {
TRACE("Joystick does not support any effects, disabling force feedback.\n");
}
}
else {
TRACE("Could not get EV_FF bits; disabling force feedback.\n");
}
}
else {
TRACE("Force feedback disabled (device is readonly or joystick incapable).\n");
}
#endif
for (i=0;i<ABS_MAX;i++) {
if (test_bit(This->absbits,i)) {
if (-1==ioctl(This->joyfd,EVIOCGABS(i),&(This->axes[i])))
continue;
FIXME("axe %d: cur=%d, min=%d, max=%d, fuzz=%d, flat=%d\n",
i,
This->axes[i][AXE_ABS],
This->axes[i][AXE_ABSMIN],
This->axes[i][AXE_ABSMAX],
This->axes[i][AXE_ABSFUZZ],
This->axes[i][AXE_ABSFLAT]
);
This->havemin[i] = This->axes[i][AXE_ABSMIN];
This->havemax[i] = This->axes[i][AXE_ABSMAX];
}
}
MESSAGE("\n");
fake_current_js_state(This);
return 0;
}
/******************************************************************************
* Unacquire : frees the joystick
*/
static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
{
JoystickImpl *This = (JoystickImpl *)iface;
TRACE("(this=%p)\n",This);
if (This->joyfd!=-1) {
close(This->joyfd);
This->joyfd = -1;
return DI_OK;
}
else
return DI_NOEFFECT;
}
/*
* This maps the read value (from the input event) to a value in the
* 'wanted' range. It also autodetects the possible range of the axe and
* adapts values accordingly.
*/
static int
map_axis(JoystickImpl* This, int axis, int val) {
int xmin = This->axes[axis][AXE_ABSMIN];
int xmax = This->axes[axis][AXE_ABSMAX];
int hmax = This->havemax[axis];
int hmin = This->havemin[axis];
int wmin = This->wantmin[axis];
int wmax = This->wantmax[axis];
int ret;
if (val > hmax) This->havemax[axis] = hmax = val;
if (val < hmin) This->havemin[axis] = hmin = val;
if (xmin == xmax) return val;
/* map the value from the hmin-hmax range into the wmin-wmax range */
ret = ((val-hmin) * (wmax-wmin)) / (hmax-hmin) + wmin;
TRACE("xmin=%d xmax=%d hmin=%d hmax=%d wmin=%d wmax=%d val=%d ret=%d\n", xmin, xmax, hmin, hmax, wmin, wmax, val, ret);
#if 0
/* deadzone doesn't work comfortably enough right now. needs more testing*/
if ((ret > -deadz/2 ) && (ret < deadz/2)) {
FIXME("%d in deadzone, return mid.\n",val);
return (wmax-wmin)/2+wmin;
}
#endif
return ret;
}
/*
* set the current state of the js device as it would be with the middle
* values on the axes
*/
static void fake_current_js_state(JoystickImpl *ji)
{
ji->js.lX = map_axis(ji, ABS_X, ji->axes[ABS_X ][AXE_ABS]);
ji->js.lY = map_axis(ji, ABS_Y, ji->axes[ABS_Y ][AXE_ABS]);
ji->js.lZ = map_axis(ji, ABS_Z, ji->axes[ABS_Z ][AXE_ABS]);
ji->js.lRx = map_axis(ji, ABS_RX, ji->axes[ABS_RX][AXE_ABS]);
ji->js.lRy = map_axis(ji, ABS_RY, ji->axes[ABS_RY][AXE_ABS]);
ji->js.lRz = map_axis(ji, ABS_RZ, ji->axes[ABS_RZ][AXE_ABS]);
ji->js.rglSlider[0] = map_axis(ji, ABS_THROTTLE, ji->axes[ABS_THROTTLE][AXE_ABS]);
ji->js.rglSlider[1] = map_axis(ji, ABS_RUDDER, ji->axes[ABS_RUDDER ][AXE_ABS]);
}
static int find_property_offset(JoystickImpl *This, LPCDIPROPHEADER ph)
{
int i,c;
switch (ph->dwHow) {
case DIPH_BYOFFSET:
for (i=0; i<This->df->dwNumObjs; i++) {
if (This->df->rgodf[i].dwOfs == ph->dwObj) {
return i;
}
}
break;
case DIPH_BYID:
/* XXX: this is a hack - see below */
c = DIDFT_GETINSTANCE(ph->dwObj)>>WINE_JOYSTICK_AXIS_BASE;
for (i=0; (c&1)==0 && i<0x0F; i++) {
c >>= 1;
}
if (i<0x0F) {
return i;
}
/* XXX - the following part won't work with LiveForSpeed
* - the game sets the dwTypes to something else then
* the ddoi.dwType set in EnumObjects
*/
#if 0
for (i=0; i<This->df->dwNumObjs; i++) {
TRACE("dwType='%08x'\n", This->df->rgodf[i].dwType);
if ((This->df->rgodf[i].dwType & 0x00ffffff) == (ph->dwObj & 0x00ffffff)) {
return i;
}
}
#endif
break;
default:
FIXME("Unhandled ph->dwHow=='%04X'\n", (unsigned int)ph->dwHow);
}
return -1;
}
static void joy_polldev(JoystickImpl *This) {
struct timeval tv;
fd_set readfds;
struct input_event ie;
if (This->joyfd==-1)
return;
while (1) {
memset(&tv,0,sizeof(tv));
FD_ZERO(&readfds);
FD_SET(This->joyfd,&readfds);
if (1>select(This->joyfd+1,&readfds,NULL,NULL,&tv))
return;
/* we have one event, so we can read */
if (sizeof(ie)!=read(This->joyfd,&ie,sizeof(ie)))
return;
TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
switch (ie.type) {
case EV_KEY: /* button */
switch (ie.code) {
case BTN_TRIGGER: /* normal flight stick */
case BTN_A: /* gamepad */
case BTN_1: /* generic */
This->js.rgbButtons[0] = ie.value?0x80:0x00;
GEN_EVENT(DIJOFS_BUTTON(0),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case BTN_THUMB:
case BTN_B:
case BTN_2:
This->js.rgbButtons[1] = ie.value?0x80:0x00;
GEN_EVENT(DIJOFS_BUTTON(1),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case BTN_THUMB2:
case BTN_C:
case BTN_3:
This->js.rgbButtons[2] = ie.value?0x80:0x00;
GEN_EVENT(DIJOFS_BUTTON(2),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case BTN_TOP:
case BTN_X:
case BTN_4:
This->js.rgbButtons[3] = ie.value?0x80:0x00;
GEN_EVENT(DIJOFS_BUTTON(3),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case BTN_TOP2:
case BTN_Y:
case BTN_5:
This->js.rgbButtons[4] = ie.value?0x80:0x00;
GEN_EVENT(DIJOFS_BUTTON(4),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case BTN_PINKIE:
case BTN_Z:
case BTN_6:
This->js.rgbButtons[5] = ie.value?0x80:0x00;
GEN_EVENT(DIJOFS_BUTTON(5),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case BTN_BASE:
case BTN_TL:
case BTN_7:
This->js.rgbButtons[6] = ie.value?0x80:0x00;
GEN_EVENT(DIJOFS_BUTTON(6),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case BTN_BASE2:
case BTN_TR:
case BTN_8:
This->js.rgbButtons[7] = ie.value?0x80:0x00;
GEN_EVENT(DIJOFS_BUTTON(7),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case BTN_BASE3:
case BTN_TL2:
case BTN_9:
This->js.rgbButtons[8] = ie.value?0x80:0x00;
GEN_EVENT(DIJOFS_BUTTON(8),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case BTN_BASE4:
case BTN_TR2:
This->js.rgbButtons[9] = ie.value?0x80:0x00;
GEN_EVENT(DIJOFS_BUTTON(9),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case BTN_BASE5:
case BTN_SELECT:
This->js.rgbButtons[10] = ie.value?0x80:0x00;
GEN_EVENT(DIJOFS_BUTTON(10),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
default:
FIXME("unhandled joystick button %x, value %d\n",ie.code,ie.value);
break;
}
break;
case EV_ABS:
switch (ie.code) {
case ABS_X:
This->js.lX = map_axis(This,ABS_X,ie.value);
GEN_EVENT(DIJOFS_X,This->js.lX,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case ABS_Y:
This->js.lY = map_axis(This,ABS_Y,ie.value);
GEN_EVENT(DIJOFS_Y,This->js.lY,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case ABS_Z:
This->js.lZ = map_axis(This,ABS_Z,ie.value);
GEN_EVENT(DIJOFS_Z,This->js.lZ,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case ABS_RX:
This->js.lRx = map_axis(This,ABS_RX,ie.value);
GEN_EVENT(DIJOFS_RX,This->js.lRx,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case ABS_RY:
This->js.lRy = map_axis(This,ABS_RY,ie.value);
GEN_EVENT(DIJOFS_RY,This->js.lRy,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case ABS_RZ:
This->js.lRz = map_axis(This,ABS_RZ,ie.value);
GEN_EVENT(DIJOFS_RZ,This->js.lRz,ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case ABS_THROTTLE:
This->js.rglSlider[0] = map_axis(This,ABS_THROTTLE,ie.value);
GEN_EVENT(DIJOFS_SLIDER(0),This->js.rglSlider[0],ie.time.tv_usec,(This->dinput->evsequence)++);
break;
case ABS_RUDDER:
This->js.rglSlider[1] = map_axis(This,ABS_RUDDER,ie.value);
GEN_EVENT(DIJOFS_SLIDER(1),This->js.rglSlider[1],ie.time.tv_usec,(This->dinput->evsequence)++);
break;
default:
FIXME("unhandled joystick axe event (code %d, value %d)\n",ie.code,ie.value);
break;
}
break;
#ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
case EV_FF_STATUS:
This->ff_state = ie.value;
break;
#endif
default:
FIXME("joystick cannot handle type %d event (code %d)\n",ie.type,ie.code);
break;
}
}
}
/******************************************************************************
* GetDeviceState : returns the "state" of the joystick.
*
*/
static HRESULT WINAPI JoystickAImpl_GetDeviceState(
LPDIRECTINPUTDEVICE8A iface,DWORD len,LPVOID ptr
) {
JoystickImpl *This = (JoystickImpl *)iface;
joy_polldev(This);
TRACE("(this=%p,0x%08lx,%p)\n",This,len,ptr);
if ((len != sizeof(DIJOYSTATE)) && (len != sizeof(DIJOYSTATE2))) {
FIXME("len %ld is not sizeof(DIJOYSTATE) or DIJOYSTATE2, unsupported format.\n",len);
return E_FAIL;
}
memcpy(ptr,&(This->js),len);
This->queue_head = 0;
This->queue_tail = 0;
return 0;
}
/******************************************************************************
* GetDeviceData : gets buffered input data.
*/
static HRESULT WINAPI JoystickAImpl_GetDeviceData(LPDIRECTINPUTDEVICE8A iface,
DWORD dodsize,
LPDIDEVICEOBJECTDATA dod,
LPDWORD entries,
DWORD flags
) {
JoystickImpl *This = (JoystickImpl *)iface;
DWORD len;
int nqtail;
HRESULT hr = DI_OK;
TRACE("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx)\n",This,dodsize,*entries,flags);
if (This->joyfd==-!1) {
WARN("not acquired\n");
return DIERR_NOTACQUIRED;
}
joy_polldev(This);
if (flags & DIGDD_PEEK)
FIXME("DIGDD_PEEK\n");
len = ((This->queue_head < This->queue_tail) ? This->queue_len : 0)
+ (This->queue_head - This->queue_tail);
if (len > *entries)
len = *entries;
if (dod == NULL) {
if (len)
TRACE("Application discarding %ld event(s).\n", len);
*entries = len;
nqtail = This->queue_tail + len;
while (nqtail >= This->queue_len)
nqtail -= This->queue_len;
} else {
if (dodsize < sizeof(DIDEVICEOBJECTDATA_DX3)) {
ERR("Wrong structure size !\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -