1 /* DirectInput Joystick device
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #ifdef HAVE_LINUX_22_JOYSTICK_API
30 #include <sys/fcntl.h>
31 #ifdef HAVE_SYS_IOCTL_H
32 # include <sys/ioctl.h>
35 #ifdef HAVE_SYS_ERRNO_H
36 # include <sys/errno.h>
38 #ifdef HAVE_LINUX_JOYSTICK_H
39 # include <linux/joystick.h>
41 #define JOYDEV "/dev/js0"
43 #include "wine/debug.h"
49 #include "dinput_private.h"
50 #include "device_private.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(dinput
);
54 /* Wine joystick driver object instances */
55 #define WINE_JOYSTICK_AXIS_BASE 0
56 #define WINE_JOYSTICK_BUTTON_BASE 8
58 typedef struct JoystickAImpl JoystickAImpl
;
59 static ICOM_VTABLE(IDirectInputDevice2A
) JoystickAvt
;
60 static ICOM_VTABLE(IDirectInputDevice7A
) Joystick7Avt
;
63 /* IDirectInputDevice2AImpl */
64 ICOM_VFIELD(IDirectInputDevice2A
);
68 /* The 'parent' DInput */
69 IDirectInputAImpl
*dinput
;
71 /* joystick private */
75 LONG lMin
,lMax
,deadzone
;
76 LPDIDEVICEOBJECTDATA data_queue
;
77 int queue_head
, queue_tail
, queue_len
;
81 static GUID DInput_Wine_Joystick_GUID
= { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
85 {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
88 static BOOL
joydev_enum_device(DWORD dwDevType
, DWORD dwFlags
, LPCDIDEVICEINSTANCEA lpddi
)
90 if ((dwDevType
==0) || (GET_DIDEVICE_TYPE(dwDevType
)==DIDEVTYPE_JOYSTICK
)) {
91 /* check whether we have a joystick */
92 if ((access(JOYDEV
,O_RDONLY
) != -1) || (errno
!=ENODEV
&& errno
!=ENOENT
)) {
93 TRACE("Enumerating the Joystick device\n");
96 lpddi
->guidInstance
= GUID_Joystick
;
97 lpddi
->guidProduct
= DInput_Wine_Joystick_GUID
;
98 /* we only support traditional joysticks for now */
99 lpddi
->dwDevType
= DIDEVTYPE_JOYSTICK
|
100 (DIDEVTYPEJOYSTICK_TRADITIONAL
<<8);
101 strcpy(lpddi
->tszInstanceName
, "Joystick");
102 /* ioctl JSIOCGNAME(len) */
103 strcpy(lpddi
->tszProductName
, "Wine Joystick");
112 static JoystickAImpl
*alloc_device(REFGUID rguid
, ICOM_VTABLE(IDirectInputDevice2A
) *jvt
, IDirectInputAImpl
*dinput
)
114 JoystickAImpl
* newDevice
;
116 newDevice
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(JoystickAImpl
));
118 ICOM_VTBL(newDevice
) = jvt
;
119 newDevice
->joyfd
= -1;
120 newDevice
->lMin
= -32768;
121 newDevice
->lMax
= +32767;
122 newDevice
->dinput
= dinput
;
123 memcpy(&(newDevice
->guid
),rguid
,sizeof(*rguid
));
128 static HRESULT
joydev_create_device(IDirectInputAImpl
*dinput
, REFGUID rguid
, REFIID riid
, LPDIRECTINPUTDEVICEA
* pdev
)
130 if ((IsEqualGUID(&GUID_Joystick
,rguid
)) ||
131 (IsEqualGUID(&DInput_Wine_Joystick_GUID
,rguid
))) {
132 if ((riid
== NULL
) || (IsEqualGUID(&IID_IDirectInputDevice2A
,riid
)) || (IsEqualGUID(&IID_IDirectInputDevice2A
,riid
))) {
133 *pdev
=(IDirectInputDeviceA
*) alloc_device(rguid
, &JoystickAvt
, dinput
);
135 TRACE("Creating a Joystick device (%p)\n", *pdev
);
137 } else if (IsEqualGUID(&IID_IDirectInputDevice7A
,riid
)) {
138 *pdev
=(IDirectInputDeviceA
*) alloc_device(rguid
, (ICOM_VTABLE(IDirectInputDevice2A
) *) &Joystick7Avt
, dinput
);
140 TRACE("Creating a Joystick DInput7A device (%p)\n", *pdev
);
143 return DIERR_NOINTERFACE
;
146 return DIERR_DEVICENOTREG
;
149 static dinput_device joydev
= {
155 DECL_GLOBAL_CONSTRUCTOR(joydev_register
) { dinput_register_device(&joydev
); }
157 /******************************************************************************
160 static ULONG WINAPI
JoystickAImpl_Release(LPDIRECTINPUTDEVICE2A iface
)
162 ICOM_THIS(JoystickAImpl
,iface
);
168 /* Free the data queue */
169 if (This
->data_queue
!= NULL
)
170 HeapFree(GetProcessHeap(),0,This
->data_queue
);
172 /* Free the DataFormat */
173 HeapFree(GetProcessHeap(), 0, This
->df
);
175 HeapFree(GetProcessHeap(),0,This
);
179 /******************************************************************************
180 * SetDataFormat : the application can choose the format of the data
181 * the device driver sends back with GetDeviceState.
183 static HRESULT WINAPI
JoystickAImpl_SetDataFormat(
184 LPDIRECTINPUTDEVICE2A iface
,LPCDIDATAFORMAT df
187 ICOM_THIS(JoystickAImpl
,iface
);
190 TRACE("(this=%p,%p)\n",This
,df
);
192 TRACE("(df.dwSize=%ld)\n",df
->dwSize
);
193 TRACE("(df.dwObjsize=%ld)\n",df
->dwObjSize
);
194 TRACE("(df.dwFlags=0x%08lx)\n",df
->dwFlags
);
195 TRACE("(df.dwDataSize=%ld)\n",df
->dwDataSize
);
196 TRACE("(df.dwNumObjs=%ld)\n",df
->dwNumObjs
);
198 for (i
=0;i
<df
->dwNumObjs
;i
++) {
199 TRACE("df.rgodf[%d].guid %s (%p)\n",i
,debugstr_guid(df
->rgodf
[i
].pguid
), df
->rgodf
[i
].pguid
);
200 TRACE("df.rgodf[%d].dwOfs %ld\n",i
,df
->rgodf
[i
].dwOfs
);
201 TRACE("dwType 0x%02x,dwInstance %d\n",DIDFT_GETTYPE(df
->rgodf
[i
].dwType
),DIDFT_GETINSTANCE(df
->rgodf
[i
].dwType
));
202 TRACE("df.rgodf[%d].dwFlags 0x%08lx\n",i
,df
->rgodf
[i
].dwFlags
);
205 /* Store the new data format */
206 This
->df
= HeapAlloc(GetProcessHeap(),0,df
->dwSize
);
207 memcpy(This
->df
, df
, df
->dwSize
);
208 This
->df
->rgodf
= HeapAlloc(GetProcessHeap(),0,df
->dwNumObjs
*df
->dwObjSize
);
209 memcpy(This
->df
->rgodf
,df
->rgodf
,df
->dwNumObjs
*df
->dwObjSize
);
214 /******************************************************************************
215 * Acquire : gets exclusive control of the joystick
217 static HRESULT WINAPI
JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE2A iface
)
219 ICOM_THIS(JoystickAImpl
,iface
);
221 TRACE("(this=%p)\n",This
);
224 This
->joyfd
=open(JOYDEV
,O_RDONLY
);
226 return DIERR_NOTFOUND
;
230 /******************************************************************************
231 * Unacquire : frees the joystick
233 static HRESULT WINAPI
JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE2A iface
)
235 ICOM_THIS(JoystickAImpl
,iface
);
237 TRACE("(this=%p)\n",This
);
238 if (This
->joyfd
!=-1) {
245 #define map_axis(val) ((val+32768)*(This->lMax-This->lMin)/65536+This->lMin)
247 static void joy_polldev(JoystickAImpl
*This
) {
255 memset(&tv
,0,sizeof(tv
));
256 FD_ZERO(&readfds
);FD_SET(This
->joyfd
,&readfds
);
257 if (1>select(This
->joyfd
+1,&readfds
,NULL
,NULL
,&tv
))
259 /* we have one event, so we can read */
260 if (sizeof(jse
)!=read(This
->joyfd
,&jse
,sizeof(jse
))) {
263 TRACE("js_event: type 0x%x, number %d, value %d\n",jse
.type
,jse
.number
,jse
.value
);
264 if (jse
.type
& JS_EVENT_BUTTON
) {
265 GEN_EVENT(DIJOFS_BUTTON(jse
.number
),jse
.value
?0x80:0x00,jse
.time
,(This
->dinput
->evsequence
)++);
266 This
->js
.rgbButtons
[jse
.number
] = jse
.value
?0x80:0x00;
268 if (jse
.type
& JS_EVENT_AXIS
) {
269 switch (jse
.number
) {
271 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
272 This
->js
.lX
= map_axis(jse
.value
);
275 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
276 This
->js
.lY
= map_axis(jse
.value
);
279 GEN_EVENT(jse
.number
*4,jse
.value
,jse
.time
,(This
->dinput
->evsequence
)++);
280 This
->js
.lZ
= map_axis(jse
.value
);
283 FIXME("more than 3 axes (%d) not handled!\n",jse
.number
);
290 /******************************************************************************
291 * GetDeviceState : returns the "state" of the joystick.
294 static HRESULT WINAPI
JoystickAImpl_GetDeviceState(
295 LPDIRECTINPUTDEVICE2A iface
,DWORD len
,LPVOID ptr
297 ICOM_THIS(JoystickAImpl
,iface
);
300 TRACE("(this=%p,0x%08lx,%p)\n",This
,len
,ptr
);
301 if (len
!= sizeof(DIJOYSTATE
)) {
302 FIXME("len %ld is not sizeof(DIJOYSTATE), unsupported format.\n",len
);
304 memcpy(ptr
,&(This
->js
),len
);
305 This
->queue_head
= 0;
306 This
->queue_tail
= 0;
310 /******************************************************************************
311 * GetDeviceData : gets buffered input data.
313 static HRESULT WINAPI
JoystickAImpl_GetDeviceData(LPDIRECTINPUTDEVICE2A iface
,
315 LPDIDEVICEOBJECTDATA dod
,
319 ICOM_THIS(JoystickAImpl
,iface
);
321 FIXME("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx),STUB!\n",This
,dodsize
,*entries
,flags
);
324 if (flags
& DIGDD_PEEK
)
325 FIXME("DIGDD_PEEK\n");
333 /******************************************************************************
334 * SetProperty : change input device properties
336 static HRESULT WINAPI
JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE2A iface
,
340 ICOM_THIS(JoystickAImpl
,iface
);
342 FIXME("(this=%p,%s,%p)\n",This
,debugstr_guid(rguid
),ph
);
343 FIXME("ph.dwSize = %ld, ph.dwHeaderSize =%ld, ph.dwObj = %ld, ph.dwHow= %ld\n",ph
->dwSize
, ph
->dwHeaderSize
,ph
->dwObj
,ph
->dwHow
);
345 if (!HIWORD(rguid
)) {
346 switch ((DWORD
)rguid
) {
347 case (DWORD
) DIPROP_BUFFERSIZE
: {
348 LPCDIPROPDWORD pd
= (LPCDIPROPDWORD
)ph
;
350 FIXME("buffersize = %ld\n",pd
->dwData
);
353 case (DWORD
)DIPROP_RANGE
: {
354 LPCDIPROPRANGE pr
= (LPCDIPROPRANGE
)ph
;
356 FIXME("proprange(%ld,%ld)\n",pr
->lMin
,pr
->lMax
);
357 This
->lMin
= pr
->lMin
;
358 This
->lMax
= pr
->lMax
;
361 case (DWORD
)DIPROP_DEADZONE
: {
362 LPCDIPROPDWORD pd
= (LPCDIPROPDWORD
)ph
;
364 FIXME("deadzone(%ld)\n",pd
->dwData
);
365 This
->deadzone
= pd
->dwData
;
369 FIXME("Unknown type %ld (%s)\n",(DWORD
)rguid
,debugstr_guid(rguid
));
376 /******************************************************************************
377 * SetEventNotification : specifies event to be sent on state change
379 static HRESULT WINAPI
JoystickAImpl_SetEventNotification(
380 LPDIRECTINPUTDEVICE2A iface
, HANDLE hnd
382 ICOM_THIS(JoystickAImpl
,iface
);
384 TRACE("(this=%p,0x%08lx)\n",This
,(DWORD
)hnd
);
389 static HRESULT WINAPI
JoystickAImpl_GetCapabilities(
390 LPDIRECTINPUTDEVICE2A iface
,
391 LPDIDEVCAPS lpDIDevCaps
)
393 ICOM_THIS(JoystickAImpl
,iface
);
395 int xfd
= This
->joyfd
;
397 TRACE("%p->(%p)\n",iface
,lpDIDevCaps
);
399 xfd
= open(JOYDEV
,O_RDONLY
);
400 lpDIDevCaps
->dwFlags
= DIDC_ATTACHED
;
401 lpDIDevCaps
->dwDevType
= DIDEVTYPE_JOYSTICK
;
403 if (-1==ioctl(xfd
,JSIOCGAXES
,&axes
))
405 lpDIDevCaps
->dwAxes
= axes
;
408 if (-1==ioctl(xfd
,JSIOCGAXES
,&buttons
))
410 lpDIDevCaps
->dwButtons
= buttons
;
412 if (xfd
!=This
->joyfd
)
416 static HRESULT WINAPI
JoystickAImpl_Poll(LPDIRECTINPUTDEVICE2A iface
) {
417 ICOM_THIS(JoystickAImpl
,iface
);
424 /******************************************************************************
425 * EnumObjects : enumerate the different buttons and axis...
427 static HRESULT WINAPI
JoystickAImpl_EnumObjects(
428 LPDIRECTINPUTDEVICE2A iface
,
429 LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback
,
433 ICOM_THIS(JoystickAImpl
,iface
);
434 DIDEVICEOBJECTINSTANCEA ddoi
;
435 int xfd
= This
->joyfd
;
437 TRACE("(this=%p,%p,%p,%08lx)\n", This
, lpCallback
, lpvRef
, dwFlags
);
438 if (TRACE_ON(dinput
)) {
439 DPRINTF(" - flags = ");
440 _dump_EnumObjects_flags(dwFlags
);
444 /* Only the fields till dwFFMaxForce are relevant */
445 ddoi
.dwSize
= FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA
, dwFFMaxForce
);
447 /* For the joystick, do as is done in the GetCapabilities function */
448 if ((dwFlags
== DIDFT_ALL
) ||
449 (dwFlags
& DIDFT_AXIS
)) {
453 if (-1==ioctl(xfd
,JSIOCGAXES
,&axes
))
457 for (i
= 0; i
< axes
; i
++) {
460 ddoi
.guidType
= GUID_XAxis
;
461 ddoi
.dwOfs
= DIJOFS_X
;
464 ddoi
.guidType
= GUID_YAxis
;
465 ddoi
.dwOfs
= DIJOFS_Y
;
468 ddoi
.guidType
= GUID_ZAxis
;
469 ddoi
.dwOfs
= DIJOFS_Z
;
472 ddoi
.guidType
= GUID_Unknown
;
473 ddoi
.dwOfs
= DIJOFS_Z
+ (i
- 2) * sizeof(LONG
);
475 ddoi
.dwType
= DIDFT_MAKEINSTANCE((0x0001 << i
) << WINE_JOYSTICK_AXIS_BASE
) | DIDFT_ABSAXIS
;
476 sprintf(ddoi
.tszName
, "%d-Axis", i
);
477 _dump_OBJECTINSTANCEA(&ddoi
);
478 if (lpCallback(&ddoi
, lpvRef
) != DIENUM_CONTINUE
) return DI_OK
;
482 if ((dwFlags
== DIDFT_ALL
) ||
483 (dwFlags
& DIDFT_BUTTON
)) {
487 if (-1==ioctl(xfd
,JSIOCGAXES
,&buttons
))
491 /* The DInput SDK says that GUID_Button is only for mouse buttons but well... */
492 ddoi
.guidType
= GUID_Button
;
494 for (i
= 0; i
< buttons
; i
++) {
495 ddoi
.dwOfs
= DIJOFS_BUTTON(i
);
496 ddoi
.dwType
= DIDFT_MAKEINSTANCE((0x0001 << i
) << WINE_JOYSTICK_BUTTON_BASE
) | DIDFT_PSHBUTTON
;
497 sprintf(ddoi
.tszName
, "%d-Button", i
);
498 _dump_OBJECTINSTANCEA(&ddoi
);
499 if (lpCallback(&ddoi
, lpvRef
) != DIENUM_CONTINUE
) return DI_OK
;
503 if (xfd
!=This
->joyfd
)
509 /******************************************************************************
510 * GetProperty : get input device properties
512 static HRESULT WINAPI
JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE2A iface
,
514 LPDIPROPHEADER pdiph
)
516 ICOM_THIS(JoystickAImpl
,iface
);
518 TRACE("(this=%p,%s,%p): stub!\n",
519 iface
, debugstr_guid(rguid
), pdiph
);
521 if (TRACE_ON(dinput
))
522 _dump_DIPROPHEADER(pdiph
);
524 if (!HIWORD(rguid
)) {
525 switch ((DWORD
)rguid
) {
526 case (DWORD
) DIPROP_BUFFERSIZE
: {
527 LPDIPROPDWORD pd
= (LPDIPROPDWORD
)pdiph
;
529 TRACE(" return buffersize = %d\n",This
->queue_len
);
530 pd
->dwData
= This
->queue_len
;
534 case (DWORD
) DIPROP_RANGE
: {
535 LPDIPROPRANGE pr
= (LPDIPROPRANGE
) pdiph
;
537 if ((pdiph
->dwHow
== DIPH_BYID
) &&
538 (pdiph
->dwObj
& DIDFT_ABSAXIS
)) {
539 /* The app is querying the current range of the axis : return the lMin and lMax values */
540 pr
->lMin
= This
->lMin
;
541 pr
->lMax
= This
->lMax
;
548 FIXME("Unknown type %ld (%s)\n",(DWORD
)rguid
,debugstr_guid(rguid
));
557 static ICOM_VTABLE(IDirectInputDevice2A
) JoystickAvt
=
559 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
560 IDirectInputDevice2AImpl_QueryInterface
,
561 IDirectInputDevice2AImpl_AddRef
,
562 JoystickAImpl_Release
,
563 JoystickAImpl_GetCapabilities
,
564 JoystickAImpl_EnumObjects
,
565 JoystickAImpl_GetProperty
,
566 JoystickAImpl_SetProperty
,
567 JoystickAImpl_Acquire
,
568 JoystickAImpl_Unacquire
,
569 JoystickAImpl_GetDeviceState
,
570 JoystickAImpl_GetDeviceData
,
571 JoystickAImpl_SetDataFormat
,
572 JoystickAImpl_SetEventNotification
,
573 IDirectInputDevice2AImpl_SetCooperativeLevel
,
574 IDirectInputDevice2AImpl_GetObjectInfo
,
575 IDirectInputDevice2AImpl_GetDeviceInfo
,
576 IDirectInputDevice2AImpl_RunControlPanel
,
577 IDirectInputDevice2AImpl_Initialize
,
578 IDirectInputDevice2AImpl_CreateEffect
,
579 IDirectInputDevice2AImpl_EnumEffects
,
580 IDirectInputDevice2AImpl_GetEffectInfo
,
581 IDirectInputDevice2AImpl_GetForceFeedbackState
,
582 IDirectInputDevice2AImpl_SendForceFeedbackCommand
,
583 IDirectInputDevice2AImpl_EnumCreatedEffectObjects
,
584 IDirectInputDevice2AImpl_Escape
,
586 IDirectInputDevice2AImpl_SendDeviceData
,
589 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
590 # define XCAST(fun) (typeof(Joystick7Avt.fun))
592 # define XCAST(fun) (void*)
595 static ICOM_VTABLE(IDirectInputDevice7A
) Joystick7Avt
=
597 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
598 XCAST(QueryInterface
)IDirectInputDevice2AImpl_QueryInterface
,
599 XCAST(AddRef
)IDirectInputDevice2AImpl_AddRef
,
600 XCAST(Release
)JoystickAImpl_Release
,
601 XCAST(GetCapabilities
)JoystickAImpl_GetCapabilities
,
602 XCAST(EnumObjects
)JoystickAImpl_EnumObjects
,
603 XCAST(GetProperty
)JoystickAImpl_GetProperty
,
604 XCAST(SetProperty
)JoystickAImpl_SetProperty
,
605 XCAST(Acquire
)JoystickAImpl_Acquire
,
606 XCAST(Unacquire
)JoystickAImpl_Unacquire
,
607 XCAST(GetDeviceState
)JoystickAImpl_GetDeviceState
,
608 XCAST(GetDeviceData
)JoystickAImpl_GetDeviceData
,
609 XCAST(SetDataFormat
)JoystickAImpl_SetDataFormat
,
610 XCAST(SetEventNotification
)JoystickAImpl_SetEventNotification
,
611 XCAST(SetCooperativeLevel
)IDirectInputDevice2AImpl_SetCooperativeLevel
,
612 XCAST(GetObjectInfo
)IDirectInputDevice2AImpl_GetObjectInfo
,
613 XCAST(GetDeviceInfo
)IDirectInputDevice2AImpl_GetDeviceInfo
,
614 XCAST(RunControlPanel
)IDirectInputDevice2AImpl_RunControlPanel
,
615 XCAST(Initialize
)IDirectInputDevice2AImpl_Initialize
,
616 XCAST(CreateEffect
)IDirectInputDevice2AImpl_CreateEffect
,
617 XCAST(EnumEffects
)IDirectInputDevice2AImpl_EnumEffects
,
618 XCAST(GetEffectInfo
)IDirectInputDevice2AImpl_GetEffectInfo
,
619 XCAST(GetForceFeedbackState
)IDirectInputDevice2AImpl_GetForceFeedbackState
,
620 XCAST(SendForceFeedbackCommand
)IDirectInputDevice2AImpl_SendForceFeedbackCommand
,
621 XCAST(EnumCreatedEffectObjects
)IDirectInputDevice2AImpl_EnumCreatedEffectObjects
,
622 XCAST(Escape
)IDirectInputDevice2AImpl_Escape
,
623 XCAST(Poll
)JoystickAImpl_Poll
,
624 XCAST(SendDeviceData
)IDirectInputDevice2AImpl_SendDeviceData
,
625 IDirectInputDevice7AImpl_EnumEffectsInFile
,
626 IDirectInputDevice7AImpl_WriteEffectToFile
631 #endif /* HAVE_LINUX_22_JOYSTICK_API */