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) || (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
| DIDEVTYPEJOYSTICK_TRADITIONAL
;
85 strcpy(lpddi
->tszInstanceName
, "Joystick");
86 /* ioctl JSIOCGNAME(len) */
87 strcpy(lpddi
->tszProductName
, "Wine Joystick");
96 static JoystickAImpl
*alloc_device(REFGUID rguid
, ICOM_VTABLE(IDirectInputDevice2A
) *jvt
, IDirectInputAImpl
*dinput
)
98 JoystickAImpl
* newDevice
;
100 newDevice
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(JoystickAImpl
));
102 ICOM_VTBL(newDevice
) = jvt
;
103 newDevice
->joyfd
= -1;
104 newDevice
->lMin
= -32768;
105 newDevice
->lMax
= +32767;
106 newDevice
->dinput
= dinput
;
107 memcpy(&(newDevice
->guid
),rguid
,sizeof(*rguid
));
112 static HRESULT
joydev_create_device(IDirectInputAImpl
*dinput
, REFGUID rguid
, REFIID riid
, LPDIRECTINPUTDEVICEA
* pdev
)
114 if ((IsEqualGUID(&GUID_Joystick
,rguid
)) ||
115 (IsEqualGUID(&DInput_Wine_Joystick_GUID
,rguid
))) {
116 if ((riid
== NULL
) || (IsEqualGUID(&IID_IDirectInputDevice2A
,riid
)) || (IsEqualGUID(&IID_IDirectInputDevice2A
,riid
))) {
117 *pdev
=(IDirectInputDeviceA
*) alloc_device(rguid
, &JoystickAvt
, dinput
);
119 TRACE("Creating a Joystick device (%p)\n", *pdev
);
121 } else if (IsEqualGUID(&IID_IDirectInputDevice7A
,riid
)) {
122 *pdev
=(IDirectInputDeviceA
*) alloc_device(rguid
, (ICOM_VTABLE(IDirectInputDevice2A
) *) &Joystick7Avt
, dinput
);
124 TRACE("Creating a Joystick DInput7A device (%p)\n", *pdev
);
127 return DIERR_NOINTERFACE
;
130 return DIERR_DEVICENOTREG
;
133 static dinput_device joydev
= {
138 DECL_GLOBAL_CONSTRUCTOR(joydev_register
) { dinput_register_device(&joydev
); }
140 /******************************************************************************
143 static ULONG WINAPI
JoystickAImpl_Release(LPDIRECTINPUTDEVICE2A iface
)
145 ICOM_THIS(JoystickAImpl
,iface
);
151 /* Free the data queue */
152 if (This
->data_queue
!= NULL
)
153 HeapFree(GetProcessHeap(),0,This
->data_queue
);
155 /* Free the DataFormat */
156 HeapFree(GetProcessHeap(), 0, This
->df
);
158 HeapFree(GetProcessHeap(),0,This
);
162 /******************************************************************************
163 * SetDataFormat : the application can choose the format of the data
164 * the device driver sends back with GetDeviceState.
166 static HRESULT WINAPI
JoystickAImpl_SetDataFormat(
167 LPDIRECTINPUTDEVICE2A iface
,LPCDIDATAFORMAT df
170 ICOM_THIS(JoystickAImpl
,iface
);
173 TRACE("(this=%p,%p)\n",This
,df
);
175 TRACE("(df.dwSize=%ld)\n",df
->dwSize
);
176 TRACE("(df.dwObjsize=%ld)\n",df
->dwObjSize
);
177 TRACE("(df.dwFlags=0x%08lx)\n",df
->dwFlags
);
178 TRACE("(df.dwDataSize=%ld)\n",df
->dwDataSize
);
179 TRACE("(df.dwNumObjs=%ld)\n",df
->dwNumObjs
);
181 for (i
=0;i
<df
->dwNumObjs
;i
++) {
182 TRACE("df.rgodf[%d].guid %s (%p)\n",i
,debugstr_guid(df
->rgodf
[i
].pguid
), df
->rgodf
[i
].pguid
);
183 TRACE("df.rgodf[%d].dwOfs %ld\n",i
,df
->rgodf
[i
].dwOfs
);
184 TRACE("dwType 0x%02x,dwInstance %d\n",DIDFT_GETTYPE(df
->rgodf
[i
].dwType
),DIDFT_GETINSTANCE(df
->rgodf
[i
].dwType
));
185 TRACE("df.rgodf[%d].dwFlags 0x%08lx\n",i
,df
->rgodf
[i
].dwFlags
);
188 /* Store the new data format */
189 This
->df
= HeapAlloc(GetProcessHeap(),0,df
->dwSize
);
190 memcpy(This
->df
, df
, df
->dwSize
);
191 This
->df
->rgodf
= HeapAlloc(GetProcessHeap(),0,df
->dwNumObjs
*df
->dwObjSize
);
192 memcpy(This
->df
->rgodf
,df
->rgodf
,df
->dwNumObjs
*df
->dwObjSize
);
197 /******************************************************************************
198 * Acquire : gets exclusive control of the joystick
200 static HRESULT WINAPI
JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE2A iface
)
202 ICOM_THIS(JoystickAImpl
,iface
);
204 TRACE("(this=%p)\n",This
);
207 This
->joyfd
=open(JOYDEV
,O_RDONLY
);
209 return DIERR_NOTFOUND
;
213 /******************************************************************************
214 * Unacquire : frees the joystick
216 static HRESULT WINAPI
JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE2A iface
)
218 ICOM_THIS(JoystickAImpl
,iface
);
220 TRACE("(this=%p)\n",This
);
221 if (This
->joyfd
!=-1) {
228 #define map_axis(val) ((val+32768)*(This->lMax-This->lMin)/65536+This->lMin)
230 static void joy_polldev(JoystickAImpl
*This
) {
238 memset(&tv
,0,sizeof(tv
));
239 FD_ZERO(&readfds
);FD_SET(This
->joyfd
,&readfds
);
240 if (1>select(This
->joyfd
+1,&readfds
,NULL
,NULL
,&tv
))
242 /* we have one event, so we can read */
243 if (sizeof(jse
)!=read(This
->joyfd
,&jse
,sizeof(jse
))) {
246 TRACE("js_event: type 0x%x, number %d, value %d\n",jse
.type
,jse
.number
,jse
.value
);
247 if (jse
.type
& JS_EVENT_BUTTON
) {
248 GEN_EVENT(DIJOFS_BUTTON(jse
.number
),jse
.value
?0x80:0x00,jse
.time
,(This
->dinput
->evsequence
)++);
249 This
->js
.rgbButtons
[jse
.number
] = jse
.value
?0x80:0x00;
251 if (jse
.type
& JS_EVENT_AXIS
) {
252 switch (jse
.number
) {
254 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
255 This
->js
.lX
= map_axis(jse
.value
);
258 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
259 This
->js
.lY
= map_axis(jse
.value
);
262 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
263 This
->js
.lZ
= map_axis(jse
.value
);
266 FIXME("more then 3 axes (%d) not handled!\n",jse
.number
);
273 /******************************************************************************
274 * GetDeviceState : returns the "state" of the joystick.
277 static HRESULT WINAPI
JoystickAImpl_GetDeviceState(
278 LPDIRECTINPUTDEVICE2A iface
,DWORD len
,LPVOID ptr
280 ICOM_THIS(JoystickAImpl
,iface
);
283 TRACE("(this=%p,0x%08lx,%p)\n",This
,len
,ptr
);
284 if (len
!= sizeof(DIJOYSTATE
)) {
285 FIXME("len %ld is not sizeof(DIJOYSTATE), unsupported format.\n",len
);
287 memcpy(ptr
,&(This
->js
),len
);
292 /******************************************************************************
293 * GetDeviceData : gets buffered input data.
295 static HRESULT WINAPI
JoystickAImpl_GetDeviceData(LPDIRECTINPUTDEVICE2A iface
,
297 LPDIDEVICEOBJECTDATA dod
,
301 ICOM_THIS(JoystickAImpl
,iface
);
303 FIXME("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx),STUB!\n",This
,dodsize
,*entries
,flags
);
306 if (flags
& DIGDD_PEEK
)
307 FIXME("DIGDD_PEEK\n");
315 /******************************************************************************
316 * SetProperty : change input device properties
318 static HRESULT WINAPI
JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE2A iface
,
322 ICOM_THIS(JoystickAImpl
,iface
);
324 FIXME("(this=%p,%s,%p)\n",This
,debugstr_guid(rguid
),ph
);
325 FIXME("ph.dwSize = %ld, ph.dwHeaderSize =%ld, ph.dwObj = %ld, ph.dwHow= %ld\n",ph
->dwSize
, ph
->dwHeaderSize
,ph
->dwObj
,ph
->dwHow
);
327 if (!HIWORD(rguid
)) {
328 switch ((DWORD
)rguid
) {
329 case (DWORD
) DIPROP_BUFFERSIZE
: {
330 LPCDIPROPDWORD pd
= (LPCDIPROPDWORD
)ph
;
332 FIXME("buffersize = %ld\n",pd
->dwData
);
335 case (DWORD
)DIPROP_RANGE
: {
336 LPCDIPROPRANGE pr
= (LPCDIPROPRANGE
)ph
;
338 FIXME("proprange(%ld,%ld)\n",pr
->lMin
,pr
->lMax
);
339 This
->lMin
= pr
->lMin
;
340 This
->lMax
= pr
->lMax
;
343 case (DWORD
)DIPROP_DEADZONE
: {
344 LPCDIPROPDWORD pd
= (LPCDIPROPDWORD
)ph
;
346 FIXME("deadzone(%ld)\n",pd
->dwData
);
347 This
->deadzone
= pd
->dwData
;
351 FIXME("Unknown type %ld (%s)\n",(DWORD
)rguid
,debugstr_guid(rguid
));
358 /******************************************************************************
359 * SetEventNotification : specifies event to be sent on state change
361 static HRESULT WINAPI
JoystickAImpl_SetEventNotification(
362 LPDIRECTINPUTDEVICE2A iface
, HANDLE hnd
364 ICOM_THIS(JoystickAImpl
,iface
);
366 TRACE("(this=%p,0x%08lx)\n",This
,(DWORD
)hnd
);
371 static HRESULT WINAPI
JoystickAImpl_GetCapabilities(
372 LPDIRECTINPUTDEVICE2A iface
,
373 LPDIDEVCAPS lpDIDevCaps
)
375 ICOM_THIS(JoystickAImpl
,iface
);
377 int xfd
= This
->joyfd
;
379 TRACE("%p->(%p)\n",iface
,lpDIDevCaps
);
381 xfd
= open(JOYDEV
,O_RDONLY
);
382 lpDIDevCaps
->dwFlags
= DIDC_ATTACHED
;
383 lpDIDevCaps
->dwDevType
= DIDEVTYPE_JOYSTICK
;
385 if (-1==ioctl(xfd
,JSIOCGAXES
,&axes
))
387 lpDIDevCaps
->dwAxes
= axes
;
390 if (-1==ioctl(xfd
,JSIOCGAXES
,&buttons
))
392 lpDIDevCaps
->dwButtons
= buttons
;
394 if (xfd
!=This
->joyfd
)
398 static HRESULT WINAPI
JoystickAImpl_Poll(LPDIRECTINPUTDEVICE2A iface
) {
399 ICOM_THIS(JoystickAImpl
,iface
);
406 /******************************************************************************
407 * EnumObjects : enumerate the different buttons and axis...
409 static HRESULT WINAPI
JoystickAImpl_EnumObjects(
410 LPDIRECTINPUTDEVICE2A iface
,
411 LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback
,
415 ICOM_THIS(JoystickAImpl
,iface
);
416 DIDEVICEOBJECTINSTANCEA ddoi
;
417 int xfd
= This
->joyfd
;
419 TRACE("(this=%p,%p,%p,%08lx)\n", This
, lpCallback
, lpvRef
, dwFlags
);
420 if (TRACE_ON(dinput
)) {
421 DPRINTF(" - flags = ");
422 _dump_EnumObjects_flags(dwFlags
);
426 /* Only the fields till dwFFMaxForce are relevant */
427 ddoi
.dwSize
= FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA
, dwFFMaxForce
);
429 /* For the joystick, do as is done in the GetCapabilities function */
430 if ((dwFlags
== DIDFT_ALL
) ||
431 (dwFlags
& DIDFT_AXIS
)) {
435 if (-1==ioctl(xfd
,JSIOCGAXES
,&axes
))
439 for (i
= 0; i
< axes
; i
++) {
442 ddoi
.guidType
= GUID_XAxis
;
443 ddoi
.dwOfs
= DIJOFS_X
;
446 ddoi
.guidType
= GUID_YAxis
;
447 ddoi
.dwOfs
= DIJOFS_Y
;
450 ddoi
.guidType
= GUID_ZAxis
;
451 ddoi
.dwOfs
= DIJOFS_Z
;
454 ddoi
.guidType
= GUID_Unknown
;
455 ddoi
.dwOfs
= DIJOFS_Z
+ (i
- 2) * sizeof(LONG
);
457 ddoi
.dwType
= DIDFT_MAKEINSTANCE((0x0001 << i
) << WINE_JOYSTICK_AXIS_BASE
) | DIDFT_ABSAXIS
;
458 sprintf(ddoi
.tszName
, "%d-Axis", i
);
459 _dump_OBJECTINSTANCEA(&ddoi
);
460 if (lpCallback(&ddoi
, lpvRef
) != DIENUM_CONTINUE
) return DI_OK
;
464 if ((dwFlags
== DIDFT_ALL
) ||
465 (dwFlags
& DIDFT_BUTTON
)) {
469 if (-1==ioctl(xfd
,JSIOCGAXES
,&buttons
))
473 /* The DInput SDK says that GUID_Button is only for mouse buttons but well... */
474 ddoi
.guidType
= GUID_Button
;
476 for (i
= 0; i
< buttons
; i
++) {
477 ddoi
.dwOfs
= DIJOFS_BUTTON(i
);
478 ddoi
.dwType
= DIDFT_MAKEINSTANCE((0x0001 << i
) << WINE_JOYSTICK_BUTTON_BASE
) | DIDFT_PSHBUTTON
;
479 sprintf(ddoi
.tszName
, "%d-Button", i
);
480 _dump_OBJECTINSTANCEA(&ddoi
);
481 if (lpCallback(&ddoi
, lpvRef
) != DIENUM_CONTINUE
) return DI_OK
;
485 if (xfd
!=This
->joyfd
)
491 /******************************************************************************
492 * GetProperty : get input device properties
494 static HRESULT WINAPI
JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE2A iface
,
496 LPDIPROPHEADER pdiph
)
498 ICOM_THIS(JoystickAImpl
,iface
);
500 TRACE("(this=%p,%s,%p): stub!\n",
501 iface
, debugstr_guid(rguid
), pdiph
);
503 if (TRACE_ON(dinput
))
504 _dump_DIPROPHEADER(pdiph
);
506 if (!HIWORD(rguid
)) {
507 switch ((DWORD
)rguid
) {
508 case (DWORD
) DIPROP_BUFFERSIZE
: {
509 LPDIPROPDWORD pd
= (LPDIPROPDWORD
)pdiph
;
511 TRACE(" return buffersize = %d\n",This
->queue_len
);
512 pd
->dwData
= This
->queue_len
;
516 case (DWORD
) DIPROP_RANGE
: {
517 LPDIPROPRANGE pr
= (LPDIPROPRANGE
) pdiph
;
519 if ((pdiph
->dwHow
== DIPH_BYID
) &&
520 (pdiph
->dwObj
& DIDFT_ABSAXIS
)) {
521 /* The app is querying the current range of the axis : return the lMin and lMax values */
522 pr
->lMin
= This
->lMin
;
523 pr
->lMax
= This
->lMax
;
530 FIXME("Unknown type %ld (%s)\n",(DWORD
)rguid
,debugstr_guid(rguid
));
539 static ICOM_VTABLE(IDirectInputDevice2A
) JoystickAvt
=
541 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
542 IDirectInputDevice2AImpl_QueryInterface
,
543 IDirectInputDevice2AImpl_AddRef
,
544 JoystickAImpl_Release
,
545 JoystickAImpl_GetCapabilities
,
546 JoystickAImpl_EnumObjects
,
547 JoystickAImpl_GetProperty
,
548 JoystickAImpl_SetProperty
,
549 JoystickAImpl_Acquire
,
550 JoystickAImpl_Unacquire
,
551 JoystickAImpl_GetDeviceState
,
552 JoystickAImpl_GetDeviceData
,
553 JoystickAImpl_SetDataFormat
,
554 JoystickAImpl_SetEventNotification
,
555 IDirectInputDevice2AImpl_SetCooperativeLevel
,
556 IDirectInputDevice2AImpl_GetObjectInfo
,
557 IDirectInputDevice2AImpl_GetDeviceInfo
,
558 IDirectInputDevice2AImpl_RunControlPanel
,
559 IDirectInputDevice2AImpl_Initialize
,
560 IDirectInputDevice2AImpl_CreateEffect
,
561 IDirectInputDevice2AImpl_EnumEffects
,
562 IDirectInputDevice2AImpl_GetEffectInfo
,
563 IDirectInputDevice2AImpl_GetForceFeedbackState
,
564 IDirectInputDevice2AImpl_SendForceFeedbackCommand
,
565 IDirectInputDevice2AImpl_EnumCreatedEffectObjects
,
566 IDirectInputDevice2AImpl_Escape
,
568 IDirectInputDevice2AImpl_SendDeviceData
,
571 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
572 # define XCAST(fun) (typeof(Joystick7Avt.fn##fun))
574 # define XCAST(fun) (void*)
577 static ICOM_VTABLE(IDirectInputDevice7A
) Joystick7Avt
=
579 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
580 XCAST(QueryInterface
)IDirectInputDevice2AImpl_QueryInterface
,
581 XCAST(AddRef
)IDirectInputDevice2AImpl_AddRef
,
582 XCAST(Release
)JoystickAImpl_Release
,
583 XCAST(GetCapabilities
)JoystickAImpl_GetCapabilities
,
584 XCAST(EnumObjects
)JoystickAImpl_EnumObjects
,
585 XCAST(GetProperty
)JoystickAImpl_GetProperty
,
586 XCAST(SetProperty
)JoystickAImpl_SetProperty
,
587 XCAST(Acquire
)JoystickAImpl_Acquire
,
588 XCAST(Unacquire
)JoystickAImpl_Unacquire
,
589 XCAST(GetDeviceState
)JoystickAImpl_GetDeviceState
,
590 XCAST(GetDeviceData
)JoystickAImpl_GetDeviceData
,
591 XCAST(SetDataFormat
)JoystickAImpl_SetDataFormat
,
592 XCAST(SetEventNotification
)JoystickAImpl_SetEventNotification
,
593 XCAST(SetCooperativeLevel
)IDirectInputDevice2AImpl_SetCooperativeLevel
,
594 XCAST(GetObjectInfo
)IDirectInputDevice2AImpl_GetObjectInfo
,
595 XCAST(GetDeviceInfo
)IDirectInputDevice2AImpl_GetDeviceInfo
,
596 XCAST(RunControlPanel
)IDirectInputDevice2AImpl_RunControlPanel
,
597 XCAST(Initialize
)IDirectInputDevice2AImpl_Initialize
,
598 XCAST(CreateEffect
)IDirectInputDevice2AImpl_CreateEffect
,
599 XCAST(EnumEffects
)IDirectInputDevice2AImpl_EnumEffects
,
600 XCAST(GetEffectInfo
)IDirectInputDevice2AImpl_GetEffectInfo
,
601 XCAST(GetForceFeedbackState
)IDirectInputDevice2AImpl_GetForceFeedbackState
,
602 XCAST(SendForceFeedbackCommand
)IDirectInputDevice2AImpl_SendForceFeedbackCommand
,
603 XCAST(EnumCreatedEffectObjects
)IDirectInputDevice2AImpl_EnumCreatedEffectObjects
,
604 XCAST(Escape
)IDirectInputDevice2AImpl_Escape
,
605 XCAST(Poll
)JoystickAImpl_Poll
,
606 XCAST(SendDeviceData
)IDirectInputDevice2AImpl_SendDeviceData
,
607 IDirectInputDevice7AImpl_EnumEffectsInFile
,
608 IDirectInputDevice7AImpl_WriteEffectToFile
613 #endif /* HAVE_LINUX_22_JOYSTICK_API */