1 /* DirectInput Joystick device
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
10 #ifdef HAVE_LINUX_22_JOYSTICK_API
17 #include <sys/fcntl.h>
18 #include <sys/ioctl.h>
20 #ifdef HAVE_SYS_ERRNO_H
21 # include <sys/errno.h>
23 #ifdef HAVE_LINUX_JOYSTICK_H
24 # include <linux/joystick.h>
26 #define JOYDEV "/dev/js0"
28 #include "debugtools.h"
34 #include "dinput_private.h"
35 #include "device_private.h"
37 DEFAULT_DEBUG_CHANNEL(dinput
);
39 /* Wine joystick driver object instances */
40 #define WINE_JOYSTICK_AXIS_BASE 0
41 #define WINE_JOYSTICK_BUTTON_BASE 8
43 typedef struct JoystickAImpl JoystickAImpl
;
44 static ICOM_VTABLE(IDirectInputDevice2A
) JoystickAvt
;
45 static ICOM_VTABLE(IDirectInputDevice7A
) Joystick7Avt
;
48 /* IDirectInputDevice2AImpl */
49 ICOM_VFIELD(IDirectInputDevice2A
);
53 /* The 'parent' DInput */
54 IDirectInputAImpl
*dinput
;
56 /* joystick private */
60 LONG lMin
,lMax
,deadzone
;
61 LPDIDEVICEOBJECTDATA data_queue
;
62 int queue_pos
, queue_len
;
66 static GUID DInput_Wine_Joystick_GUID
= { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
70 {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
73 static BOOL
joydev_enum_device(DWORD dwDevType
, DWORD dwFlags
, LPCDIDEVICEINSTANCEA lpddi
)
75 if ((dwDevType
==0) || (GET_DIDEVICE_TYPE(dwDevType
)==DIDEVTYPE_JOYSTICK
)) {
76 /* check whether we have a joystick */
77 if ((access(JOYDEV
,O_RDONLY
) != -1) || (errno
!=ENODEV
&& errno
!=ENOENT
)) {
78 TRACE("Enumerating the Joystick device\n");
81 lpddi
->guidInstance
= GUID_Joystick
;
82 lpddi
->guidProduct
= DInput_Wine_Joystick_GUID
;
83 /* we only support traditional joysticks for now */
84 lpddi
->dwDevType
= DIDEVTYPE_JOYSTICK
|
85 (DIDEVTYPEJOYSTICK_TRADITIONAL
<<8);
86 strcpy(lpddi
->tszInstanceName
, "Joystick");
87 /* ioctl JSIOCGNAME(len) */
88 strcpy(lpddi
->tszProductName
, "Wine Joystick");
97 static JoystickAImpl
*alloc_device(REFGUID rguid
, ICOM_VTABLE(IDirectInputDevice2A
) *jvt
, IDirectInputAImpl
*dinput
)
99 JoystickAImpl
* newDevice
;
101 newDevice
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(JoystickAImpl
));
103 ICOM_VTBL(newDevice
) = jvt
;
104 newDevice
->joyfd
= -1;
105 newDevice
->lMin
= -32768;
106 newDevice
->lMax
= +32767;
107 newDevice
->dinput
= dinput
;
108 memcpy(&(newDevice
->guid
),rguid
,sizeof(*rguid
));
113 static HRESULT
joydev_create_device(IDirectInputAImpl
*dinput
, REFGUID rguid
, REFIID riid
, LPDIRECTINPUTDEVICEA
* pdev
)
115 if ((IsEqualGUID(&GUID_Joystick
,rguid
)) ||
116 (IsEqualGUID(&DInput_Wine_Joystick_GUID
,rguid
))) {
117 if ((riid
== NULL
) || (IsEqualGUID(&IID_IDirectInputDevice2A
,riid
)) || (IsEqualGUID(&IID_IDirectInputDevice2A
,riid
))) {
118 *pdev
=(IDirectInputDeviceA
*) alloc_device(rguid
, &JoystickAvt
, dinput
);
120 TRACE("Creating a Joystick device (%p)\n", *pdev
);
122 } else if (IsEqualGUID(&IID_IDirectInputDevice7A
,riid
)) {
123 *pdev
=(IDirectInputDeviceA
*) alloc_device(rguid
, (ICOM_VTABLE(IDirectInputDevice2A
) *) &Joystick7Avt
, dinput
);
125 TRACE("Creating a Joystick DInput7A device (%p)\n", *pdev
);
128 return DIERR_NOINTERFACE
;
131 return DIERR_DEVICENOTREG
;
134 static dinput_device joydev
= {
140 DECL_GLOBAL_CONSTRUCTOR(joydev_register
) { dinput_register_device(&joydev
); }
142 /******************************************************************************
145 static ULONG WINAPI
JoystickAImpl_Release(LPDIRECTINPUTDEVICE2A iface
)
147 ICOM_THIS(JoystickAImpl
,iface
);
153 /* Free the data queue */
154 if (This
->data_queue
!= NULL
)
155 HeapFree(GetProcessHeap(),0,This
->data_queue
);
157 /* Free the DataFormat */
158 HeapFree(GetProcessHeap(), 0, This
->df
);
160 HeapFree(GetProcessHeap(),0,This
);
164 /******************************************************************************
165 * SetDataFormat : the application can choose the format of the data
166 * the device driver sends back with GetDeviceState.
168 static HRESULT WINAPI
JoystickAImpl_SetDataFormat(
169 LPDIRECTINPUTDEVICE2A iface
,LPCDIDATAFORMAT df
172 ICOM_THIS(JoystickAImpl
,iface
);
175 TRACE("(this=%p,%p)\n",This
,df
);
177 TRACE("(df.dwSize=%ld)\n",df
->dwSize
);
178 TRACE("(df.dwObjsize=%ld)\n",df
->dwObjSize
);
179 TRACE("(df.dwFlags=0x%08lx)\n",df
->dwFlags
);
180 TRACE("(df.dwDataSize=%ld)\n",df
->dwDataSize
);
181 TRACE("(df.dwNumObjs=%ld)\n",df
->dwNumObjs
);
183 for (i
=0;i
<df
->dwNumObjs
;i
++) {
184 TRACE("df.rgodf[%d].guid %s (%p)\n",i
,debugstr_guid(df
->rgodf
[i
].pguid
), df
->rgodf
[i
].pguid
);
185 TRACE("df.rgodf[%d].dwOfs %ld\n",i
,df
->rgodf
[i
].dwOfs
);
186 TRACE("dwType 0x%02x,dwInstance %d\n",DIDFT_GETTYPE(df
->rgodf
[i
].dwType
),DIDFT_GETINSTANCE(df
->rgodf
[i
].dwType
));
187 TRACE("df.rgodf[%d].dwFlags 0x%08lx\n",i
,df
->rgodf
[i
].dwFlags
);
190 /* Store the new data format */
191 This
->df
= HeapAlloc(GetProcessHeap(),0,df
->dwSize
);
192 memcpy(This
->df
, df
, df
->dwSize
);
193 This
->df
->rgodf
= HeapAlloc(GetProcessHeap(),0,df
->dwNumObjs
*df
->dwObjSize
);
194 memcpy(This
->df
->rgodf
,df
->rgodf
,df
->dwNumObjs
*df
->dwObjSize
);
199 /******************************************************************************
200 * Acquire : gets exclusive control of the joystick
202 static HRESULT WINAPI
JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE2A iface
)
204 ICOM_THIS(JoystickAImpl
,iface
);
206 TRACE("(this=%p)\n",This
);
209 This
->joyfd
=open(JOYDEV
,O_RDONLY
);
211 return DIERR_NOTFOUND
;
215 /******************************************************************************
216 * Unacquire : frees the joystick
218 static HRESULT WINAPI
JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE2A iface
)
220 ICOM_THIS(JoystickAImpl
,iface
);
222 TRACE("(this=%p)\n",This
);
223 if (This
->joyfd
!=-1) {
230 #define map_axis(val) ((val+32768)*(This->lMax-This->lMin)/65536+This->lMin)
232 static void joy_polldev(JoystickAImpl
*This
) {
240 memset(&tv
,0,sizeof(tv
));
241 FD_ZERO(&readfds
);FD_SET(This
->joyfd
,&readfds
);
242 if (1>select(This
->joyfd
+1,&readfds
,NULL
,NULL
,&tv
))
244 /* we have one event, so we can read */
245 if (sizeof(jse
)!=read(This
->joyfd
,&jse
,sizeof(jse
))) {
248 TRACE("js_event: type 0x%x, number %d, value %d\n",jse
.type
,jse
.number
,jse
.value
);
249 if (jse
.type
& JS_EVENT_BUTTON
) {
250 GEN_EVENT(DIJOFS_BUTTON(jse
.number
),jse
.value
?0x80:0x00,jse
.time
,(This
->dinput
->evsequence
)++);
251 This
->js
.rgbButtons
[jse
.number
] = jse
.value
?0x80:0x00;
253 if (jse
.type
& JS_EVENT_AXIS
) {
254 switch (jse
.number
) {
256 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
257 This
->js
.lX
= map_axis(jse
.value
);
260 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
261 This
->js
.lY
= map_axis(jse
.value
);
264 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
265 This
->js
.lZ
= map_axis(jse
.value
);
268 FIXME("more then 3 axes (%d) not handled!\n",jse
.number
);
275 /******************************************************************************
276 * GetDeviceState : returns the "state" of the joystick.
279 static HRESULT WINAPI
JoystickAImpl_GetDeviceState(
280 LPDIRECTINPUTDEVICE2A iface
,DWORD len
,LPVOID ptr
282 ICOM_THIS(JoystickAImpl
,iface
);
285 TRACE("(this=%p,0x%08lx,%p)\n",This
,len
,ptr
);
286 if (len
!= sizeof(DIJOYSTATE
)) {
287 FIXME("len %ld is not sizeof(DIJOYSTATE), unsupported format.\n",len
);
289 memcpy(ptr
,&(This
->js
),len
);
294 /******************************************************************************
295 * GetDeviceData : gets buffered input data.
297 static HRESULT WINAPI
JoystickAImpl_GetDeviceData(LPDIRECTINPUTDEVICE2A iface
,
299 LPDIDEVICEOBJECTDATA dod
,
303 ICOM_THIS(JoystickAImpl
,iface
);
305 FIXME("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx),STUB!\n",This
,dodsize
,*entries
,flags
);
308 if (flags
& DIGDD_PEEK
)
309 FIXME("DIGDD_PEEK\n");
317 /******************************************************************************
318 * SetProperty : change input device properties
320 static HRESULT WINAPI
JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE2A iface
,
324 ICOM_THIS(JoystickAImpl
,iface
);
326 FIXME("(this=%p,%s,%p)\n",This
,debugstr_guid(rguid
),ph
);
327 FIXME("ph.dwSize = %ld, ph.dwHeaderSize =%ld, ph.dwObj = %ld, ph.dwHow= %ld\n",ph
->dwSize
, ph
->dwHeaderSize
,ph
->dwObj
,ph
->dwHow
);
329 if (!HIWORD(rguid
)) {
330 switch ((DWORD
)rguid
) {
331 case (DWORD
) DIPROP_BUFFERSIZE
: {
332 LPCDIPROPDWORD pd
= (LPCDIPROPDWORD
)ph
;
334 FIXME("buffersize = %ld\n",pd
->dwData
);
337 case (DWORD
)DIPROP_RANGE
: {
338 LPCDIPROPRANGE pr
= (LPCDIPROPRANGE
)ph
;
340 FIXME("proprange(%ld,%ld)\n",pr
->lMin
,pr
->lMax
);
341 This
->lMin
= pr
->lMin
;
342 This
->lMax
= pr
->lMax
;
345 case (DWORD
)DIPROP_DEADZONE
: {
346 LPCDIPROPDWORD pd
= (LPCDIPROPDWORD
)ph
;
348 FIXME("deadzone(%ld)\n",pd
->dwData
);
349 This
->deadzone
= pd
->dwData
;
353 FIXME("Unknown type %ld (%s)\n",(DWORD
)rguid
,debugstr_guid(rguid
));
360 /******************************************************************************
361 * SetEventNotification : specifies event to be sent on state change
363 static HRESULT WINAPI
JoystickAImpl_SetEventNotification(
364 LPDIRECTINPUTDEVICE2A iface
, HANDLE hnd
366 ICOM_THIS(JoystickAImpl
,iface
);
368 TRACE("(this=%p,0x%08lx)\n",This
,(DWORD
)hnd
);
373 static HRESULT WINAPI
JoystickAImpl_GetCapabilities(
374 LPDIRECTINPUTDEVICE2A iface
,
375 LPDIDEVCAPS lpDIDevCaps
)
377 ICOM_THIS(JoystickAImpl
,iface
);
379 int xfd
= This
->joyfd
;
381 TRACE("%p->(%p)\n",iface
,lpDIDevCaps
);
383 xfd
= open(JOYDEV
,O_RDONLY
);
384 lpDIDevCaps
->dwFlags
= DIDC_ATTACHED
;
385 lpDIDevCaps
->dwDevType
= DIDEVTYPE_JOYSTICK
;
387 if (-1==ioctl(xfd
,JSIOCGAXES
,&axes
))
389 lpDIDevCaps
->dwAxes
= axes
;
392 if (-1==ioctl(xfd
,JSIOCGAXES
,&buttons
))
394 lpDIDevCaps
->dwButtons
= buttons
;
396 if (xfd
!=This
->joyfd
)
400 static HRESULT WINAPI
JoystickAImpl_Poll(LPDIRECTINPUTDEVICE2A iface
) {
401 ICOM_THIS(JoystickAImpl
,iface
);
408 /******************************************************************************
409 * EnumObjects : enumerate the different buttons and axis...
411 static HRESULT WINAPI
JoystickAImpl_EnumObjects(
412 LPDIRECTINPUTDEVICE2A iface
,
413 LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback
,
417 ICOM_THIS(JoystickAImpl
,iface
);
418 DIDEVICEOBJECTINSTANCEA ddoi
;
419 int xfd
= This
->joyfd
;
421 TRACE("(this=%p,%p,%p,%08lx)\n", This
, lpCallback
, lpvRef
, dwFlags
);
422 if (TRACE_ON(dinput
)) {
423 DPRINTF(" - flags = ");
424 _dump_EnumObjects_flags(dwFlags
);
428 /* Only the fields till dwFFMaxForce are relevant */
429 ddoi
.dwSize
= FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA
, dwFFMaxForce
);
431 /* For the joystick, do as is done in the GetCapabilities function */
432 if ((dwFlags
== DIDFT_ALL
) ||
433 (dwFlags
& DIDFT_AXIS
)) {
437 if (-1==ioctl(xfd
,JSIOCGAXES
,&axes
))
441 for (i
= 0; i
< axes
; i
++) {
444 ddoi
.guidType
= GUID_XAxis
;
445 ddoi
.dwOfs
= DIJOFS_X
;
448 ddoi
.guidType
= GUID_YAxis
;
449 ddoi
.dwOfs
= DIJOFS_Y
;
452 ddoi
.guidType
= GUID_ZAxis
;
453 ddoi
.dwOfs
= DIJOFS_Z
;
456 ddoi
.guidType
= GUID_Unknown
;
457 ddoi
.dwOfs
= DIJOFS_Z
+ (i
- 2) * sizeof(LONG
);
459 ddoi
.dwType
= DIDFT_MAKEINSTANCE((0x0001 << i
) << WINE_JOYSTICK_AXIS_BASE
) | DIDFT_ABSAXIS
;
460 sprintf(ddoi
.tszName
, "%d-Axis", i
);
461 _dump_OBJECTINSTANCEA(&ddoi
);
462 if (lpCallback(&ddoi
, lpvRef
) != DIENUM_CONTINUE
) return DI_OK
;
466 if ((dwFlags
== DIDFT_ALL
) ||
467 (dwFlags
& DIDFT_BUTTON
)) {
471 if (-1==ioctl(xfd
,JSIOCGAXES
,&buttons
))
475 /* The DInput SDK says that GUID_Button is only for mouse buttons but well... */
476 ddoi
.guidType
= GUID_Button
;
478 for (i
= 0; i
< buttons
; i
++) {
479 ddoi
.dwOfs
= DIJOFS_BUTTON(i
);
480 ddoi
.dwType
= DIDFT_MAKEINSTANCE((0x0001 << i
) << WINE_JOYSTICK_BUTTON_BASE
) | DIDFT_PSHBUTTON
;
481 sprintf(ddoi
.tszName
, "%d-Button", i
);
482 _dump_OBJECTINSTANCEA(&ddoi
);
483 if (lpCallback(&ddoi
, lpvRef
) != DIENUM_CONTINUE
) return DI_OK
;
487 if (xfd
!=This
->joyfd
)
493 /******************************************************************************
494 * GetProperty : get input device properties
496 static HRESULT WINAPI
JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE2A iface
,
498 LPDIPROPHEADER pdiph
)
500 ICOM_THIS(JoystickAImpl
,iface
);
502 TRACE("(this=%p,%s,%p): stub!\n",
503 iface
, debugstr_guid(rguid
), pdiph
);
505 if (TRACE_ON(dinput
))
506 _dump_DIPROPHEADER(pdiph
);
508 if (!HIWORD(rguid
)) {
509 switch ((DWORD
)rguid
) {
510 case (DWORD
) DIPROP_BUFFERSIZE
: {
511 LPDIPROPDWORD pd
= (LPDIPROPDWORD
)pdiph
;
513 TRACE(" return buffersize = %d\n",This
->queue_len
);
514 pd
->dwData
= This
->queue_len
;
518 case (DWORD
) DIPROP_RANGE
: {
519 LPDIPROPRANGE pr
= (LPDIPROPRANGE
) pdiph
;
521 if ((pdiph
->dwHow
== DIPH_BYID
) &&
522 (pdiph
->dwObj
& DIDFT_ABSAXIS
)) {
523 /* The app is querying the current range of the axis : return the lMin and lMax values */
524 pr
->lMin
= This
->lMin
;
525 pr
->lMax
= This
->lMax
;
532 FIXME("Unknown type %ld (%s)\n",(DWORD
)rguid
,debugstr_guid(rguid
));
541 static ICOM_VTABLE(IDirectInputDevice2A
) JoystickAvt
=
543 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
544 IDirectInputDevice2AImpl_QueryInterface
,
545 IDirectInputDevice2AImpl_AddRef
,
546 JoystickAImpl_Release
,
547 JoystickAImpl_GetCapabilities
,
548 JoystickAImpl_EnumObjects
,
549 JoystickAImpl_GetProperty
,
550 JoystickAImpl_SetProperty
,
551 JoystickAImpl_Acquire
,
552 JoystickAImpl_Unacquire
,
553 JoystickAImpl_GetDeviceState
,
554 JoystickAImpl_GetDeviceData
,
555 JoystickAImpl_SetDataFormat
,
556 JoystickAImpl_SetEventNotification
,
557 IDirectInputDevice2AImpl_SetCooperativeLevel
,
558 IDirectInputDevice2AImpl_GetObjectInfo
,
559 IDirectInputDevice2AImpl_GetDeviceInfo
,
560 IDirectInputDevice2AImpl_RunControlPanel
,
561 IDirectInputDevice2AImpl_Initialize
,
562 IDirectInputDevice2AImpl_CreateEffect
,
563 IDirectInputDevice2AImpl_EnumEffects
,
564 IDirectInputDevice2AImpl_GetEffectInfo
,
565 IDirectInputDevice2AImpl_GetForceFeedbackState
,
566 IDirectInputDevice2AImpl_SendForceFeedbackCommand
,
567 IDirectInputDevice2AImpl_EnumCreatedEffectObjects
,
568 IDirectInputDevice2AImpl_Escape
,
570 IDirectInputDevice2AImpl_SendDeviceData
,
573 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
574 # define XCAST(fun) (typeof(Joystick7Avt.fn##fun))
576 # define XCAST(fun) (void*)
579 static ICOM_VTABLE(IDirectInputDevice7A
) Joystick7Avt
=
581 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
582 XCAST(QueryInterface
)IDirectInputDevice2AImpl_QueryInterface
,
583 XCAST(AddRef
)IDirectInputDevice2AImpl_AddRef
,
584 XCAST(Release
)JoystickAImpl_Release
,
585 XCAST(GetCapabilities
)JoystickAImpl_GetCapabilities
,
586 XCAST(EnumObjects
)JoystickAImpl_EnumObjects
,
587 XCAST(GetProperty
)JoystickAImpl_GetProperty
,
588 XCAST(SetProperty
)JoystickAImpl_SetProperty
,
589 XCAST(Acquire
)JoystickAImpl_Acquire
,
590 XCAST(Unacquire
)JoystickAImpl_Unacquire
,
591 XCAST(GetDeviceState
)JoystickAImpl_GetDeviceState
,
592 XCAST(GetDeviceData
)JoystickAImpl_GetDeviceData
,
593 XCAST(SetDataFormat
)JoystickAImpl_SetDataFormat
,
594 XCAST(SetEventNotification
)JoystickAImpl_SetEventNotification
,
595 XCAST(SetCooperativeLevel
)IDirectInputDevice2AImpl_SetCooperativeLevel
,
596 XCAST(GetObjectInfo
)IDirectInputDevice2AImpl_GetObjectInfo
,
597 XCAST(GetDeviceInfo
)IDirectInputDevice2AImpl_GetDeviceInfo
,
598 XCAST(RunControlPanel
)IDirectInputDevice2AImpl_RunControlPanel
,
599 XCAST(Initialize
)IDirectInputDevice2AImpl_Initialize
,
600 XCAST(CreateEffect
)IDirectInputDevice2AImpl_CreateEffect
,
601 XCAST(EnumEffects
)IDirectInputDevice2AImpl_EnumEffects
,
602 XCAST(GetEffectInfo
)IDirectInputDevice2AImpl_GetEffectInfo
,
603 XCAST(GetForceFeedbackState
)IDirectInputDevice2AImpl_GetForceFeedbackState
,
604 XCAST(SendForceFeedbackCommand
)IDirectInputDevice2AImpl_SendForceFeedbackCommand
,
605 XCAST(EnumCreatedEffectObjects
)IDirectInputDevice2AImpl_EnumCreatedEffectObjects
,
606 XCAST(Escape
)IDirectInputDevice2AImpl_Escape
,
607 XCAST(Poll
)JoystickAImpl_Poll
,
608 XCAST(SendDeviceData
)IDirectInputDevice2AImpl_SendDeviceData
,
609 IDirectInputDevice7AImpl_EnumEffectsInFile
,
610 IDirectInputDevice7AImpl_WriteEffectToFile
615 #endif /* HAVE_LINUX_22_JOYSTICK_API */