1 /* DirectInput Generic Joystick device
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2009 Aric Stewart, CodeWeavers
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "joystick_private.h"
30 #include "wine/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(dinput
);
34 /******************************************************************************
35 * SetProperty : change input device properties
37 HRESULT WINAPI
JoystickAGenericImpl_SetProperty(
38 LPDIRECTINPUTDEVICE8A iface
,
42 JoystickGenericImpl
*This
= (JoystickGenericImpl
*)iface
;
45 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(rguid
),ph
);
48 WARN("invalid parameter: ph == NULL\n");
49 return DIERR_INVALIDPARAM
;
53 _dump_DIPROPHEADER(ph
);
56 switch (LOWORD(rguid
)) {
57 case (DWORD_PTR
)DIPROP_RANGE
: {
58 LPCDIPROPRANGE pr
= (LPCDIPROPRANGE
)ph
;
59 if (ph
->dwHow
== DIPH_DEVICE
) {
60 TRACE("proprange(%d,%d) all\n", pr
->lMin
, pr
->lMax
);
61 for (i
= 0; i
< This
->base
.data_format
.wine_df
->dwNumObjs
; i
++) {
62 This
->props
[i
].lMin
= pr
->lMin
;
63 This
->props
[i
].lMax
= pr
->lMax
;
66 int obj
= find_property(&This
->base
.data_format
, ph
);
68 TRACE("proprange(%d,%d) obj=%d\n", pr
->lMin
, pr
->lMax
, obj
);
70 This
->props
[obj
].lMin
= pr
->lMin
;
71 This
->props
[obj
].lMax
= pr
->lMax
;
77 case (DWORD_PTR
)DIPROP_DEADZONE
: {
78 LPCDIPROPDWORD pd
= (LPCDIPROPDWORD
)ph
;
79 if (ph
->dwHow
== DIPH_DEVICE
) {
80 TRACE("deadzone(%d) all\n", pd
->dwData
);
81 for (i
= 0; i
< This
->base
.data_format
.wine_df
->dwNumObjs
; i
++)
82 This
->props
[i
].lDeadZone
= pd
->dwData
;
84 int obj
= find_property(&This
->base
.data_format
, ph
);
86 TRACE("deadzone(%d) obj=%d\n", pd
->dwData
, obj
);
88 This
->props
[obj
].lDeadZone
= pd
->dwData
;
94 case (DWORD_PTR
)DIPROP_SATURATION
: {
95 LPCDIPROPDWORD pd
= (LPCDIPROPDWORD
)ph
;
96 if (ph
->dwHow
== DIPH_DEVICE
) {
97 TRACE("saturation(%d) all\n", pd
->dwData
);
98 for (i
= 0; i
< This
->base
.data_format
.wine_df
->dwNumObjs
; i
++)
99 This
->props
[i
].lSaturation
= pd
->dwData
;
101 int obj
= find_property(&This
->base
.data_format
, ph
);
103 TRACE("saturation(%d) obj=%d\n", pd
->dwData
, obj
);
105 This
->props
[obj
].lSaturation
= pd
->dwData
;
112 return IDirectInputDevice2AImpl_SetProperty(iface
, rguid
, ph
);
119 void _dump_DIDEVCAPS(const DIDEVCAPS
*lpDIDevCaps
)
121 TRACE("dwSize: %d\n", lpDIDevCaps
->dwSize
);
122 TRACE("dwFlags: %08x\n", lpDIDevCaps
->dwFlags
);
123 TRACE("dwDevType: %08x %s\n", lpDIDevCaps
->dwDevType
,
124 lpDIDevCaps
->dwDevType
== DIDEVTYPE_DEVICE
? "DIDEVTYPE_DEVICE" :
125 lpDIDevCaps
->dwDevType
== DIDEVTYPE_DEVICE
? "DIDEVTYPE_DEVICE" :
126 lpDIDevCaps
->dwDevType
== DIDEVTYPE_MOUSE
? "DIDEVTYPE_MOUSE" :
127 lpDIDevCaps
->dwDevType
== DIDEVTYPE_KEYBOARD
? "DIDEVTYPE_KEYBOARD" :
128 lpDIDevCaps
->dwDevType
== DIDEVTYPE_JOYSTICK
? "DIDEVTYPE_JOYSTICK" :
129 lpDIDevCaps
->dwDevType
== DIDEVTYPE_HID
? "DIDEVTYPE_HID" : "UNKNOWN");
130 TRACE("dwAxes: %d\n", lpDIDevCaps
->dwAxes
);
131 TRACE("dwButtons: %d\n", lpDIDevCaps
->dwButtons
);
132 TRACE("dwPOVs: %d\n", lpDIDevCaps
->dwPOVs
);
133 if (lpDIDevCaps
->dwSize
> sizeof(DIDEVCAPS_DX3
)) {
134 TRACE("dwFFSamplePeriod: %d\n", lpDIDevCaps
->dwFFSamplePeriod
);
135 TRACE("dwFFMinTimeResolution: %d\n", lpDIDevCaps
->dwFFMinTimeResolution
);
136 TRACE("dwFirmwareRevision: %d\n", lpDIDevCaps
->dwFirmwareRevision
);
137 TRACE("dwHardwareRevision: %d\n", lpDIDevCaps
->dwHardwareRevision
);
138 TRACE("dwFFDriverVersion: %d\n", lpDIDevCaps
->dwFFDriverVersion
);
142 HRESULT WINAPI
JoystickAGenericImpl_GetCapabilities(
143 LPDIRECTINPUTDEVICE8A iface
,
144 LPDIDEVCAPS lpDIDevCaps
)
146 JoystickGenericImpl
*This
= (JoystickGenericImpl
*)iface
;
149 TRACE("%p->(%p)\n",iface
,lpDIDevCaps
);
151 if (lpDIDevCaps
== NULL
) {
152 WARN("invalid pointer\n");
156 size
= lpDIDevCaps
->dwSize
;
158 if (!(size
== sizeof(DIDEVCAPS
) || size
== sizeof(DIDEVCAPS_DX3
))) {
159 WARN("invalid parameter\n");
160 return DIERR_INVALIDPARAM
;
163 CopyMemory(lpDIDevCaps
, &This
->devcaps
, size
);
164 lpDIDevCaps
->dwSize
= size
;
166 if (TRACE_ON(dinput
))
167 _dump_DIDEVCAPS(lpDIDevCaps
);
172 /******************************************************************************
173 * GetObjectInfo : get object info
175 HRESULT WINAPI
JoystickWGenericImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface
,
176 LPDIDEVICEOBJECTINSTANCEW pdidoi
, DWORD dwObj
, DWORD dwHow
)
178 static const WCHAR axisW
[] = {'A','x','i','s',' ','%','d',0};
179 static const WCHAR povW
[] = {'P','O','V',' ','%','d',0};
180 static const WCHAR buttonW
[] = {'B','u','t','t','o','n',' ','%','d',0};
183 res
= IDirectInputDevice2WImpl_GetObjectInfo(iface
, pdidoi
, dwObj
, dwHow
);
184 if (res
!= DI_OK
) return res
;
186 if (pdidoi
->dwType
& DIDFT_AXIS
)
187 sprintfW(pdidoi
->tszName
, axisW
, DIDFT_GETINSTANCE(pdidoi
->dwType
));
188 else if (pdidoi
->dwType
& DIDFT_POV
)
189 sprintfW(pdidoi
->tszName
, povW
, DIDFT_GETINSTANCE(pdidoi
->dwType
));
190 else if (pdidoi
->dwType
& DIDFT_BUTTON
)
191 sprintfW(pdidoi
->tszName
, buttonW
, DIDFT_GETINSTANCE(pdidoi
->dwType
));
193 _dump_OBJECTINSTANCEW(pdidoi
);
197 HRESULT WINAPI
JoystickAGenericImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8A iface
,
198 LPDIDEVICEOBJECTINSTANCEA pdidoi
, DWORD dwObj
, DWORD dwHow
)
201 DIDEVICEOBJECTINSTANCEW didoiW
;
202 DWORD dwSize
= pdidoi
->dwSize
;
204 didoiW
.dwSize
= sizeof(didoiW
);
205 res
= JoystickWGenericImpl_GetObjectInfo((LPDIRECTINPUTDEVICE8W
)iface
, &didoiW
, dwObj
, dwHow
);
206 if (res
!= DI_OK
) return res
;
208 memset(pdidoi
, 0, pdidoi
->dwSize
);
209 memcpy(pdidoi
, &didoiW
, FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW
, tszName
));
210 pdidoi
->dwSize
= dwSize
;
211 WideCharToMultiByte(CP_ACP
, 0, didoiW
.tszName
, -1, pdidoi
->tszName
,
212 sizeof(pdidoi
->tszName
), NULL
, NULL
);
217 /******************************************************************************
218 * GetProperty : get input device properties
220 HRESULT WINAPI
JoystickAGenericImpl_GetProperty(
221 LPDIRECTINPUTDEVICE8A iface
,
223 LPDIPROPHEADER pdiph
)
225 JoystickGenericImpl
*This
= (JoystickGenericImpl
*)iface
;
227 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(rguid
), pdiph
);
229 if (TRACE_ON(dinput
))
230 _dump_DIPROPHEADER(pdiph
);
232 if (!HIWORD(rguid
)) {
233 switch (LOWORD(rguid
)) {
234 case (DWORD_PTR
) DIPROP_RANGE
: {
235 LPDIPROPRANGE pr
= (LPDIPROPRANGE
)pdiph
;
236 int obj
= find_property(&This
->base
.data_format
, pdiph
);
238 /* The app is querying the current range of the axis
239 * return the lMin and lMax values */
241 pr
->lMin
= This
->props
[obj
].lMin
;
242 pr
->lMax
= This
->props
[obj
].lMax
;
243 TRACE("range(%d, %d) obj=%d\n", pr
->lMin
, pr
->lMax
, obj
);
248 case (DWORD_PTR
) DIPROP_DEADZONE
: {
249 LPDIPROPDWORD pd
= (LPDIPROPDWORD
)pdiph
;
250 int obj
= find_property(&This
->base
.data_format
, pdiph
);
253 pd
->dwData
= This
->props
[obj
].lDeadZone
;
254 TRACE("deadzone(%d) obj=%d\n", pd
->dwData
, obj
);
259 case (DWORD_PTR
) DIPROP_SATURATION
: {
260 LPDIPROPDWORD pd
= (LPDIPROPDWORD
)pdiph
;
261 int obj
= find_property(&This
->base
.data_format
, pdiph
);
264 pd
->dwData
= This
->props
[obj
].lSaturation
;
265 TRACE("saturation(%d) obj=%d\n", pd
->dwData
, obj
);
271 return IDirectInputDevice2AImpl_GetProperty(iface
, rguid
, pdiph
);
278 /******************************************************************************
279 * GetDeviceInfo : get information about a device's identity
281 HRESULT WINAPI
JoystickAGenericImpl_GetDeviceInfo(
282 LPDIRECTINPUTDEVICE8A iface
,
283 LPDIDEVICEINSTANCEA pdidi
)
285 JoystickGenericImpl
*This
= (JoystickGenericImpl
*)iface
;
287 TRACE("(%p,%p)\n", iface
, pdidi
);
290 WARN("invalid pointer\n");
294 if ((pdidi
->dwSize
!= sizeof(DIDEVICEINSTANCE_DX3A
)) &&
295 (pdidi
->dwSize
!= sizeof(DIDEVICEINSTANCEA
))) {
296 WARN("invalid parameter: pdidi->dwSize = %d\n", pdidi
->dwSize
);
297 return DIERR_INVALIDPARAM
;
300 /* Return joystick */
301 pdidi
->guidInstance
= This
->guidInstance
;
302 pdidi
->guidProduct
= This
->guidProduct
;
303 /* we only support traditional joysticks for now */
304 pdidi
->dwDevType
= This
->devcaps
.dwDevType
;
305 strcpy(pdidi
->tszInstanceName
, "Joystick");
306 strcpy(pdidi
->tszProductName
, This
->name
);
307 if (pdidi
->dwSize
> sizeof(DIDEVICEINSTANCE_DX3A
)) {
308 pdidi
->guidFFDriver
= GUID_NULL
;
309 pdidi
->wUsagePage
= 0;
316 /******************************************************************************
317 * GetDeviceInfo : get information about a device's identity
319 HRESULT WINAPI
JoystickWGenericImpl_GetDeviceInfo(
320 LPDIRECTINPUTDEVICE8W iface
,
321 LPDIDEVICEINSTANCEW pdidi
)
323 JoystickGenericImpl
*This
= (JoystickGenericImpl
*)iface
;
325 TRACE("(%p,%p)\n", iface
, pdidi
);
327 if ((pdidi
->dwSize
!= sizeof(DIDEVICEINSTANCE_DX3W
)) &&
328 (pdidi
->dwSize
!= sizeof(DIDEVICEINSTANCEW
))) {
329 WARN("invalid parameter: pdidi->dwSize = %d\n", pdidi
->dwSize
);
330 return DIERR_INVALIDPARAM
;
333 /* Return joystick */
334 pdidi
->guidInstance
= This
->guidInstance
;
335 pdidi
->guidProduct
= This
->guidProduct
;
336 /* we only support traditional joysticks for now */
337 pdidi
->dwDevType
= This
->devcaps
.dwDevType
;
338 MultiByteToWideChar(CP_ACP
, 0, "Joystick", -1, pdidi
->tszInstanceName
, MAX_PATH
);
339 MultiByteToWideChar(CP_ACP
, 0, This
->name
, -1, pdidi
->tszProductName
, MAX_PATH
);
340 if (pdidi
->dwSize
> sizeof(DIDEVICEINSTANCE_DX3W
)) {
341 pdidi
->guidFFDriver
= GUID_NULL
;
342 pdidi
->wUsagePage
= 0;
349 HRESULT WINAPI
JoystickAGenericImpl_Poll(LPDIRECTINPUTDEVICE8A iface
)
351 JoystickGenericImpl
*This
= (JoystickGenericImpl
*)iface
;
353 TRACE("(%p)\n",This
);
355 if (!This
->base
.acquired
) {
356 WARN("not acquired\n");
357 return DIERR_NOTACQUIRED
;
360 This
->joy_polldev(This
);
364 /******************************************************************************
365 * GetDeviceState : returns the "state" of the joystick.
368 HRESULT WINAPI
JoystickAGenericImpl_GetDeviceState(
369 LPDIRECTINPUTDEVICE8A iface
,
373 JoystickGenericImpl
*This
= (JoystickGenericImpl
*)iface
;
375 TRACE("(%p,0x%08x,%p)\n", This
, len
, ptr
);
377 if (!This
->base
.acquired
) {
378 WARN("not acquired\n");
379 return DIERR_NOTACQUIRED
;
382 /* update joystick state */
383 This
->joy_polldev(This
);
385 /* convert and copy data to user supplied buffer */
386 fill_DataFormat(ptr
, len
, &This
->js
, &This
->base
.data_format
);
392 * This maps the read value (from the input event) to a value in the
395 * Dead zone is in % multiplied by a 100 (range 0..10000)
397 LONG
joystick_map_axis(ObjProps
*props
, int val
)
400 LONG dead_zone
= MulDiv( props
->lDeadZone
, props
->lDevMax
- props
->lDevMin
, 10000 );
401 LONG dev_range
= props
->lDevMax
- props
->lDevMin
- dead_zone
;
404 val
-= (props
->lDevMin
+ props
->lDevMax
) / 2;
406 /* Remove dead zone */
407 if (abs( val
) <= dead_zone
/ 2)
410 val
= val
< 0 ? val
+ dead_zone
/ 2 : val
- dead_zone
/ 2;
412 /* Scale and map the value from the device range into the required range */
413 ret
= MulDiv( val
, props
->lMax
- props
->lMin
, dev_range
) +
414 (props
->lMin
+ props
->lMax
) / 2;
416 /* Clamp in case or rounding errors */
417 if (ret
> props
->lMax
) ret
= props
->lMax
;
418 else if (ret
< props
->lMin
) ret
= props
->lMin
;
420 TRACE( "(%d <%d> %d) -> (%d <%d> %d): val=%d ret=%d\n",
421 props
->lDevMin
, dead_zone
, props
->lDevMax
,
422 props
->lMin
, props
->lDeadZone
, props
->lMax
,
429 * Maps POV x & y event values to a DX "clock" position:
436 DWORD
joystick_map_pov(POINTL
*p
)
439 return p
->y
< 0 ? 4500 : !p
->y
? 9000 : 13500;
441 return p
->y
< 0 ? 31500 : !p
->y
? 27000 : 22500;
443 return p
->y
< 0 ? 0 : !p
->y
? -1 : 18000;