1 /* DirectInput Joystick device
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "wine/port.h"
25 #ifdef HAVE_LINUX_22_JOYSTICK_API
33 #ifdef HAVE_SYS_TIME_H
34 # include <sys/time.h>
36 #include <sys/fcntl.h>
37 #ifdef HAVE_SYS_IOCTL_H
38 # include <sys/ioctl.h>
41 #ifdef HAVE_SYS_ERRNO_H
42 # include <sys/errno.h>
44 #ifdef HAVE_LINUX_JOYSTICK_H
45 # include <linux/joystick.h>
47 #define JOYDEV "/dev/js0"
49 #include "wine/debug.h"
55 #include "dinput_private.h"
56 #include "device_private.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(dinput
);
60 /* Wine joystick driver object instances */
61 #define WINE_JOYSTICK_AXIS_BASE 0
62 #define WINE_JOYSTICK_BUTTON_BASE 8
64 typedef struct JoystickAImpl JoystickAImpl
;
65 static ICOM_VTABLE(IDirectInputDevice8A
) JoystickAvt
;
72 /* The 'parent' DInput */
73 IDirectInputAImpl
*dinput
;
75 /* joystick private */
79 LONG lMin
,lMax
,deadzone
;
80 LPDIDEVICEOBJECTDATA data_queue
;
81 int queue_head
, queue_tail
, queue_len
;
85 static GUID DInput_Wine_Joystick_GUID
= { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
89 {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
92 static BOOL
joydev_enum_device(DWORD dwDevType
, DWORD dwFlags
, LPDIDEVICEINSTANCEA lpddi
)
96 if (dwFlags
& DIEDFL_FORCEFEEDBACK
)
99 if ((dwDevType
==0) || (GET_DIDEVICE_TYPE(dwDevType
)==DIDEVTYPE_JOYSTICK
)) {
100 /* check whether we have a joystick */
101 if ((fd
= open(JOYDEV
,O_RDONLY
) != -1) || (errno
!=ENODEV
&& errno
!=ENOENT
)) {
102 TRACE("Enumerating the linux Joystick device\n");
104 /* Return joystick */
105 lpddi
->guidInstance
= GUID_Joystick
;
106 lpddi
->guidProduct
= DInput_Wine_Joystick_GUID
;
107 /* we only support traditional joysticks for now */
108 lpddi
->dwDevType
= DIDEVTYPE_JOYSTICK
|
109 (DIDEVTYPEJOYSTICK_TRADITIONAL
<<8);
110 strcpy(lpddi
->tszInstanceName
, "Joystick");
111 /* ioctl JSIOCGNAME(len) */
112 strcpy(lpddi
->tszProductName
, "Wine Joystick");
114 lpddi
->guidFFDriver
= GUID_NULL
;
124 static JoystickAImpl
*alloc_device(REFGUID rguid
, LPVOID jvt
, IDirectInputAImpl
*dinput
)
126 JoystickAImpl
* newDevice
;
128 newDevice
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(JoystickAImpl
));
129 newDevice
->lpVtbl
= jvt
;
131 newDevice
->joyfd
= -1;
132 newDevice
->lMin
= -32768;
133 newDevice
->lMax
= +32767;
134 newDevice
->dinput
= dinput
;
135 memcpy(&(newDevice
->guid
),rguid
,sizeof(*rguid
));
140 static HRESULT
joydev_create_device(IDirectInputAImpl
*dinput
, REFGUID rguid
, REFIID riid
, LPDIRECTINPUTDEVICEA
* pdev
)
142 if ((IsEqualGUID(&GUID_Joystick
,rguid
)) ||
143 (IsEqualGUID(&DInput_Wine_Joystick_GUID
,rguid
))) {
144 if ((riid
== NULL
) ||
145 IsEqualGUID(&IID_IDirectInputDeviceA
,riid
) ||
146 IsEqualGUID(&IID_IDirectInputDevice2A
,riid
) ||
147 IsEqualGUID(&IID_IDirectInputDevice7A
,riid
) ||
148 IsEqualGUID(&IID_IDirectInputDevice8A
,riid
)) {
149 *pdev
=(IDirectInputDeviceA
*) alloc_device(rguid
, &JoystickAvt
, dinput
);
151 TRACE("Creating a Joystick device (%p)\n", *pdev
);
154 return DIERR_NOINTERFACE
;
157 return DIERR_DEVICENOTREG
;
160 static dinput_device joydev
= {
166 DECL_GLOBAL_CONSTRUCTOR(joydev_register
) { dinput_register_device(&joydev
); }
168 /******************************************************************************
171 static ULONG WINAPI
JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface
)
173 ICOM_THIS(JoystickAImpl
,iface
);
179 /* Free the data queue */
180 if (This
->data_queue
!= NULL
)
181 HeapFree(GetProcessHeap(),0,This
->data_queue
);
183 /* Free the DataFormat */
184 HeapFree(GetProcessHeap(), 0, This
->df
);
186 HeapFree(GetProcessHeap(),0,This
);
190 /******************************************************************************
191 * SetDataFormat : the application can choose the format of the data
192 * the device driver sends back with GetDeviceState.
194 static HRESULT WINAPI
JoystickAImpl_SetDataFormat(
195 LPDIRECTINPUTDEVICE8A iface
,LPCDIDATAFORMAT df
198 ICOM_THIS(JoystickAImpl
,iface
);
201 TRACE("(this=%p,%p)\n",This
,df
);
203 TRACE("(df.dwSize=%ld)\n",df
->dwSize
);
204 TRACE("(df.dwObjsize=%ld)\n",df
->dwObjSize
);
205 TRACE("(df.dwFlags=0x%08lx)\n",df
->dwFlags
);
206 TRACE("(df.dwDataSize=%ld)\n",df
->dwDataSize
);
207 TRACE("(df.dwNumObjs=%ld)\n",df
->dwNumObjs
);
209 for (i
=0;i
<df
->dwNumObjs
;i
++) {
210 TRACE("df.rgodf[%d].guid %s (%p)\n",i
,debugstr_guid(df
->rgodf
[i
].pguid
), df
->rgodf
[i
].pguid
);
211 TRACE("df.rgodf[%d].dwOfs %ld\n",i
,df
->rgodf
[i
].dwOfs
);
212 TRACE("dwType 0x%02x,dwInstance %d\n",DIDFT_GETTYPE(df
->rgodf
[i
].dwType
),DIDFT_GETINSTANCE(df
->rgodf
[i
].dwType
));
213 TRACE("df.rgodf[%d].dwFlags 0x%08lx\n",i
,df
->rgodf
[i
].dwFlags
);
216 /* Store the new data format */
217 This
->df
= HeapAlloc(GetProcessHeap(),0,df
->dwSize
);
218 memcpy(This
->df
, df
, df
->dwSize
);
219 This
->df
->rgodf
= HeapAlloc(GetProcessHeap(),0,df
->dwNumObjs
*df
->dwObjSize
);
220 memcpy(This
->df
->rgodf
,df
->rgodf
,df
->dwNumObjs
*df
->dwObjSize
);
225 /******************************************************************************
226 * Acquire : gets exclusive control of the joystick
228 static HRESULT WINAPI
JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface
)
230 ICOM_THIS(JoystickAImpl
,iface
);
232 TRACE("(this=%p)\n",This
);
235 This
->joyfd
=open(JOYDEV
,O_RDONLY
);
237 return DIERR_NOTFOUND
;
241 /******************************************************************************
242 * Unacquire : frees the joystick
244 static HRESULT WINAPI
JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface
)
246 ICOM_THIS(JoystickAImpl
,iface
);
248 TRACE("(this=%p)\n",This
);
249 if (This
->joyfd
!=-1) {
256 #define map_axis(val) ((val+32768)*(This->lMax-This->lMin)/65536+This->lMin)
258 static void joy_polldev(JoystickAImpl
*This
) {
266 memset(&tv
,0,sizeof(tv
));
267 FD_ZERO(&readfds
);FD_SET(This
->joyfd
,&readfds
);
268 if (1>select(This
->joyfd
+1,&readfds
,NULL
,NULL
,&tv
))
270 /* we have one event, so we can read */
271 if (sizeof(jse
)!=read(This
->joyfd
,&jse
,sizeof(jse
))) {
274 TRACE("js_event: type 0x%x, number %d, value %d\n",jse
.type
,jse
.number
,jse
.value
);
275 if (jse
.type
& JS_EVENT_BUTTON
) {
276 GEN_EVENT(DIJOFS_BUTTON(jse
.number
),jse
.value
?0x80:0x00,jse
.time
,(This
->dinput
->evsequence
)++);
277 This
->js
.rgbButtons
[jse
.number
] = jse
.value
?0x80:0x00;
279 if (jse
.type
& JS_EVENT_AXIS
) {
280 switch (jse
.number
) {
282 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
283 This
->js
.lX
= map_axis(jse
.value
);
286 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
287 This
->js
.lY
= map_axis(jse
.value
);
290 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
291 This
->js
.lZ
= map_axis(jse
.value
);
294 FIXME("more than 3 axes (%d) not handled!\n",jse
.number
);
301 /******************************************************************************
302 * GetDeviceState : returns the "state" of the joystick.
305 static HRESULT WINAPI
JoystickAImpl_GetDeviceState(
306 LPDIRECTINPUTDEVICE8A iface
,DWORD len
,LPVOID ptr
308 ICOM_THIS(JoystickAImpl
,iface
);
311 TRACE("(this=%p,0x%08lx,%p)\n",This
,len
,ptr
);
312 if (len
!= sizeof(DIJOYSTATE
)) {
313 FIXME("len %ld is not sizeof(DIJOYSTATE), unsupported format.\n",len
);
315 memcpy(ptr
,&(This
->js
),len
);
316 This
->queue_head
= 0;
317 This
->queue_tail
= 0;
321 /******************************************************************************
322 * GetDeviceData : gets buffered input data.
324 static HRESULT WINAPI
JoystickAImpl_GetDeviceData(LPDIRECTINPUTDEVICE8A iface
,
326 LPDIDEVICEOBJECTDATA dod
,
330 ICOM_THIS(JoystickAImpl
,iface
);
332 FIXME("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx),STUB!\n",This
,dodsize
,*entries
,flags
);
335 if (flags
& DIGDD_PEEK
)
336 FIXME("DIGDD_PEEK\n");
344 /******************************************************************************
345 * SetProperty : change input device properties
347 static HRESULT WINAPI
JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface
,
351 ICOM_THIS(JoystickAImpl
,iface
);
353 FIXME("(this=%p,%s,%p)\n",This
,debugstr_guid(rguid
),ph
);
354 FIXME("ph.dwSize = %ld, ph.dwHeaderSize =%ld, ph.dwObj = %ld, ph.dwHow= %ld\n",ph
->dwSize
, ph
->dwHeaderSize
,ph
->dwObj
,ph
->dwHow
);
356 if (!HIWORD(rguid
)) {
357 switch ((DWORD
)rguid
) {
358 case (DWORD
) DIPROP_BUFFERSIZE
: {
359 LPCDIPROPDWORD pd
= (LPCDIPROPDWORD
)ph
;
361 FIXME("buffersize = %ld\n",pd
->dwData
);
364 case (DWORD
)DIPROP_RANGE
: {
365 LPCDIPROPRANGE pr
= (LPCDIPROPRANGE
)ph
;
367 FIXME("proprange(%ld,%ld)\n",pr
->lMin
,pr
->lMax
);
368 This
->lMin
= pr
->lMin
;
369 This
->lMax
= pr
->lMax
;
372 case (DWORD
)DIPROP_DEADZONE
: {
373 LPCDIPROPDWORD pd
= (LPCDIPROPDWORD
)ph
;
375 FIXME("deadzone(%ld)\n",pd
->dwData
);
376 This
->deadzone
= pd
->dwData
;
380 FIXME("Unknown type %ld (%s)\n",(DWORD
)rguid
,debugstr_guid(rguid
));
387 /******************************************************************************
388 * SetEventNotification : specifies event to be sent on state change
390 static HRESULT WINAPI
JoystickAImpl_SetEventNotification(
391 LPDIRECTINPUTDEVICE8A iface
, HANDLE hnd
393 ICOM_THIS(JoystickAImpl
,iface
);
395 TRACE("(this=%p,0x%08lx)\n",This
,(DWORD
)hnd
);
400 static HRESULT WINAPI
JoystickAImpl_GetCapabilities(
401 LPDIRECTINPUTDEVICE8A iface
,
402 LPDIDEVCAPS lpDIDevCaps
)
404 ICOM_THIS(JoystickAImpl
,iface
);
406 int xfd
= This
->joyfd
;
408 TRACE("%p->(%p)\n",iface
,lpDIDevCaps
);
410 xfd
= open(JOYDEV
,O_RDONLY
);
411 lpDIDevCaps
->dwFlags
= DIDC_ATTACHED
;
412 lpDIDevCaps
->dwDevType
= DIDEVTYPE_JOYSTICK
;
414 if (-1==ioctl(xfd
,JSIOCGAXES
,&axes
))
416 lpDIDevCaps
->dwAxes
= axes
;
419 if (-1==ioctl(xfd
,JSIOCGAXES
,&buttons
))
421 lpDIDevCaps
->dwButtons
= buttons
;
423 if (xfd
!=This
->joyfd
)
427 static HRESULT WINAPI
JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface
) {
428 ICOM_THIS(JoystickAImpl
,iface
);
435 /******************************************************************************
436 * EnumObjects : enumerate the different buttons and axis...
438 static HRESULT WINAPI
JoystickAImpl_EnumObjects(
439 LPDIRECTINPUTDEVICE8A iface
,
440 LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback
,
444 ICOM_THIS(JoystickAImpl
,iface
);
445 DIDEVICEOBJECTINSTANCEA ddoi
;
446 int xfd
= This
->joyfd
;
448 TRACE("(this=%p,%p,%p,%08lx)\n", This
, lpCallback
, lpvRef
, dwFlags
);
449 if (TRACE_ON(dinput
)) {
450 DPRINTF(" - flags = ");
451 _dump_EnumObjects_flags(dwFlags
);
455 /* Only the fields till dwFFMaxForce are relevant */
456 ddoi
.dwSize
= FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA
, dwFFMaxForce
);
458 /* For the joystick, do as is done in the GetCapabilities function */
459 if ((dwFlags
== DIDFT_ALL
) ||
460 (dwFlags
& DIDFT_AXIS
)) {
464 if (-1==ioctl(xfd
,JSIOCGAXES
,&axes
))
468 for (i
= 0; i
< axes
; i
++) {
471 ddoi
.guidType
= GUID_XAxis
;
472 ddoi
.dwOfs
= DIJOFS_X
;
475 ddoi
.guidType
= GUID_YAxis
;
476 ddoi
.dwOfs
= DIJOFS_Y
;
479 ddoi
.guidType
= GUID_ZAxis
;
480 ddoi
.dwOfs
= DIJOFS_Z
;
483 ddoi
.guidType
= GUID_Unknown
;
484 ddoi
.dwOfs
= DIJOFS_Z
+ (i
- 2) * sizeof(LONG
);
486 ddoi
.dwType
= DIDFT_MAKEINSTANCE((0x0001 << i
) << WINE_JOYSTICK_AXIS_BASE
) | DIDFT_ABSAXIS
;
487 sprintf(ddoi
.tszName
, "%d-Axis", i
);
488 _dump_OBJECTINSTANCEA(&ddoi
);
489 if (lpCallback(&ddoi
, lpvRef
) != DIENUM_CONTINUE
) return DI_OK
;
493 if ((dwFlags
== DIDFT_ALL
) ||
494 (dwFlags
& DIDFT_BUTTON
)) {
498 if (-1==ioctl(xfd
,JSIOCGAXES
,&buttons
))
502 /* The DInput SDK says that GUID_Button is only for mouse buttons but well... */
503 ddoi
.guidType
= GUID_Button
;
505 for (i
= 0; i
< buttons
; i
++) {
506 ddoi
.dwOfs
= DIJOFS_BUTTON(i
);
507 ddoi
.dwType
= DIDFT_MAKEINSTANCE((0x0001 << i
) << WINE_JOYSTICK_BUTTON_BASE
) | DIDFT_PSHBUTTON
;
508 sprintf(ddoi
.tszName
, "%d-Button", i
);
509 _dump_OBJECTINSTANCEA(&ddoi
);
510 if (lpCallback(&ddoi
, lpvRef
) != DIENUM_CONTINUE
) return DI_OK
;
514 if (xfd
!=This
->joyfd
)
520 /******************************************************************************
521 * GetProperty : get input device properties
523 static HRESULT WINAPI
JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface
,
525 LPDIPROPHEADER pdiph
)
527 ICOM_THIS(JoystickAImpl
,iface
);
529 TRACE("(this=%p,%s,%p): stub!\n",
530 iface
, debugstr_guid(rguid
), pdiph
);
532 if (TRACE_ON(dinput
))
533 _dump_DIPROPHEADER(pdiph
);
535 if (!HIWORD(rguid
)) {
536 switch ((DWORD
)rguid
) {
537 case (DWORD
) DIPROP_BUFFERSIZE
: {
538 LPDIPROPDWORD pd
= (LPDIPROPDWORD
)pdiph
;
540 TRACE(" return buffersize = %d\n",This
->queue_len
);
541 pd
->dwData
= This
->queue_len
;
545 case (DWORD
) DIPROP_RANGE
: {
546 LPDIPROPRANGE pr
= (LPDIPROPRANGE
) pdiph
;
548 if ((pdiph
->dwHow
== DIPH_BYID
) &&
549 (pdiph
->dwObj
& DIDFT_ABSAXIS
)) {
550 /* The app is querying the current range of the axis : return the lMin and lMax values */
551 pr
->lMin
= This
->lMin
;
552 pr
->lMax
= This
->lMax
;
559 FIXME("Unknown type %ld (%s)\n",(DWORD
)rguid
,debugstr_guid(rguid
));
568 static ICOM_VTABLE(IDirectInputDevice8A
) JoystickAvt
=
570 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
571 IDirectInputDevice2AImpl_QueryInterface
,
572 IDirectInputDevice2AImpl_AddRef
,
573 JoystickAImpl_Release
,
574 JoystickAImpl_GetCapabilities
,
575 JoystickAImpl_EnumObjects
,
576 JoystickAImpl_GetProperty
,
577 JoystickAImpl_SetProperty
,
578 JoystickAImpl_Acquire
,
579 JoystickAImpl_Unacquire
,
580 JoystickAImpl_GetDeviceState
,
581 JoystickAImpl_GetDeviceData
,
582 JoystickAImpl_SetDataFormat
,
583 JoystickAImpl_SetEventNotification
,
584 IDirectInputDevice2AImpl_SetCooperativeLevel
,
585 IDirectInputDevice2AImpl_GetObjectInfo
,
586 IDirectInputDevice2AImpl_GetDeviceInfo
,
587 IDirectInputDevice2AImpl_RunControlPanel
,
588 IDirectInputDevice2AImpl_Initialize
,
589 IDirectInputDevice2AImpl_CreateEffect
,
590 IDirectInputDevice2AImpl_EnumEffects
,
591 IDirectInputDevice2AImpl_GetEffectInfo
,
592 IDirectInputDevice2AImpl_GetForceFeedbackState
,
593 IDirectInputDevice2AImpl_SendForceFeedbackCommand
,
594 IDirectInputDevice2AImpl_EnumCreatedEffectObjects
,
595 IDirectInputDevice2AImpl_Escape
,
597 IDirectInputDevice2AImpl_SendDeviceData
,
598 IDirectInputDevice7AImpl_EnumEffectsInFile
,
599 IDirectInputDevice7AImpl_WriteEffectToFile
,
600 IDirectInputDevice8AImpl_BuildActionMap
,
601 IDirectInputDevice8AImpl_SetActionMap
,
602 IDirectInputDevice8AImpl_GetImageInfo
605 #endif /* HAVE_LINUX_22_JOYSTICK_API */