include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / dinput / device.c
blob9a9b559902a420397bba7fbe4ed60cbc7d3cb81b
1 /* DirectInput Device
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
23 #include <string.h>
24 #include <math.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winuser.h"
30 #include "winerror.h"
31 #include "dinput.h"
32 #include "dinputd.h"
33 #include "hidusage.h"
35 #include "initguid.h"
36 #include "device_private.h"
37 #include "dinput_private.h"
39 #include "wine/debug.h"
41 #define WM_WINE_NOTIFY_ACTIVITY WM_USER
43 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
45 /* Windows uses this GUID for guidProduct on non-keyboard/mouse devices.
46 * Data1 contains the device VID (low word) and PID (high word).
47 * Data4 ends with the ASCII bytes "PIDVID".
49 DEFINE_GUID( dinput_pidvid_guid, 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 'P', 'I', 'D', 'V', 'I', 'D' );
51 static inline struct dinput_device *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface )
53 return CONTAINING_RECORD( iface, struct dinput_device, IDirectInputDevice8W_iface );
56 static inline IDirectInputDevice8A *IDirectInputDevice8A_from_impl( struct dinput_device *This )
58 return &This->IDirectInputDevice8A_iface;
60 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl( struct dinput_device *This )
62 return &This->IDirectInputDevice8W_iface;
65 static inline const char *debugstr_didataformat( const DIDATAFORMAT *data )
67 if (!data) return "(null)";
68 return wine_dbg_sprintf( "%p dwSize %lu, dwObjsize %lu, dwFlags %#lx, dwDataSize %lu, dwNumObjs %lu, rgodf %p",
69 data, data->dwSize, data->dwObjSize, data->dwFlags, data->dwDataSize, data->dwNumObjs, data->rgodf );
72 static inline const char *debugstr_diobjectdataformat( const DIOBJECTDATAFORMAT *data )
74 if (!data) return "(null)";
75 return wine_dbg_sprintf( "%p pguid %s, dwOfs %#lx, dwType %#lx, dwFlags %#lx", data,
76 debugstr_guid( data->pguid ), data->dwOfs, data->dwType, data->dwFlags );
79 static inline BOOL is_exclusively_acquired( struct dinput_device *device )
81 return device->status == STATUS_ACQUIRED && (device->dwCoopLevel & DISCL_EXCLUSIVE);
84 static void _dump_cooperativelevel_DI(DWORD dwFlags) {
85 if (TRACE_ON(dinput)) {
86 unsigned int i;
87 static const struct {
88 DWORD mask;
89 const char *name;
90 } flags[] = {
91 #define FE(x) { x, #x}
92 FE(DISCL_BACKGROUND),
93 FE(DISCL_EXCLUSIVE),
94 FE(DISCL_FOREGROUND),
95 FE(DISCL_NONEXCLUSIVE),
96 FE(DISCL_NOWINKEY)
97 #undef FE
99 TRACE(" cooperative level : ");
100 for (i = 0; i < ARRAY_SIZE(flags); i++)
101 if (flags[i].mask & dwFlags)
102 TRACE("%s ",flags[i].name);
103 TRACE("\n");
107 BOOL get_app_key(HKEY *defkey, HKEY *appkey)
109 char buffer[MAX_PATH+16];
110 DWORD len;
112 *appkey = 0;
114 /* @@ Wine registry key: HKCU\Software\Wine\DirectInput */
115 if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\DirectInput", defkey))
116 *defkey = 0;
118 len = GetModuleFileNameA(0, buffer, MAX_PATH);
119 if (len && len < MAX_PATH)
121 HKEY tmpkey;
123 /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DirectInput */
124 if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey))
126 char *p, *appname = buffer;
127 if ((p = strrchr(appname, '/'))) appname = p + 1;
128 if ((p = strrchr(appname, '\\'))) appname = p + 1;
129 strcat(appname, "\\DirectInput");
131 if (RegOpenKeyA(tmpkey, appname, appkey)) *appkey = 0;
132 RegCloseKey(tmpkey);
136 return *defkey || *appkey;
139 DWORD get_config_key( HKEY defkey, HKEY appkey, const WCHAR *name, WCHAR *buffer, DWORD size )
141 if (appkey && !RegQueryValueExW( appkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
143 if (defkey && !RegQueryValueExW( defkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
145 return ERROR_FILE_NOT_FOUND;
148 BOOL device_instance_is_disabled( DIDEVICEINSTANCEW *instance, BOOL *override )
150 static const WCHAR disabled_str[] = {'d', 'i', 's', 'a', 'b', 'l', 'e', 'd', 0};
151 static const WCHAR override_str[] = {'o', 'v', 'e', 'r', 'r', 'i', 'd', 'e', 0};
152 static const WCHAR joystick_key[] = {'J', 'o', 'y', 's', 't', 'i', 'c', 'k', 's', 0};
153 WCHAR buffer[MAX_PATH];
154 HKEY hkey, appkey, temp;
155 BOOL disable = FALSE;
157 get_app_key( &hkey, &appkey );
158 if (override) *override = FALSE;
160 /* Joystick settings are in the 'joysticks' subkey */
161 if (appkey)
163 if (RegOpenKeyW( appkey, joystick_key, &temp )) temp = 0;
164 RegCloseKey( appkey );
165 appkey = temp;
168 if (hkey)
170 if (RegOpenKeyW( hkey, joystick_key, &temp )) temp = 0;
171 RegCloseKey( hkey );
172 hkey = temp;
175 /* Look for the "controllername"="disabled" key */
176 if (!get_config_key( hkey, appkey, instance->tszInstanceName, buffer, sizeof(buffer) ))
178 if (!wcscmp( disabled_str, buffer ))
180 TRACE( "Disabling joystick '%s' based on registry key.\n", debugstr_w(instance->tszInstanceName) );
181 disable = TRUE;
183 else if (override && !wcscmp( override_str, buffer ))
185 TRACE( "Force enabling joystick '%s' based on registry key.\n", debugstr_w(instance->tszInstanceName) );
186 *override = TRUE;
190 if (appkey) RegCloseKey( appkey );
191 if (hkey) RegCloseKey( hkey );
193 return disable;
196 static void dinput_device_release_user_format( struct dinput_device *impl )
198 free( impl->user_format.rgodf );
199 impl->user_format.rgodf = NULL;
202 static inline LPDIOBJECTDATAFORMAT dataformat_to_odf(LPCDIDATAFORMAT df, int idx)
204 if (idx < 0 || idx >= df->dwNumObjs) return NULL;
205 return (LPDIOBJECTDATAFORMAT)((LPBYTE)df->rgodf + idx * df->dwObjSize);
208 /* dataformat_to_odf_by_type
209 * Find the Nth object of the selected type in the DataFormat
211 LPDIOBJECTDATAFORMAT dataformat_to_odf_by_type(LPCDIDATAFORMAT df, int n, DWORD type)
213 int i, nfound = 0;
215 for (i=0; i < df->dwNumObjs; i++)
217 LPDIOBJECTDATAFORMAT odf = dataformat_to_odf(df, i);
219 if (odf->dwType & type)
221 if (n == nfound)
222 return odf;
224 nfound++;
228 return NULL;
231 static BOOL match_device_object( const DIDATAFORMAT *device_format, DIDATAFORMAT *user_format,
232 const DIOBJECTDATAFORMAT *match_obj, DWORD version )
234 DWORD i, device_instance, instance = DIDFT_GETINSTANCE( match_obj->dwType );
235 DIOBJECTDATAFORMAT *device_obj, *user_obj;
237 if (version < 0x0700 && instance == 0xff) instance = 0xffff;
239 for (i = 0; i < device_format->dwNumObjs; i++)
241 user_obj = user_format->rgodf + i;
242 device_obj = device_format->rgodf + i;
243 device_instance = DIDFT_GETINSTANCE( device_obj->dwType );
245 if (!(user_obj->dwType & DIDFT_OPTIONAL)) continue; /* already matched */
246 if (match_obj->pguid && device_obj->pguid && !IsEqualGUID( device_obj->pguid, match_obj->pguid )) continue;
247 if (instance != DIDFT_GETINSTANCE( DIDFT_ANYINSTANCE ) && instance != device_instance) continue;
248 if (!(DIDFT_GETTYPE( match_obj->dwType ) & DIDFT_GETTYPE( device_obj->dwType ))) continue;
250 TRACE( "match %s with device %s\n", debugstr_diobjectdataformat( match_obj ),
251 debugstr_diobjectdataformat( device_obj ) );
253 *user_obj = *device_obj;
254 user_obj->dwOfs = match_obj->dwOfs;
255 return TRUE;
258 return FALSE;
261 static HRESULT dinput_device_init_user_format( struct dinput_device *impl, const DIDATAFORMAT *format )
263 DIDATAFORMAT *user_format = &impl->user_format, *device_format = &impl->device_format;
264 DIOBJECTDATAFORMAT *user_obj, *match_obj;
265 DWORD i;
267 *user_format = *device_format;
268 user_format->dwFlags = format->dwFlags;
269 user_format->dwDataSize = format->dwDataSize;
270 user_format->dwNumObjs += format->dwNumObjs;
271 if (!(user_format->rgodf = calloc( user_format->dwNumObjs, sizeof(DIOBJECTDATAFORMAT) ))) return DIERR_OUTOFMEMORY;
273 user_obj = user_format->rgodf + user_format->dwNumObjs;
274 while (user_obj-- > user_format->rgodf) user_obj->dwType |= DIDFT_OPTIONAL;
276 for (i = 0; i < device_format->dwNumObjs; i++)
277 impl->object_properties[i].app_data = -1;
279 for (i = 0; i < format->dwNumObjs; ++i)
281 match_obj = format->rgodf + i;
283 if (!match_device_object( device_format, user_format, match_obj, impl->dinput->dwVersion ))
285 WARN( "object %s not found\n", debugstr_diobjectdataformat( match_obj ) );
286 if (!(match_obj->dwType & DIDFT_OPTIONAL)) goto failed;
287 user_obj = user_format->rgodf + device_format->dwNumObjs + i;
288 *user_obj = *match_obj;
292 user_obj = user_format->rgodf + user_format->dwNumObjs;
293 while (user_obj-- > user_format->rgodf) user_obj->dwType &= ~DIDFT_OPTIONAL;
295 return DI_OK;
297 failed:
298 free( user_format->rgodf );
299 user_format->rgodf = NULL;
300 return DIERR_INVALIDPARAM;
303 int dinput_device_object_index_from_id( IDirectInputDevice8W *iface, DWORD id )
305 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
306 const DIDATAFORMAT *format = &impl->device_format;
307 const DIOBJECTDATAFORMAT *object;
309 if (!format->rgodf) return -1;
311 object = format->rgodf + impl->device_format.dwNumObjs;
312 while (object-- > format->rgodf)
314 if (!object->dwType) continue;
315 if ((object->dwType & 0x00ffffff) == (id & 0x00ffffff)) return object - format->rgodf;
318 return -1;
322 * get_mapping_key
323 * Retrieves an open registry key to save the mapping, parametrized for an username,
324 * specific device and specific action mapping guid.
326 static HKEY get_mapping_key( const WCHAR *device, const WCHAR *username, const WCHAR *guid, BOOL delete )
328 static const WCHAR format[] = L"Software\\Wine\\DirectInput\\Mappings\\%s\\%s\\%s";
329 SIZE_T len = wcslen( format ) + wcslen( username ) + wcslen( device ) + wcslen( guid ) + 1;
330 WCHAR *keyname;
331 HKEY hkey;
333 if (!(keyname = malloc( sizeof(WCHAR) * len ))) return 0;
335 /* The key used is HKCU\Software\Wine\DirectInput\Mappings\[username]\[device]\[mapping_guid] */
336 swprintf( keyname, len, format, username, device, guid );
338 if (delete) RegDeleteTreeW( HKEY_CURRENT_USER, keyname );
339 if (RegCreateKeyW( HKEY_CURRENT_USER, keyname, &hkey )) hkey = 0;
341 free( keyname );
343 return hkey;
346 static HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername)
348 WCHAR *guid_str = NULL;
349 DIDEVICEINSTANCEW didev;
350 HKEY hkey;
351 int i;
353 didev.dwSize = sizeof(didev);
354 IDirectInputDevice8_GetDeviceInfo(iface, &didev);
356 if (StringFromCLSID(&lpdiaf->guidActionMap, &guid_str) != S_OK)
357 return DI_SETTINGSNOTSAVED;
359 if (!(hkey = get_mapping_key( didev.tszInstanceName, lpszUsername, guid_str, TRUE )))
361 CoTaskMemFree(guid_str);
362 return DI_SETTINGSNOTSAVED;
365 /* Write each of the actions mapped for this device.
366 Format is "dwSemantic"="dwObjID" and key is of type REG_DWORD
368 for (i = 0; i < lpdiaf->dwNumActions; i++)
370 WCHAR label[9];
372 if (IsEqualGUID(&didev.guidInstance, &lpdiaf->rgoAction[i].guidInstance) &&
373 lpdiaf->rgoAction[i].dwHow != DIAH_UNMAPPED)
375 swprintf( label, 9, L"%x", lpdiaf->rgoAction[i].dwSemantic );
376 RegSetValueExW( hkey, label, 0, REG_DWORD, (const BYTE *)&lpdiaf->rgoAction[i].dwObjID,
377 sizeof(DWORD) );
381 RegCloseKey(hkey);
382 CoTaskMemFree(guid_str);
384 return DI_OK;
387 static BOOL load_mapping_settings( struct dinput_device *This, LPDIACTIONFORMATW lpdiaf, const WCHAR *username )
389 HKEY hkey;
390 WCHAR *guid_str;
391 DIDEVICEINSTANCEW didev;
392 int i, mapped = 0;
394 didev.dwSize = sizeof(didev);
395 IDirectInputDevice8_GetDeviceInfo(&This->IDirectInputDevice8W_iface, &didev);
397 if (StringFromCLSID(&lpdiaf->guidActionMap, &guid_str) != S_OK)
398 return FALSE;
400 if (!(hkey = get_mapping_key( didev.tszInstanceName, username, guid_str, FALSE )))
402 CoTaskMemFree(guid_str);
403 return FALSE;
406 /* Try to read each action in the DIACTIONFORMAT from registry */
407 for (i = 0; i < lpdiaf->dwNumActions; i++)
409 DWORD id, size = sizeof(DWORD);
410 WCHAR label[9];
412 swprintf( label, 9, L"%x", lpdiaf->rgoAction[i].dwSemantic );
414 if (!RegQueryValueExW(hkey, label, 0, NULL, (LPBYTE) &id, &size))
416 lpdiaf->rgoAction[i].dwObjID = id;
417 lpdiaf->rgoAction[i].guidInstance = didev.guidInstance;
418 lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT;
419 mapped += 1;
423 RegCloseKey(hkey);
424 CoTaskMemFree(guid_str);
426 return mapped > 0;
429 void queue_event( IDirectInputDevice8W *iface, int index, DWORD data, DWORD time, DWORD seq )
431 static ULONGLONG notify_ms = 0;
432 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
433 struct object_properties *properties = This->object_properties + index;
434 const DIOBJECTDATAFORMAT *user_obj = This->user_format.rgodf + index;
435 ULONGLONG time_ms = GetTickCount64();
436 int next_pos;
438 if (time_ms - notify_ms > 1000)
440 PostMessageW(GetDesktopWindow(), WM_WINE_NOTIFY_ACTIVITY, 0, 0);
441 notify_ms = time_ms;
444 if (!This->queue_len || This->overflow || !user_obj->dwType) return;
446 next_pos = (This->queue_head + 1) % This->queue_len;
447 if (next_pos == This->queue_tail)
449 TRACE(" queue overflowed\n");
450 This->overflow = TRUE;
451 return;
454 TRACE( " queueing %lu at offset %lu (queue head %u / size %u)\n", data, user_obj->dwOfs, This->queue_head, This->queue_len );
456 This->data_queue[This->queue_head].dwOfs = user_obj->dwOfs;
457 This->data_queue[This->queue_head].dwData = data;
458 This->data_queue[This->queue_head].dwTimeStamp = time;
459 This->data_queue[This->queue_head].dwSequence = seq;
460 This->data_queue[This->queue_head].uAppData = properties->app_data;
462 This->queue_head = next_pos;
463 /* Send event if asked */
466 static HRESULT WINAPI dinput_device_Acquire( IDirectInputDevice8W *iface )
468 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
469 HRESULT hr = DI_OK;
470 DWORD pid;
472 TRACE( "iface %p.\n", iface );
474 EnterCriticalSection( &impl->crit );
475 if (impl->status == STATUS_ACQUIRED)
476 hr = DI_NOEFFECT;
477 else if (!impl->user_format.rgodf)
478 hr = DIERR_INVALIDPARAM;
479 else if ((impl->dwCoopLevel & DISCL_FOREGROUND) && impl->win != GetForegroundWindow())
480 hr = DIERR_OTHERAPPHASPRIO;
481 else if ((impl->dwCoopLevel & DISCL_FOREGROUND) && (!GetWindowThreadProcessId( impl->win, &pid ) || pid != GetCurrentProcessId()))
482 hr = DIERR_INVALIDPARAM;
483 else
485 impl->status = STATUS_ACQUIRED;
486 if (FAILED(hr = impl->vtbl->acquire( iface ))) impl->status = STATUS_UNACQUIRED;
488 LeaveCriticalSection( &impl->crit );
489 if (hr != DI_OK) return hr;
491 dinput_hooks_acquire_device( iface );
493 return hr;
496 static HRESULT WINAPI dinput_device_Unacquire( IDirectInputDevice8W *iface )
498 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
499 HRESULT hr = DI_OK;
501 TRACE( "iface %p.\n", iface );
503 EnterCriticalSection( &impl->crit );
504 if (impl->status != STATUS_ACQUIRED) hr = DI_NOEFFECT;
505 else hr = impl->vtbl->unacquire( iface );
506 impl->status = STATUS_UNACQUIRED;
507 LeaveCriticalSection( &impl->crit );
508 if (hr != DI_OK) return hr;
510 dinput_hooks_unacquire_device( iface );
512 return hr;
515 static HRESULT WINAPI dinput_device_SetDataFormat( IDirectInputDevice8W *iface, const DIDATAFORMAT *format )
517 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
518 HRESULT res = DI_OK;
519 ULONG i;
521 TRACE( "iface %p, format %p.\n", iface, format );
523 if (!format) return E_POINTER;
524 if (TRACE_ON( dinput ))
526 TRACE( "user format %s\n", debugstr_didataformat( format ) );
527 for (i = 0; i < format->dwNumObjs; ++i) TRACE( " %lu: object %s\n", i, debugstr_diobjectdataformat( format->rgodf + i ) );
530 if (format->dwSize != sizeof(DIDATAFORMAT)) return DIERR_INVALIDPARAM;
531 if (format->dwObjSize != sizeof(DIOBJECTDATAFORMAT)) return DIERR_INVALIDPARAM;
532 if (This->status == STATUS_ACQUIRED) return DIERR_ACQUIRED;
534 EnterCriticalSection(&This->crit);
536 dinput_device_release_user_format( This );
537 res = dinput_device_init_user_format( This, format );
539 LeaveCriticalSection(&This->crit);
540 return res;
543 static HRESULT WINAPI dinput_device_SetCooperativeLevel( IDirectInputDevice8W *iface, HWND hwnd, DWORD flags )
545 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
546 HRESULT hr;
548 TRACE( "iface %p, hwnd %p, flags %#lx.\n", iface, hwnd, flags );
550 _dump_cooperativelevel_DI( flags );
552 if ((flags & (DISCL_EXCLUSIVE | DISCL_NONEXCLUSIVE)) == 0 ||
553 (flags & (DISCL_EXCLUSIVE | DISCL_NONEXCLUSIVE)) == (DISCL_EXCLUSIVE | DISCL_NONEXCLUSIVE) ||
554 (flags & (DISCL_FOREGROUND | DISCL_BACKGROUND)) == 0 ||
555 (flags & (DISCL_FOREGROUND | DISCL_BACKGROUND)) == (DISCL_FOREGROUND | DISCL_BACKGROUND))
556 return DIERR_INVALIDPARAM;
558 if (hwnd && GetWindowLongW(hwnd, GWL_STYLE) & WS_CHILD) return E_HANDLE;
560 if (!hwnd && flags == (DISCL_NONEXCLUSIVE | DISCL_BACKGROUND)) hwnd = GetDesktopWindow();
562 if (!IsWindow(hwnd)) return E_HANDLE;
564 /* For security reasons native does not allow exclusive background level
565 for mouse and keyboard only */
566 if (flags & DISCL_EXCLUSIVE && flags & DISCL_BACKGROUND &&
567 (IsEqualGUID( &This->guid, &GUID_SysMouse ) || IsEqualGUID( &This->guid, &GUID_SysKeyboard )))
568 return DIERR_UNSUPPORTED;
570 EnterCriticalSection(&This->crit);
571 if (This->status == STATUS_ACQUIRED) hr = DIERR_ACQUIRED;
572 else
574 This->win = hwnd;
575 This->dwCoopLevel = flags;
576 hr = DI_OK;
578 LeaveCriticalSection(&This->crit);
580 return hr;
583 static HRESULT WINAPI dinput_device_GetDeviceInfo( IDirectInputDevice8W *iface, DIDEVICEINSTANCEW *instance )
585 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
586 DWORD size;
588 TRACE( "iface %p, instance %p.\n", iface, instance );
590 if (!instance) return E_POINTER;
591 if (instance->dwSize != sizeof(DIDEVICEINSTANCE_DX3W) &&
592 instance->dwSize != sizeof(DIDEVICEINSTANCEW))
593 return DIERR_INVALIDPARAM;
595 size = instance->dwSize;
596 memcpy( instance, &impl->instance, size );
597 instance->dwSize = size;
599 return S_OK;
602 static HRESULT WINAPI dinput_device_SetEventNotification( IDirectInputDevice8W *iface, HANDLE event )
604 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
606 TRACE( "iface %p, event %p.\n", iface, event );
608 EnterCriticalSection(&This->crit);
609 This->hEvent = event;
610 LeaveCriticalSection(&This->crit);
611 return DI_OK;
614 void dinput_device_internal_addref( struct dinput_device *impl )
616 ULONG ref = InterlockedIncrement( &impl->internal_ref );
617 TRACE( "impl %p, internal ref %lu.\n", impl, ref );
620 void dinput_device_internal_release( struct dinput_device *impl )
622 ULONG ref = InterlockedDecrement( &impl->internal_ref );
623 TRACE( "impl %p, internal ref %lu.\n", impl, ref );
625 if (!ref)
627 if (impl->vtbl->destroy) impl->vtbl->destroy( &impl->IDirectInputDevice8W_iface );
629 free( impl->object_properties );
630 free( impl->data_queue );
632 free( impl->device_format.rgodf );
633 dinput_device_release_user_format( impl );
635 dinput_internal_release( impl->dinput );
636 impl->crit.DebugInfo->Spare[0] = 0;
637 DeleteCriticalSection( &impl->crit );
639 free( impl );
643 static ULONG WINAPI dinput_device_Release( IDirectInputDevice8W *iface )
645 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
646 ULONG ref = InterlockedDecrement( &impl->ref );
648 TRACE( "iface %p, ref %lu.\n", iface, ref );
650 if (!ref)
652 IDirectInputDevice_Unacquire( iface );
653 input_thread_remove_user();
654 dinput_device_internal_release( impl );
657 return ref;
660 static HRESULT WINAPI dinput_device_GetCapabilities( IDirectInputDevice8W *iface, DIDEVCAPS *caps )
662 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
663 DWORD size;
665 TRACE( "iface %p, caps %p.\n", iface, caps );
667 if (!caps) return E_POINTER;
668 if (caps->dwSize != sizeof(DIDEVCAPS_DX3) &&
669 caps->dwSize != sizeof(DIDEVCAPS))
670 return DIERR_INVALIDPARAM;
672 size = caps->dwSize;
673 memcpy( caps, &impl->caps, size );
674 caps->dwSize = size;
676 return DI_OK;
679 static HRESULT WINAPI dinput_device_QueryInterface( IDirectInputDevice8W *iface, const GUID *iid, void **out )
681 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
683 TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out );
685 if (IsEqualGUID( &IID_IDirectInputDeviceA, iid ) ||
686 IsEqualGUID( &IID_IDirectInputDevice2A, iid ) ||
687 IsEqualGUID( &IID_IDirectInputDevice7A, iid ) ||
688 IsEqualGUID( &IID_IDirectInputDevice8A, iid ))
690 IDirectInputDevice2_AddRef(iface);
691 *out = IDirectInputDevice8A_from_impl( This );
692 return DI_OK;
695 if (IsEqualGUID( &IID_IUnknown, iid ) ||
696 IsEqualGUID( &IID_IDirectInputDeviceW, iid ) ||
697 IsEqualGUID( &IID_IDirectInputDevice2W, iid ) ||
698 IsEqualGUID( &IID_IDirectInputDevice7W, iid ) ||
699 IsEqualGUID( &IID_IDirectInputDevice8W, iid ))
701 IDirectInputDevice2_AddRef(iface);
702 *out = IDirectInputDevice8W_from_impl( This );
703 return DI_OK;
706 WARN( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
707 return E_NOINTERFACE;
710 static ULONG WINAPI dinput_device_AddRef( IDirectInputDevice8W *iface )
712 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
713 ULONG ref = InterlockedIncrement( &impl->ref );
714 TRACE( "iface %p, ref %lu.\n", iface, ref );
715 return ref;
718 struct enum_objects_params
720 LPDIENUMDEVICEOBJECTSCALLBACKW callback;
721 void *context;
724 static BOOL enum_objects_callback( struct dinput_device *impl, UINT index, struct hid_value_caps *caps,
725 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
727 struct enum_objects_params *params = data;
728 if (instance->wUsagePage == HID_USAGE_PAGE_PID && !(instance->dwType & DIDFT_NODATA))
729 return DIENUM_CONTINUE;
731 /* Applications may return non-zero values instead of DIENUM_CONTINUE. */
732 return params->callback( instance, params->context ) ? DIENUM_CONTINUE : DIENUM_STOP;
735 static HRESULT WINAPI dinput_device_EnumObjects( IDirectInputDevice8W *iface, LPDIENUMDEVICEOBJECTSCALLBACKW callback,
736 void *context, DWORD flags )
738 static const DIPROPHEADER filter =
740 .dwSize = sizeof(filter),
741 .dwHeaderSize = sizeof(filter),
742 .dwHow = DIPH_DEVICE,
744 struct enum_objects_params params = {.callback = callback, .context = context};
745 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
746 HRESULT hr;
748 TRACE( "iface %p, callback %p, context %p, flags %#lx.\n", iface, callback, context, flags );
750 if (!callback) return DIERR_INVALIDPARAM;
751 if (flags & ~(DIDFT_AXIS | DIDFT_POV | DIDFT_BUTTON | DIDFT_NODATA | DIDFT_COLLECTION))
752 return DIERR_INVALIDPARAM;
754 if (flags == DIDFT_ALL || (flags & DIDFT_AXIS))
756 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_AXIS, enum_objects_callback, &params );
757 if (FAILED(hr)) return hr;
758 if (hr != DIENUM_CONTINUE) return DI_OK;
760 if (flags == DIDFT_ALL || (flags & DIDFT_POV))
762 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_POV, enum_objects_callback, &params );
763 if (FAILED(hr)) return hr;
764 if (hr != DIENUM_CONTINUE) return DI_OK;
766 if (flags == DIDFT_ALL || (flags & DIDFT_BUTTON))
768 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_BUTTON, enum_objects_callback, &params );
769 if (FAILED(hr)) return hr;
770 if (hr != DIENUM_CONTINUE) return DI_OK;
772 if (flags == DIDFT_ALL || (flags & (DIDFT_NODATA | DIDFT_COLLECTION)))
774 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_NODATA, enum_objects_callback, &params );
775 if (FAILED(hr)) return hr;
776 if (hr != DIENUM_CONTINUE) return DI_OK;
779 return DI_OK;
782 static HRESULT enum_object_filter_init( struct dinput_device *impl, DIPROPHEADER *filter )
784 DIOBJECTDATAFORMAT *user_objs = impl->user_format.rgodf;
785 DWORD i, count = impl->device_format.dwNumObjs;
787 if (filter->dwHow > DIPH_BYUSAGE) return DIERR_INVALIDPARAM;
788 if (filter->dwHow == DIPH_BYUSAGE && !(impl->instance.dwDevType & DIDEVTYPE_HID)) return DIERR_UNSUPPORTED;
789 if (filter->dwHow != DIPH_BYOFFSET) return DI_OK;
791 if (!user_objs) return DIERR_NOTFOUND;
793 for (i = 0; i < count; i++)
795 if (!user_objs[i].dwType) continue;
796 if (user_objs[i].dwOfs == filter->dwObj) break;
798 if (i == count) return DIERR_NOTFOUND;
800 filter->dwObj = impl->device_format.rgodf[i].dwOfs;
801 return DI_OK;
804 static HRESULT check_property( struct dinput_device *impl, const GUID *guid, const DIPROPHEADER *header, BOOL set )
806 switch (LOWORD( guid ))
808 case (DWORD_PTR)DIPROP_VIDPID:
809 case (DWORD_PTR)DIPROP_TYPENAME:
810 case (DWORD_PTR)DIPROP_USERNAME:
811 case (DWORD_PTR)DIPROP_KEYNAME:
812 case (DWORD_PTR)DIPROP_LOGICALRANGE:
813 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
814 case (DWORD_PTR)DIPROP_APPDATA:
815 if (impl->dinput->dwVersion < 0x0800) return DIERR_UNSUPPORTED;
816 break;
819 switch (LOWORD( guid ))
821 case (DWORD_PTR)DIPROP_INSTANCENAME:
822 case (DWORD_PTR)DIPROP_KEYNAME:
823 case (DWORD_PTR)DIPROP_PRODUCTNAME:
824 case (DWORD_PTR)DIPROP_TYPENAME:
825 case (DWORD_PTR)DIPROP_USERNAME:
826 if (header->dwSize != sizeof(DIPROPSTRING)) return DIERR_INVALIDPARAM;
827 break;
829 case (DWORD_PTR)DIPROP_AUTOCENTER:
830 case (DWORD_PTR)DIPROP_AXISMODE:
831 case (DWORD_PTR)DIPROP_BUFFERSIZE:
832 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
833 case (DWORD_PTR)DIPROP_DEADZONE:
834 case (DWORD_PTR)DIPROP_FFGAIN:
835 case (DWORD_PTR)DIPROP_FFLOAD:
836 case (DWORD_PTR)DIPROP_GRANULARITY:
837 case (DWORD_PTR)DIPROP_JOYSTICKID:
838 case (DWORD_PTR)DIPROP_SATURATION:
839 case (DWORD_PTR)DIPROP_SCANCODE:
840 case (DWORD_PTR)DIPROP_VIDPID:
841 if (header->dwSize != sizeof(DIPROPDWORD)) return DIERR_INVALIDPARAM;
842 break;
844 case (DWORD_PTR)DIPROP_APPDATA:
845 if (header->dwSize != sizeof(DIPROPPOINTER)) return DIERR_INVALIDPARAM;
846 break;
848 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
849 case (DWORD_PTR)DIPROP_LOGICALRANGE:
850 case (DWORD_PTR)DIPROP_RANGE:
851 if (header->dwSize != sizeof(DIPROPRANGE)) return DIERR_INVALIDPARAM;
852 break;
854 case (DWORD_PTR)DIPROP_GUIDANDPATH:
855 if (header->dwSize != sizeof(DIPROPGUIDANDPATH)) return DIERR_INVALIDPARAM;
856 break;
859 switch (LOWORD( guid ))
861 case (DWORD_PTR)DIPROP_PRODUCTNAME:
862 case (DWORD_PTR)DIPROP_INSTANCENAME:
863 case (DWORD_PTR)DIPROP_VIDPID:
864 case (DWORD_PTR)DIPROP_JOYSTICKID:
865 case (DWORD_PTR)DIPROP_GUIDANDPATH:
866 case (DWORD_PTR)DIPROP_BUFFERSIZE:
867 case (DWORD_PTR)DIPROP_FFGAIN:
868 case (DWORD_PTR)DIPROP_TYPENAME:
869 case (DWORD_PTR)DIPROP_USERNAME:
870 case (DWORD_PTR)DIPROP_AUTOCENTER:
871 case (DWORD_PTR)DIPROP_AXISMODE:
872 case (DWORD_PTR)DIPROP_FFLOAD:
873 if (header->dwHow != DIPH_DEVICE) return DIERR_UNSUPPORTED;
874 if (header->dwObj) return DIERR_INVALIDPARAM;
875 break;
877 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
878 case (DWORD_PTR)DIPROP_LOGICALRANGE:
879 case (DWORD_PTR)DIPROP_RANGE:
880 case (DWORD_PTR)DIPROP_DEADZONE:
881 case (DWORD_PTR)DIPROP_SATURATION:
882 case (DWORD_PTR)DIPROP_GRANULARITY:
883 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
884 if (header->dwHow == DIPH_DEVICE && !set) return DIERR_UNSUPPORTED;
885 break;
887 case (DWORD_PTR)DIPROP_KEYNAME:
888 if (header->dwHow == DIPH_DEVICE) return DIERR_INVALIDPARAM;
889 break;
891 case (DWORD_PTR)DIPROP_SCANCODE:
892 case (DWORD_PTR)DIPROP_APPDATA:
893 if (header->dwHow == DIPH_DEVICE) return DIERR_UNSUPPORTED;
894 break;
897 if (set)
899 switch (LOWORD( guid ))
901 case (DWORD_PTR)DIPROP_AUTOCENTER:
902 if (impl->status == STATUS_ACQUIRED) return DIERR_ACQUIRED;
903 break;
904 case (DWORD_PTR)DIPROP_AXISMODE:
905 case (DWORD_PTR)DIPROP_BUFFERSIZE:
906 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
907 case (DWORD_PTR)DIPROP_LOGICALRANGE:
908 if (impl->status == STATUS_ACQUIRED) return DIERR_ACQUIRED;
909 break;
910 case (DWORD_PTR)DIPROP_FFLOAD:
911 case (DWORD_PTR)DIPROP_GRANULARITY:
912 case (DWORD_PTR)DIPROP_VIDPID:
913 case (DWORD_PTR)DIPROP_TYPENAME:
914 case (DWORD_PTR)DIPROP_USERNAME:
915 case (DWORD_PTR)DIPROP_GUIDANDPATH:
916 return DIERR_READONLY;
919 switch (LOWORD( guid ))
921 case (DWORD_PTR)DIPROP_RANGE:
923 const DIPROPRANGE *value = (const DIPROPRANGE *)header;
924 if (value->lMin > value->lMax) return DIERR_INVALIDPARAM;
925 break;
927 case (DWORD_PTR)DIPROP_DEADZONE:
928 case (DWORD_PTR)DIPROP_SATURATION:
929 case (DWORD_PTR)DIPROP_FFGAIN:
931 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
932 if (value->dwData > 10000) return DIERR_INVALIDPARAM;
933 break;
935 case (DWORD_PTR)DIPROP_AUTOCENTER:
936 case (DWORD_PTR)DIPROP_AXISMODE:
937 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
939 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
940 if (value->dwData > 1) return DIERR_INVALIDPARAM;
941 break;
943 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
944 case (DWORD_PTR)DIPROP_LOGICALRANGE:
945 return DIERR_UNSUPPORTED;
948 else
950 switch (LOWORD( guid ))
952 case (DWORD_PTR)DIPROP_RANGE:
953 case (DWORD_PTR)DIPROP_GRANULARITY:
954 if (!impl->caps.dwAxes) return DIERR_UNSUPPORTED;
955 break;
957 case (DWORD_PTR)DIPROP_KEYNAME:
958 /* not supported on the mouse */
959 if (impl->caps.dwAxes && !(impl->caps.dwDevType & DIDEVTYPE_HID)) return DIERR_UNSUPPORTED;
960 break;
962 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
963 case (DWORD_PTR)DIPROP_LOGICALRANGE:
964 case (DWORD_PTR)DIPROP_DEADZONE:
965 case (DWORD_PTR)DIPROP_SATURATION:
966 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
967 /* not supported on the mouse or keyboard */
968 if (!(impl->caps.dwDevType & DIDEVTYPE_HID)) return DIERR_UNSUPPORTED;
969 break;
971 case (DWORD_PTR)DIPROP_FFLOAD:
972 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
973 if (!is_exclusively_acquired( impl )) return DIERR_NOTEXCLUSIVEACQUIRED;
974 /* fallthrough */
975 case (DWORD_PTR)DIPROP_PRODUCTNAME:
976 case (DWORD_PTR)DIPROP_INSTANCENAME:
977 case (DWORD_PTR)DIPROP_VIDPID:
978 case (DWORD_PTR)DIPROP_JOYSTICKID:
979 case (DWORD_PTR)DIPROP_GUIDANDPATH:
980 if (!impl->vtbl->get_property) return DIERR_UNSUPPORTED;
981 break;
985 return DI_OK;
988 struct get_object_property_params
990 IDirectInputDevice8W *iface;
991 DIPROPHEADER *header;
992 DWORD property;
995 static BOOL get_object_property( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
996 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
998 struct get_object_property_params *params = data;
999 struct dinput_device *impl = impl_from_IDirectInputDevice8W( params->iface );
1000 const struct object_properties *properties;
1002 if (index == -1) return DIENUM_STOP;
1003 properties = impl->object_properties + index;
1005 switch (params->property)
1007 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
1009 DIPROPRANGE *value = (DIPROPRANGE *)params->header;
1010 value->lMin = properties->physical_min;
1011 value->lMax = properties->physical_max;
1012 return DI_OK;
1014 case (DWORD_PTR)DIPROP_LOGICALRANGE:
1016 DIPROPRANGE *value = (DIPROPRANGE *)params->header;
1017 value->lMin = properties->logical_min;
1018 value->lMax = properties->logical_max;
1019 return DI_OK;
1021 case (DWORD_PTR)DIPROP_RANGE:
1023 DIPROPRANGE *value = (DIPROPRANGE *)params->header;
1024 value->lMin = properties->range_min;
1025 value->lMax = properties->range_max;
1026 return DIENUM_STOP;
1028 case (DWORD_PTR)DIPROP_DEADZONE:
1030 DIPROPDWORD *value = (DIPROPDWORD *)params->header;
1031 value->dwData = properties->deadzone;
1032 return DIENUM_STOP;
1034 case (DWORD_PTR)DIPROP_SATURATION:
1036 DIPROPDWORD *value = (DIPROPDWORD *)params->header;
1037 value->dwData = properties->saturation;
1038 return DIENUM_STOP;
1040 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
1042 DIPROPDWORD *value = (DIPROPDWORD *)params->header;
1043 value->dwData = properties->calibration_mode;
1044 return DI_OK;
1046 case (DWORD_PTR)DIPROP_GRANULARITY:
1048 DIPROPDWORD *value = (DIPROPDWORD *)params->header;
1049 value->dwData = properties->granularity;
1050 return DIENUM_STOP;
1052 case (DWORD_PTR)DIPROP_KEYNAME:
1054 DIPROPSTRING *value = (DIPROPSTRING *)params->header;
1055 lstrcpynW( value->wsz, instance->tszName, ARRAY_SIZE(value->wsz) );
1056 return DIENUM_STOP;
1058 case (DWORD_PTR)DIPROP_APPDATA:
1060 DIPROPPOINTER *value = (DIPROPPOINTER *)params->header;
1061 value->uData = properties->app_data;
1062 return DIENUM_STOP;
1066 return DIENUM_STOP;
1069 static HRESULT dinput_device_get_property( IDirectInputDevice8W *iface, const GUID *guid, DIPROPHEADER *header )
1071 struct get_object_property_params params = {.iface = iface, .header = header, .property = LOWORD( guid )};
1072 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1073 DWORD object_mask = DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV;
1074 DIPROPHEADER filter;
1075 HRESULT hr;
1077 filter = *header;
1078 if (FAILED(hr = enum_object_filter_init( impl, &filter ))) return hr;
1079 if (FAILED(hr = check_property( impl, guid, header, FALSE ))) return hr;
1081 switch (LOWORD( guid ))
1083 case (DWORD_PTR)DIPROP_PRODUCTNAME:
1084 case (DWORD_PTR)DIPROP_INSTANCENAME:
1085 case (DWORD_PTR)DIPROP_VIDPID:
1086 case (DWORD_PTR)DIPROP_JOYSTICKID:
1087 case (DWORD_PTR)DIPROP_GUIDANDPATH:
1088 case (DWORD_PTR)DIPROP_FFLOAD:
1089 return impl->vtbl->get_property( iface, LOWORD( guid ), header, NULL );
1091 case (DWORD_PTR)DIPROP_RANGE:
1092 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
1093 case (DWORD_PTR)DIPROP_LOGICALRANGE:
1094 case (DWORD_PTR)DIPROP_DEADZONE:
1095 case (DWORD_PTR)DIPROP_SATURATION:
1096 case (DWORD_PTR)DIPROP_GRANULARITY:
1097 case (DWORD_PTR)DIPROP_KEYNAME:
1098 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
1099 case (DWORD_PTR)DIPROP_APPDATA:
1100 hr = impl->vtbl->enum_objects( iface, &filter, object_mask, get_object_property, &params );
1101 if (FAILED(hr)) return hr;
1102 if (hr == DIENUM_CONTINUE) return DIERR_NOTFOUND;
1103 return DI_OK;
1105 case (DWORD_PTR)DIPROP_AUTOCENTER:
1107 DIPROPDWORD *value = (DIPROPDWORD *)header;
1108 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1109 value->dwData = impl->autocenter;
1110 return DI_OK;
1112 case (DWORD_PTR)DIPROP_BUFFERSIZE:
1114 DIPROPDWORD *value = (DIPROPDWORD *)header;
1115 value->dwData = impl->buffersize;
1116 return DI_OK;
1118 case (DWORD_PTR)DIPROP_USERNAME:
1120 DIPROPSTRING *value = (DIPROPSTRING *)header;
1121 struct DevicePlayer *device_player;
1122 LIST_FOR_EACH_ENTRY( device_player, &impl->dinput->device_players, struct DevicePlayer, entry )
1124 if (IsEqualGUID( &device_player->instance_guid, &impl->guid ))
1126 if (!*device_player->username) break;
1127 lstrcpynW( value->wsz, device_player->username, ARRAY_SIZE(value->wsz) );
1128 return DI_OK;
1131 return S_FALSE;
1133 case (DWORD_PTR)DIPROP_FFGAIN:
1135 DIPROPDWORD *value = (DIPROPDWORD *)header;
1136 value->dwData = impl->device_gain;
1137 return DI_OK;
1139 case (DWORD_PTR)DIPROP_CALIBRATION:
1140 return DIERR_INVALIDPARAM;
1141 default:
1142 FIXME( "Unknown property %s\n", debugstr_guid( guid ) );
1143 return DIERR_UNSUPPORTED;
1146 return DI_OK;
1149 static HRESULT WINAPI dinput_device_GetProperty( IDirectInputDevice8W *iface, const GUID *guid, DIPROPHEADER *header )
1151 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1152 HRESULT hr;
1154 TRACE( "iface %p, guid %s, header %p\n", iface, debugstr_guid( guid ), header );
1156 if (!header) return DIERR_INVALIDPARAM;
1157 if (header->dwHeaderSize != sizeof(DIPROPHEADER)) return DIERR_INVALIDPARAM;
1158 if (!IS_DIPROP( guid )) return DI_OK;
1160 EnterCriticalSection( &impl->crit );
1161 hr = dinput_device_get_property( iface, guid, header );
1162 LeaveCriticalSection( &impl->crit );
1164 return hr;
1167 struct set_object_property_params
1169 IDirectInputDevice8W *iface;
1170 const DIPROPHEADER *header;
1171 DWORD property;
1174 static BOOL set_object_property( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1175 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1177 struct set_object_property_params *params = data;
1178 struct dinput_device *impl = impl_from_IDirectInputDevice8W( params->iface );
1179 struct object_properties *properties;
1181 if (index == -1) return DIENUM_STOP;
1182 properties = impl->object_properties + index;
1184 switch (params->property)
1186 case (DWORD_PTR)DIPROP_RANGE:
1188 const DIPROPRANGE *value = (const DIPROPRANGE *)params->header;
1189 properties->range_min = value->lMin;
1190 properties->range_max = value->lMax;
1191 return DIENUM_CONTINUE;
1193 case (DWORD_PTR)DIPROP_DEADZONE:
1195 const DIPROPDWORD *value = (const DIPROPDWORD *)params->header;
1196 properties->deadzone = value->dwData;
1197 return DIENUM_CONTINUE;
1199 case (DWORD_PTR)DIPROP_SATURATION:
1201 const DIPROPDWORD *value = (const DIPROPDWORD *)params->header;
1202 properties->saturation = value->dwData;
1203 return DIENUM_CONTINUE;
1205 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
1207 const DIPROPDWORD *value = (const DIPROPDWORD *)params->header;
1208 properties->calibration_mode = value->dwData;
1209 return DIENUM_CONTINUE;
1211 case (DWORD_PTR)DIPROP_APPDATA:
1213 DIPROPPOINTER *value = (DIPROPPOINTER *)params->header;
1214 properties->app_data = value->uData;
1215 return DIENUM_CONTINUE;
1219 return DIENUM_STOP;
1222 static BOOL reset_object_value( struct dinput_device *impl, UINT index, struct hid_value_caps *caps,
1223 const DIDEVICEOBJECTINSTANCEW *instance, void *context )
1225 struct object_properties *properties;
1226 LONG tmp = -1;
1228 if (index == -1) return DIENUM_STOP;
1229 properties = impl->object_properties + index;
1231 if (instance->dwType & DIDFT_AXIS)
1233 LONG range_min = 0, range_max = 0xfffe;
1234 if (properties->range_min != DIPROPRANGE_NOMIN) range_min = properties->range_min;
1235 if (properties->range_max != DIPROPRANGE_NOMAX) range_max = properties->range_max;
1236 tmp = round( (range_min + range_max) / 2.0 );
1239 *(LONG *)(impl->device_state + instance->dwOfs) = tmp;
1240 return DIENUM_CONTINUE;
1243 static void reset_device_state( IDirectInputDevice8W *iface )
1245 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1246 DIPROPHEADER filter =
1248 .dwHeaderSize = sizeof(DIPROPHEADER),
1249 .dwSize = sizeof(DIPROPHEADER),
1250 .dwHow = DIPH_DEVICE,
1251 .dwObj = 0,
1254 impl->vtbl->enum_objects( iface, &filter, DIDFT_AXIS | DIDFT_POV, reset_object_value, impl );
1257 static HRESULT dinput_device_set_property( IDirectInputDevice8W *iface, const GUID *guid,
1258 const DIPROPHEADER *header )
1260 struct set_object_property_params params = {.iface = iface, .header = header, .property = LOWORD( guid )};
1261 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1262 DIPROPHEADER filter;
1263 HRESULT hr;
1265 filter = *header;
1266 if (FAILED(hr = enum_object_filter_init( impl, &filter ))) return hr;
1267 if (FAILED(hr = check_property( impl, guid, header, TRUE ))) return hr;
1269 switch (LOWORD( guid ))
1271 case (DWORD_PTR)DIPROP_RANGE:
1272 case (DWORD_PTR)DIPROP_DEADZONE:
1273 case (DWORD_PTR)DIPROP_SATURATION:
1275 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_AXIS, set_object_property, &params );
1276 if (FAILED(hr)) return hr;
1277 reset_device_state( iface );
1278 return DI_OK;
1280 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
1282 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1283 if (value->dwData > DIPROPCALIBRATIONMODE_RAW) return DIERR_INVALIDPARAM;
1284 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_AXIS, set_object_property, &params );
1285 if (FAILED(hr)) return hr;
1286 reset_device_state( iface );
1287 return DI_OK;
1289 case (DWORD_PTR)DIPROP_AUTOCENTER:
1291 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1292 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1293 impl->autocenter = value->dwData;
1294 return DI_OK;
1296 case (DWORD_PTR)DIPROP_FFGAIN:
1298 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1299 impl->device_gain = value->dwData;
1300 if (!is_exclusively_acquired( impl )) return DI_OK;
1301 if (!impl->vtbl->send_device_gain) return DI_OK;
1302 return impl->vtbl->send_device_gain( iface, impl->device_gain );
1304 case (DWORD_PTR)DIPROP_AXISMODE:
1306 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1308 TRACE( "Axis mode: %s\n", value->dwData == DIPROPAXISMODE_ABS ? "absolute" : "relative" );
1310 impl->user_format.dwFlags &= ~DIDFT_AXIS;
1311 impl->user_format.dwFlags |= value->dwData == DIPROPAXISMODE_ABS ? DIDF_ABSAXIS : DIDF_RELAXIS;
1313 return DI_OK;
1315 case (DWORD_PTR)DIPROP_BUFFERSIZE:
1317 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1319 TRACE( "buffersize %lu\n", value->dwData );
1321 impl->buffersize = value->dwData;
1322 impl->queue_len = min( impl->buffersize, 1024 );
1323 free( impl->data_queue );
1325 impl->data_queue = impl->queue_len ? malloc( impl->queue_len * sizeof(DIDEVICEOBJECTDATA) ) : NULL;
1326 impl->queue_head = impl->queue_tail = impl->overflow = 0;
1327 return DI_OK;
1329 case (DWORD_PTR)DIPROP_APPDATA:
1331 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, set_object_property, &params );
1332 if (FAILED(hr)) return hr;
1333 return DI_OK;
1335 default:
1336 FIXME( "Unknown property %s\n", debugstr_guid( guid ) );
1337 return DIERR_UNSUPPORTED;
1340 return DI_OK;
1343 static HRESULT WINAPI dinput_device_SetProperty( IDirectInputDevice8W *iface, const GUID *guid,
1344 const DIPROPHEADER *header )
1346 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1347 HRESULT hr;
1349 TRACE( "iface %p, guid %s, header %p\n", iface, debugstr_guid( guid ), header );
1351 if (!header) return DIERR_INVALIDPARAM;
1352 if (header->dwHeaderSize != sizeof(DIPROPHEADER)) return DIERR_INVALIDPARAM;
1353 if (!IS_DIPROP( guid )) return DI_OK;
1355 EnterCriticalSection( &impl->crit );
1356 hr = dinput_device_set_property( iface, guid, header );
1357 LeaveCriticalSection( &impl->crit );
1359 return hr;
1362 static void dinput_device_set_username( struct dinput_device *impl, const DIPROPSTRING *value )
1364 struct DevicePlayer *device_player;
1365 BOOL found = FALSE;
1367 LIST_FOR_EACH_ENTRY( device_player, &impl->dinput->device_players, struct DevicePlayer, entry )
1369 if (IsEqualGUID( &device_player->instance_guid, &impl->guid ))
1371 found = TRUE;
1372 break;
1375 if (!found && (device_player = malloc( sizeof(struct DevicePlayer) )))
1377 list_add_tail( &impl->dinput->device_players, &device_player->entry );
1378 device_player->instance_guid = impl->guid;
1380 if (device_player)
1381 lstrcpynW( device_player->username, value->wsz, ARRAY_SIZE(device_player->username) );
1384 static BOOL get_object_info( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1385 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1387 DIDEVICEOBJECTINSTANCEW *dest = data;
1388 DWORD size = dest->dwSize;
1390 memcpy( dest, instance, size );
1391 dest->dwSize = size;
1393 return DIENUM_STOP;
1396 static HRESULT WINAPI dinput_device_GetObjectInfo( IDirectInputDevice8W *iface,
1397 DIDEVICEOBJECTINSTANCEW *instance, DWORD obj, DWORD how )
1399 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1400 DIPROPHEADER filter =
1402 .dwSize = sizeof(filter),
1403 .dwHeaderSize = sizeof(filter),
1404 .dwHow = how,
1405 .dwObj = obj
1407 HRESULT hr;
1409 TRACE( "iface %p, instance %p, obj %#lx, how %#lx.\n", iface, instance, obj, how );
1411 if (!instance) return E_POINTER;
1412 if (instance->dwSize != sizeof(DIDEVICEOBJECTINSTANCE_DX3W) && instance->dwSize != sizeof(DIDEVICEOBJECTINSTANCEW))
1413 return DIERR_INVALIDPARAM;
1414 if (how == DIPH_DEVICE) return DIERR_INVALIDPARAM;
1415 if (FAILED(hr = enum_object_filter_init( impl, &filter ))) return hr;
1417 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, get_object_info, instance );
1418 if (FAILED(hr)) return hr;
1419 if (hr == DIENUM_CONTINUE) return DIERR_NOTFOUND;
1420 return DI_OK;
1423 static HRESULT WINAPI dinput_device_GetDeviceState( IDirectInputDevice8W *iface, DWORD size, void *data )
1425 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1426 DIDATAFORMAT *device_format = &impl->device_format, *user_format = &impl->user_format;
1427 DIOBJECTDATAFORMAT *device_obj, *user_obj;
1428 BYTE *user_state = data;
1429 DIPROPHEADER filter =
1431 .dwSize = sizeof(filter),
1432 .dwHeaderSize = sizeof(filter),
1433 .dwHow = DIPH_DEVICE,
1434 .dwObj = 0,
1436 HRESULT hr;
1438 TRACE( "iface %p, size %lu, data %p.\n", iface, size, data );
1440 if (!data) return DIERR_INVALIDPARAM;
1442 IDirectInputDevice2_Poll( iface );
1444 EnterCriticalSection( &impl->crit );
1445 if (impl->status == STATUS_UNPLUGGED)
1446 hr = DIERR_INPUTLOST;
1447 else if (impl->status != STATUS_ACQUIRED)
1448 hr = DIERR_NOTACQUIRED;
1449 else if (!user_format->rgodf)
1450 hr = DIERR_INVALIDPARAM;
1451 else if (size != user_format->dwDataSize)
1452 hr = DIERR_INVALIDPARAM;
1453 else
1455 memset( user_state, 0, size );
1457 user_obj = user_format->rgodf + device_format->dwNumObjs;
1458 device_obj = device_format->rgodf + device_format->dwNumObjs;
1459 while (user_obj-- > user_format->rgodf && device_obj-- > device_format->rgodf)
1461 if (user_obj->dwType & DIDFT_BUTTON)
1462 user_state[user_obj->dwOfs] = impl->device_state[device_obj->dwOfs];
1465 /* reset optional POVs to their default */
1466 user_obj = user_format->rgodf + user_format->dwNumObjs;
1467 while (user_obj-- > user_format->rgodf + device_format->dwNumObjs)
1468 if (user_obj->dwType & DIDFT_POV) *(ULONG *)(user_state + user_obj->dwOfs) = 0xffffffff;
1470 user_obj = user_format->rgodf + device_format->dwNumObjs;
1471 device_obj = device_format->rgodf + device_format->dwNumObjs;
1472 while (user_obj-- > user_format->rgodf && device_obj-- > device_format->rgodf)
1474 if (user_obj->dwType & (DIDFT_POV | DIDFT_AXIS))
1475 *(ULONG *)(user_state + user_obj->dwOfs) = *(ULONG *)(impl->device_state + device_obj->dwOfs);
1476 if (!(user_format->dwFlags & DIDF_ABSAXIS) && (device_obj->dwType & DIDFT_RELAXIS))
1477 *(ULONG *)(impl->device_state + device_obj->dwOfs) = 0;
1480 hr = DI_OK;
1482 LeaveCriticalSection( &impl->crit );
1484 return hr;
1487 static HRESULT WINAPI dinput_device_GetDeviceData( IDirectInputDevice8W *iface, DWORD size, DIDEVICEOBJECTDATA *data,
1488 DWORD *count, DWORD flags )
1490 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
1491 HRESULT ret = DI_OK;
1492 int len;
1494 TRACE( "iface %p, size %lu, data %p, count %p, flags %#lx.\n", iface, size, data, count, flags );
1496 if (This->dinput->dwVersion == 0x0800 || size == sizeof(DIDEVICEOBJECTDATA_DX3))
1498 if (!This->queue_len) return DIERR_NOTBUFFERED;
1499 if (This->status == STATUS_UNPLUGGED) return DIERR_INPUTLOST;
1500 if (This->status != STATUS_ACQUIRED) return DIERR_NOTACQUIRED;
1503 if (!This->queue_len)
1504 return DI_OK;
1505 if (size < sizeof(DIDEVICEOBJECTDATA_DX3)) return DIERR_INVALIDPARAM;
1507 IDirectInputDevice2_Poll(iface);
1508 EnterCriticalSection(&This->crit);
1510 len = This->queue_head - This->queue_tail;
1511 if (len < 0) len += This->queue_len;
1513 if ((*count != INFINITE) && (len > *count)) len = *count;
1515 if (data)
1517 int i;
1518 for (i = 0; i < len; i++)
1520 int n = (This->queue_tail + i) % This->queue_len;
1521 memcpy( (char *)data + size * i, This->data_queue + n, size );
1524 *count = len;
1526 if (This->overflow && This->dinput->dwVersion == 0x0800)
1527 ret = DI_BUFFEROVERFLOW;
1529 if (!(flags & DIGDD_PEEK))
1531 /* Advance reading position */
1532 This->queue_tail = (This->queue_tail + len) % This->queue_len;
1533 This->overflow = FALSE;
1536 LeaveCriticalSection(&This->crit);
1538 TRACE( "Returning %lu events queued\n", *count );
1539 return ret;
1542 static HRESULT WINAPI dinput_device_RunControlPanel( IDirectInputDevice8W *iface, HWND hwnd, DWORD flags )
1544 FIXME( "iface %p, hwnd %p, flags %#lx stub!\n", iface, hwnd, flags );
1545 return DI_OK;
1548 static HRESULT WINAPI dinput_device_Initialize( IDirectInputDevice8W *iface, HINSTANCE instance,
1549 DWORD version, const GUID *guid )
1551 FIXME( "iface %p, instance %p, version %#lx, guid %s stub!\n", iface, instance, version,
1552 debugstr_guid( guid ) );
1553 return DI_OK;
1556 static HRESULT WINAPI dinput_device_CreateEffect( IDirectInputDevice8W *iface, const GUID *guid,
1557 const DIEFFECT *params, IDirectInputEffect **out,
1558 IUnknown *outer )
1560 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1561 DWORD flags;
1562 HRESULT hr;
1564 TRACE( "iface %p, guid %s, params %p, out %p, outer %p\n", iface, debugstr_guid( guid ),
1565 params, out, outer );
1567 if (!out) return E_POINTER;
1568 *out = NULL;
1570 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1571 if (!impl->vtbl->create_effect) return DIERR_UNSUPPORTED;
1572 if (FAILED(hr = impl->vtbl->create_effect( iface, out ))) return hr;
1574 hr = IDirectInputEffect_Initialize( *out, DINPUT_instance, impl->dinput->dwVersion, guid );
1575 if (FAILED(hr)) goto failed;
1576 if (!params) return DI_OK;
1578 flags = params->dwSize == sizeof(DIEFFECT_DX6) ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
1579 if (!is_exclusively_acquired( impl )) flags |= DIEP_NODOWNLOAD;
1580 hr = IDirectInputEffect_SetParameters( *out, params, flags );
1581 if (FAILED(hr)) goto failed;
1582 return DI_OK;
1584 failed:
1585 IDirectInputEffect_Release( *out );
1586 *out = NULL;
1587 return hr;
1590 static HRESULT WINAPI dinput_device_EnumEffects( IDirectInputDevice8W *iface, LPDIENUMEFFECTSCALLBACKW callback,
1591 void *context, DWORD type )
1593 DIEFFECTINFOW info = {.dwSize = sizeof(info)};
1594 HRESULT hr;
1596 TRACE( "iface %p, callback %p, context %p, type %#lx.\n", iface, callback, context, type );
1598 if (!callback) return DIERR_INVALIDPARAM;
1600 type = DIEFT_GETTYPE( type );
1602 if (type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1604 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_ConstantForce );
1605 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1606 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1609 if (type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1611 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_RampForce );
1612 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1613 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1616 if (type == DIEFT_ALL || type == DIEFT_PERIODIC)
1618 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Square );
1619 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1620 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1622 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Sine );
1623 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1624 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1626 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Triangle );
1627 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1628 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1630 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_SawtoothUp );
1631 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1632 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1634 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_SawtoothDown );
1635 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1636 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1639 if (type == DIEFT_ALL || type == DIEFT_CONDITION)
1641 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Spring );
1642 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1643 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1645 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Damper );
1646 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1647 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1649 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Inertia );
1650 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1651 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1653 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Friction );
1654 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1655 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1658 return DI_OK;
1661 static HRESULT WINAPI dinput_device_GetEffectInfo( IDirectInputDevice8W *iface, DIEFFECTINFOW *info,
1662 const GUID *guid )
1664 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1666 TRACE( "iface %p, info %p, guid %s.\n", iface, info, debugstr_guid( guid ) );
1668 if (!info) return E_POINTER;
1669 if (info->dwSize != sizeof(DIEFFECTINFOW)) return DIERR_INVALIDPARAM;
1670 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_DEVICENOTREG;
1671 if (!impl->vtbl->get_effect_info) return DIERR_UNSUPPORTED;
1672 return impl->vtbl->get_effect_info( iface, info, guid );
1675 static HRESULT WINAPI dinput_device_GetForceFeedbackState( IDirectInputDevice8W *iface, DWORD *out )
1677 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1678 HRESULT hr = DI_OK;
1680 TRACE( "iface %p, out %p.\n", iface, out );
1682 if (!out) return E_POINTER;
1683 *out = 0;
1685 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1687 EnterCriticalSection( &impl->crit );
1688 if (!is_exclusively_acquired( impl )) hr = DIERR_NOTEXCLUSIVEACQUIRED;
1689 else *out = impl->force_feedback_state;
1690 LeaveCriticalSection( &impl->crit );
1692 return hr;
1695 static HRESULT WINAPI dinput_device_SendForceFeedbackCommand( IDirectInputDevice8W *iface, DWORD command )
1697 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1698 HRESULT hr;
1700 TRACE( "iface %p, command %#lx.\n", iface, command );
1702 switch (command)
1704 case DISFFC_RESET: break;
1705 case DISFFC_STOPALL: break;
1706 case DISFFC_PAUSE: break;
1707 case DISFFC_CONTINUE: break;
1708 case DISFFC_SETACTUATORSON: break;
1709 case DISFFC_SETACTUATORSOFF: break;
1710 default: return DIERR_INVALIDPARAM;
1713 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1714 if (!impl->vtbl->send_force_feedback_command) return DIERR_UNSUPPORTED;
1716 EnterCriticalSection( &impl->crit );
1717 if (!is_exclusively_acquired( impl )) hr = DIERR_NOTEXCLUSIVEACQUIRED;
1718 else hr = impl->vtbl->send_force_feedback_command( iface, command, FALSE );
1719 LeaveCriticalSection( &impl->crit );
1721 return hr;
1724 static HRESULT WINAPI dinput_device_EnumCreatedEffectObjects( IDirectInputDevice8W *iface,
1725 LPDIENUMCREATEDEFFECTOBJECTSCALLBACK callback,
1726 void *context, DWORD flags )
1728 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1730 TRACE( "iface %p, callback %p, context %p, flags %#lx.\n", iface, callback, context, flags );
1732 if (!callback) return DIERR_INVALIDPARAM;
1733 if (flags) return DIERR_INVALIDPARAM;
1734 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DI_OK;
1735 if (!impl->vtbl->enum_created_effect_objects) return DIERR_UNSUPPORTED;
1737 return impl->vtbl->enum_created_effect_objects( iface, callback, context, flags );
1740 static HRESULT WINAPI dinput_device_Escape( IDirectInputDevice8W *iface, DIEFFESCAPE *escape )
1742 FIXME( "iface %p, escape %p stub!\n", iface, escape );
1743 return DI_OK;
1746 static HRESULT WINAPI dinput_device_Poll( IDirectInputDevice8W *iface )
1748 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1749 HRESULT hr = DI_NOEFFECT;
1751 EnterCriticalSection( &impl->crit );
1752 if (impl->status == STATUS_UNPLUGGED) hr = DIERR_INPUTLOST;
1753 else if (impl->status != STATUS_ACQUIRED) hr = DIERR_NOTACQUIRED;
1754 LeaveCriticalSection( &impl->crit );
1755 if (FAILED(hr)) return hr;
1757 if (impl->vtbl->poll) return impl->vtbl->poll( iface );
1758 return hr;
1761 static HRESULT WINAPI dinput_device_SendDeviceData( IDirectInputDevice8W *iface, DWORD size,
1762 const DIDEVICEOBJECTDATA *data, DWORD *count, DWORD flags )
1764 FIXME( "iface %p, size %lu, data %p, count %p, flags %#lx stub!\n", iface, size, data, count, flags );
1765 return DI_OK;
1768 static HRESULT WINAPI dinput_device_EnumEffectsInFile( IDirectInputDevice8W *iface, const WCHAR *filename,
1769 LPDIENUMEFFECTSINFILECALLBACK callback,
1770 void *context, DWORD flags )
1772 FIXME( "iface %p, filename %s, callback %p, context %p, flags %#lx stub!\n", iface,
1773 debugstr_w(filename), callback, context, flags );
1774 return DI_OK;
1777 static HRESULT WINAPI dinput_device_WriteEffectToFile( IDirectInputDevice8W *iface, const WCHAR *filename,
1778 DWORD count, DIFILEEFFECT *effects, DWORD flags )
1780 FIXME( "iface %p, filename %s, count %lu, effects %p, flags %#lx stub!\n", iface,
1781 debugstr_w(filename), count, effects, flags );
1782 return DI_OK;
1785 BOOL device_object_matches_semantic( const DIDEVICEINSTANCEW *instance, const DIOBJECTDATAFORMAT *object,
1786 DWORD semantic, BOOL exact )
1788 DWORD value = semantic & 0xff, axis = (semantic >> 15) & 3, type;
1790 switch (semantic & 0x700)
1792 case 0x200: type = DIDFT_ABSAXIS; break;
1793 case 0x300: type = DIDFT_RELAXIS; break;
1794 case 0x400: type = DIDFT_BUTTON; break;
1795 case 0x600: type = DIDFT_POV; break;
1796 default: return FALSE;
1799 if (!(DIDFT_GETTYPE( object->dwType ) & type)) return FALSE;
1800 if ((semantic & 0xf0000000) == 0x80000000)
1802 switch (semantic & 0x0f000000)
1804 case 0x01000000: return (instance->dwDevType & 0xf) == DIDEVTYPE_KEYBOARD && object->dwOfs == value;
1805 case 0x02000000: return (instance->dwDevType & 0xf) == DIDEVTYPE_MOUSE && object->dwOfs == value;
1806 default: return FALSE;
1809 if (axis && (axis - 1) != DIDFT_GETINSTANCE( object->dwType )) return FALSE;
1810 return !exact || !value || value == DIDFT_GETINSTANCE( object->dwType ) + 1;
1813 static HRESULT WINAPI dinput_device_BuildActionMap( IDirectInputDevice8W *iface, DIACTIONFORMATW *format,
1814 const WCHAR *username, DWORD flags )
1816 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1817 DIOBJECTDATAFORMAT *object, *object_end;
1818 DIACTIONW *action, *action_end;
1819 DWORD i, username_len = MAX_PATH;
1820 WCHAR username_buf[MAX_PATH];
1821 BOOL *mapped;
1823 TRACE( "iface %p, format %p, username %s, flags %#lx\n", iface, format,
1824 debugstr_w(username), flags );
1826 if (!format) return DIERR_INVALIDPARAM;
1827 if (flags != DIDBAM_DEFAULT && flags != DIDBAM_PRESERVE &&
1828 flags != DIDBAM_INITIALIZE && flags != DIDBAM_HWDEFAULTS)
1829 return DIERR_INVALIDPARAM;
1830 if (format->dwNumActions * 4 != format->dwDataSize)
1831 return DIERR_INVALIDPARAM;
1833 TRACE( "format guid %s, genre %#lx, name %s\n", debugstr_guid(&format->guidActionMap),
1834 format->dwGenre, debugstr_w(format->tszActionMap) );
1835 for (i = 0; i < format->dwNumActions; i++)
1837 DIACTIONW *action = format->rgoAction + i;
1838 TRACE( " %lu: app_data %#Ix, semantic %#lx, flags %#lx, instance %s, obj_id %#lx, how %#lx, name %s\n",
1839 i, action->uAppData, action->dwSemantic, action->dwFlags, debugstr_guid(&action->guidInstance),
1840 action->dwObjID, action->dwHow, debugstr_w(action->lptszActionName) );
1843 action_end = format->rgoAction + format->dwNumActions;
1844 for (action = format->rgoAction; action < action_end; action++)
1846 if (!action->dwSemantic) return DIERR_INVALIDPARAM;
1847 if (flags == DIDBAM_PRESERVE && !IsEqualCLSID( &action->guidInstance, &GUID_NULL ) &&
1848 !IsEqualCLSID( &action->guidInstance, &impl->guid )) continue;
1849 if (action->dwFlags & DIA_APPMAPPED) action->dwHow = DIAH_APPREQUESTED;
1850 else action->dwHow = 0;
1851 if (action->dwHow == DIAH_APPREQUESTED || action->dwHow == DIAH_USERCONFIG) continue;
1852 if ((action->dwSemantic & 0xf0000000) == 0x80000000) action->dwFlags &= ~DIA_APPNOMAP;
1853 if (!(action->dwFlags & DIA_APPNOMAP)) action->guidInstance = GUID_NULL;
1856 /* Unless asked the contrary by these flags, try to load a previous mapping */
1857 if (!(flags & DIDBAM_HWDEFAULTS))
1859 /* Retrieve logged user name if necessary */
1860 if (username == NULL) GetUserNameW( username_buf, &username_len );
1861 else lstrcpynW( username_buf, username, MAX_PATH );
1862 load_mapping_settings( impl, format, username_buf );
1865 if (!(mapped = calloc( impl->device_format.dwNumObjs, sizeof(*mapped) ))) return DIERR_OUTOFMEMORY;
1867 /* check already mapped objects */
1868 action_end = format->rgoAction + format->dwNumActions;
1869 for (action = format->rgoAction; action < action_end; action++)
1871 if (!action->dwHow || !action->dwObjID) continue;
1872 if (!IsEqualGUID(&action->guidInstance, &impl->guid)) continue;
1874 object_end = impl->device_format.rgodf + impl->device_format.dwNumObjs;
1875 for (object = impl->device_format.rgodf; object < object_end; object++)
1877 if (action->dwObjID != object->dwType) continue;
1878 mapped[object - impl->device_format.rgodf] = TRUE;
1879 break;
1883 /* map any unmapped priority 1 objects */
1884 action_end = format->rgoAction + format->dwNumActions;
1885 for (action = format->rgoAction; action < action_end; action++)
1887 if (action->dwHow || (action->dwFlags & DIA_APPNOMAP)) continue; /* already mapped */
1888 if (action->dwSemantic & 0x4000) continue; /* priority 1 */
1890 object_end = impl->device_format.rgodf + impl->device_format.dwNumObjs;
1891 for (object = impl->device_format.rgodf; object < object_end; object++)
1893 if (mapped[object - impl->device_format.rgodf]) continue;
1894 if (!device_object_matches_semantic( &impl->instance, object, action->dwSemantic, TRUE )) continue;
1895 if ((action->dwFlags & DIA_FORCEFEEDBACK) && !(object->dwType & DIDFT_FFACTUATOR)) continue;
1896 action->dwObjID = object->dwType;
1897 action->guidInstance = impl->guid;
1898 action->dwHow = DIAH_DEFAULT;
1899 mapped[object - impl->device_format.rgodf] = TRUE;
1900 break;
1904 /* map any unmapped priority 2 objects */
1905 for (action = format->rgoAction; action < action_end; action++)
1907 if (action->dwHow || (action->dwFlags & DIA_APPNOMAP)) continue; /* already mapped */
1908 if (!(action->dwSemantic & 0x4000)) continue; /* priority 2 */
1910 object_end = impl->device_format.rgodf + impl->device_format.dwNumObjs;
1911 for (object = impl->device_format.rgodf; object < object_end; object++)
1913 if (mapped[object - impl->device_format.rgodf]) continue;
1914 if (!device_object_matches_semantic( &impl->instance, object, action->dwSemantic, FALSE )) continue;
1915 if ((action->dwFlags & DIA_FORCEFEEDBACK) && !(object->dwType & DIDFT_FFACTUATOR)) continue;
1916 action->dwObjID = object->dwType;
1917 action->guidInstance = impl->guid;
1918 action->dwHow = DIAH_DEFAULT;
1919 mapped[object - impl->device_format.rgodf] = TRUE;
1920 break;
1924 for (i = 0; i < impl->device_format.dwNumObjs; ++i) if (mapped[i]) break;
1925 free( mapped );
1927 if (i == impl->device_format.dwNumObjs) return DI_NOEFFECT;
1928 return DI_OK;
1931 static BOOL init_object_app_data( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1932 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1934 struct object_properties *properties;
1935 const DIACTIONFORMATW *format = data;
1936 const DIACTIONW *action = format->rgoAction + format->dwNumActions;
1938 if (index == -1) return DIENUM_STOP;
1939 if (instance->wUsagePage == HID_USAGE_PAGE_PID) return DIENUM_CONTINUE;
1941 properties = device->object_properties + index;
1942 properties->app_data = 0;
1944 while (action-- > format->rgoAction)
1946 if (action->dwObjID != instance->dwType) continue;
1947 properties->app_data = action->uAppData;
1948 break;
1951 return DIENUM_CONTINUE;
1954 static HRESULT WINAPI dinput_device_SetActionMap( IDirectInputDevice8W *iface, DIACTIONFORMATW *format,
1955 const WCHAR *username, DWORD flags )
1957 static const DIPROPHEADER filter =
1959 .dwSize = sizeof(filter),
1960 .dwHeaderSize = sizeof(filter),
1961 .dwHow = DIPH_DEVICE,
1963 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1964 DIDATAFORMAT data_format =
1966 .dwSize = sizeof(DIDATAFORMAT),
1967 .dwObjSize = sizeof(DIOBJECTDATAFORMAT),
1968 .dwFlags = DIDF_RELAXIS,
1970 DIPROPDWORD prop_buffer =
1972 .diph =
1974 .dwHeaderSize = sizeof(DIPROPHEADER),
1975 .dwSize = sizeof(DIPROPDWORD),
1976 .dwHow = DIPH_DEVICE,
1979 DIPROPRANGE prop_range =
1981 .diph =
1983 .dwHeaderSize = sizeof(DIPROPHEADER),
1984 .dwSize = sizeof(DIPROPRANGE),
1985 .dwHow = DIPH_DEVICE,
1988 DIPROPSTRING prop_username =
1990 .diph =
1992 .dwHeaderSize = sizeof(DIPROPHEADER),
1993 .dwSize = sizeof(DIPROPSTRING),
1994 .dwHow = DIPH_DEVICE,
1997 WCHAR username_buf[MAX_PATH];
1998 DWORD username_len = MAX_PATH;
1999 unsigned int offset = 0;
2000 int i, index;
2001 HRESULT hr;
2003 TRACE( "iface %p, format %p, username %s, flags %#lx\n", iface, format,
2004 debugstr_w(username), flags );
2006 if (!format) return DIERR_INVALIDPARAM;
2007 if (flags != DIDSAM_DEFAULT && flags != DIDSAM_FORCESAVE && flags != DIDSAM_NOUSER) return DIERR_INVALIDPARAM;
2009 TRACE( "format guid %s, genre %#lx, name %s\n", debugstr_guid(&format->guidActionMap),
2010 format->dwGenre, debugstr_w(format->tszActionMap) );
2011 for (i = 0; i < format->dwNumActions; i++)
2013 DIACTIONW *action = format->rgoAction + i;
2014 TRACE( " %u: app_data %#Ix, semantic %#lx, flags %#lx, instance %s, obj_id %#lx, how %#lx, name %s\n",
2015 i, action->uAppData, action->dwSemantic, action->dwFlags, debugstr_guid(&action->guidInstance),
2016 action->dwObjID, action->dwHow, debugstr_w(action->lptszActionName) );
2019 if (!(data_format.rgodf = malloc( sizeof(DIOBJECTDATAFORMAT) * format->dwNumActions ))) return DIERR_OUTOFMEMORY;
2020 data_format.dwDataSize = format->dwDataSize;
2022 for (i = 0; i < format->dwNumActions; i++, offset += sizeof(ULONG))
2024 if (format->rgoAction[i].dwFlags & DIA_APPNOMAP) continue;
2025 if (!IsEqualGUID( &impl->guid, &format->rgoAction[i].guidInstance )) continue;
2026 if ((index = dinput_device_object_index_from_id( iface, format->rgoAction[i].dwObjID )) < 0) continue;
2028 data_format.rgodf[data_format.dwNumObjs] = impl->device_format.rgodf[index];
2029 data_format.rgodf[data_format.dwNumObjs].dwOfs = offset;
2030 data_format.dwNumObjs++;
2033 EnterCriticalSection( &impl->crit );
2035 if (FAILED(hr = IDirectInputDevice8_SetDataFormat( iface, &data_format )))
2036 WARN( "Failed to set data format from action map, hr %#lx\n", hr );
2037 else
2039 if (FAILED(impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, init_object_app_data, format )))
2040 WARN( "Failed to initialize action map app data\n" );
2042 if (format->lAxisMin != format->lAxisMax)
2044 prop_range.lMin = format->lAxisMin;
2045 prop_range.lMax = format->lAxisMax;
2046 IDirectInputDevice8_SetProperty( iface, DIPROP_RANGE, &prop_range.diph );
2049 prop_buffer.dwData = format->dwBufferSize;
2050 IDirectInputDevice8_SetProperty( iface, DIPROP_BUFFERSIZE, &prop_buffer.diph );
2052 if (username == NULL) GetUserNameW( username_buf, &username_len );
2053 else lstrcpynW( username_buf, username, MAX_PATH );
2055 if (flags & DIDSAM_NOUSER) prop_username.wsz[0] = '\0';
2056 else lstrcpynW( prop_username.wsz, username_buf, ARRAY_SIZE(prop_username.wsz) );
2057 dinput_device_set_username( impl, &prop_username );
2059 save_mapping_settings( iface, format, username_buf );
2062 LeaveCriticalSection( &impl->crit );
2064 free( data_format.rgodf );
2066 if (FAILED(hr)) return hr;
2067 if (flags == DIDSAM_FORCESAVE) return DI_SETTINGSNOTSAVED;
2068 if (!data_format.dwNumObjs) return DI_NOEFFECT;
2069 return hr;
2072 static HRESULT WINAPI dinput_device_GetImageInfo( IDirectInputDevice8W *iface, DIDEVICEIMAGEINFOHEADERW *header )
2074 FIXME( "iface %p, header %p stub!\n", iface, header );
2075 return DI_OK;
2078 extern const IDirectInputDevice8AVtbl dinput_device_a_vtbl;
2079 static const IDirectInputDevice8WVtbl dinput_device_w_vtbl =
2081 /*** IUnknown methods ***/
2082 dinput_device_QueryInterface,
2083 dinput_device_AddRef,
2084 dinput_device_Release,
2085 /*** IDirectInputDevice methods ***/
2086 dinput_device_GetCapabilities,
2087 dinput_device_EnumObjects,
2088 dinput_device_GetProperty,
2089 dinput_device_SetProperty,
2090 dinput_device_Acquire,
2091 dinput_device_Unacquire,
2092 dinput_device_GetDeviceState,
2093 dinput_device_GetDeviceData,
2094 dinput_device_SetDataFormat,
2095 dinput_device_SetEventNotification,
2096 dinput_device_SetCooperativeLevel,
2097 dinput_device_GetObjectInfo,
2098 dinput_device_GetDeviceInfo,
2099 dinput_device_RunControlPanel,
2100 dinput_device_Initialize,
2101 /*** IDirectInputDevice2 methods ***/
2102 dinput_device_CreateEffect,
2103 dinput_device_EnumEffects,
2104 dinput_device_GetEffectInfo,
2105 dinput_device_GetForceFeedbackState,
2106 dinput_device_SendForceFeedbackCommand,
2107 dinput_device_EnumCreatedEffectObjects,
2108 dinput_device_Escape,
2109 dinput_device_Poll,
2110 dinput_device_SendDeviceData,
2111 /*** IDirectInputDevice7 methods ***/
2112 dinput_device_EnumEffectsInFile,
2113 dinput_device_WriteEffectToFile,
2114 /*** IDirectInputDevice8 methods ***/
2115 dinput_device_BuildActionMap,
2116 dinput_device_SetActionMap,
2117 dinput_device_GetImageInfo,
2120 void dinput_device_init( struct dinput_device *device, const struct dinput_device_vtbl *vtbl,
2121 const GUID *guid, struct dinput *dinput )
2123 device->IDirectInputDevice8A_iface.lpVtbl = &dinput_device_a_vtbl;
2124 device->IDirectInputDevice8W_iface.lpVtbl = &dinput_device_w_vtbl;
2125 device->internal_ref = 1;
2126 device->ref = 1;
2127 device->guid = *guid;
2128 device->instance.dwSize = sizeof(DIDEVICEINSTANCEW);
2129 device->caps.dwSize = sizeof(DIDEVCAPS);
2130 device->caps.dwFlags = DIDC_ATTACHED | DIDC_EMULATED;
2131 device->device_gain = 10000;
2132 device->autocenter = DIPROPAUTOCENTER_ON;
2133 device->force_feedback_state = DIGFFS_STOPPED | DIGFFS_EMPTY;
2134 InitializeCriticalSectionEx( &device->crit, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO );
2135 dinput_internal_addref( (device->dinput = dinput) );
2136 device->vtbl = vtbl;
2138 input_thread_add_user();
2141 static const GUID *object_instance_guid( const DIDEVICEOBJECTINSTANCEW *instance )
2143 if (IsEqualGUID( &instance->guidType, &GUID_XAxis )) return &GUID_XAxis;
2144 if (IsEqualGUID( &instance->guidType, &GUID_YAxis )) return &GUID_YAxis;
2145 if (IsEqualGUID( &instance->guidType, &GUID_ZAxis )) return &GUID_ZAxis;
2146 if (IsEqualGUID( &instance->guidType, &GUID_RxAxis )) return &GUID_RxAxis;
2147 if (IsEqualGUID( &instance->guidType, &GUID_RyAxis )) return &GUID_RyAxis;
2148 if (IsEqualGUID( &instance->guidType, &GUID_RzAxis )) return &GUID_RzAxis;
2149 if (IsEqualGUID( &instance->guidType, &GUID_Slider )) return &GUID_Slider;
2150 if (IsEqualGUID( &instance->guidType, &GUID_Button )) return &GUID_Button;
2151 if (IsEqualGUID( &instance->guidType, &GUID_Key )) return &GUID_Key;
2152 if (IsEqualGUID( &instance->guidType, &GUID_POV )) return &GUID_POV;
2153 return &GUID_Unknown;
2156 static BOOL enum_objects_count( struct dinput_device *impl, UINT index, struct hid_value_caps *caps,
2157 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
2159 DIDATAFORMAT *format = &impl->device_format;
2161 if (index == -1) return DIENUM_STOP;
2162 format->dwNumObjs++;
2163 if (instance->wUsagePage == HID_USAGE_PAGE_PID) return DIENUM_CONTINUE;
2165 format->dwDataSize = max( format->dwDataSize, instance->dwOfs + sizeof(LONG) );
2166 if (instance->dwType & DIDFT_BUTTON) impl->caps.dwButtons++;
2167 if (instance->dwType & DIDFT_AXIS) impl->caps.dwAxes++;
2168 if (instance->dwType & DIDFT_POV) impl->caps.dwPOVs++;
2169 if (instance->dwType & (DIDFT_BUTTON|DIDFT_AXIS|DIDFT_POV))
2171 if (!impl->device_state_report_id)
2172 impl->device_state_report_id = instance->wReportId;
2173 else if (impl->device_state_report_id != instance->wReportId)
2174 FIXME( "multiple device state reports found!\n" );
2177 return DIENUM_CONTINUE;
2180 static BOOL enum_objects_init( struct dinput_device *impl, UINT index, struct hid_value_caps *caps,
2181 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
2183 static const struct object_properties default_properties =
2185 .range_min = DIPROPRANGE_NOMIN,
2186 .range_max = DIPROPRANGE_NOMAX,
2187 .granularity = 1,
2188 .app_data = -1,
2190 DIDATAFORMAT *format = &impl->device_format;
2191 DIOBJECTDATAFORMAT *object_format;
2193 if (index == -1) return DIENUM_STOP;
2194 if (instance->wUsagePage == HID_USAGE_PAGE_PID) return DIENUM_CONTINUE;
2196 object_format = format->rgodf + index;
2197 object_format->pguid = object_instance_guid( instance );
2198 object_format->dwOfs = instance->dwOfs;
2199 object_format->dwType = instance->dwType;
2200 object_format->dwFlags = instance->dwFlags;
2202 impl->object_properties[index] = default_properties;
2203 if (instance->dwType & (DIDFT_AXIS | DIDFT_POV)) reset_object_value( impl, index, caps, instance, NULL );
2205 return DIENUM_CONTINUE;
2208 HRESULT dinput_device_init_device_format( IDirectInputDevice8W *iface )
2210 static const DIPROPHEADER filter =
2212 .dwSize = sizeof(filter),
2213 .dwHeaderSize = sizeof(filter),
2214 .dwHow = DIPH_DEVICE,
2216 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
2217 DIDATAFORMAT *format = &impl->device_format;
2218 HRESULT hr;
2219 ULONG i;
2221 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, enum_objects_count, NULL );
2222 if (FAILED(hr)) return hr;
2224 if (format->dwDataSize > DEVICE_STATE_MAX_SIZE)
2226 FIXME( "unable to create device, state is too large\n" );
2227 return DIERR_OUTOFMEMORY;
2230 if (!(impl->object_properties = calloc( format->dwNumObjs, sizeof(*impl->object_properties) ))) return DIERR_OUTOFMEMORY;
2231 if (!(format->rgodf = calloc( format->dwNumObjs, sizeof(*format->rgodf) ))) return DIERR_OUTOFMEMORY;
2233 format->dwSize = sizeof(*format);
2234 format->dwObjSize = sizeof(*format->rgodf);
2235 format->dwFlags = DIDF_ABSAXIS;
2237 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_AXIS, enum_objects_init, NULL );
2238 if (FAILED(hr)) return hr;
2239 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_POV, enum_objects_init, NULL );
2240 if (FAILED(hr)) return hr;
2241 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_BUTTON, enum_objects_init, NULL );
2242 if (FAILED(hr)) return hr;
2243 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_NODATA, enum_objects_init, NULL );
2244 if (FAILED(hr)) return hr;
2246 if (TRACE_ON( dinput ))
2248 TRACE( "device format %s\n", debugstr_didataformat( format ) );
2249 for (i = 0; i < format->dwNumObjs; ++i) TRACE( " %lu: object %s\n", i, debugstr_diobjectdataformat( format->rgodf + i ) );
2252 return DI_OK;