dinput: Delete the action mapping registry key on SetActionMap.
[wine.git] / dlls / dinput / device.c
blob4f76168a1a346e15ba53d63e46982dfe1416421a
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 && !is_exclusively_acquired( impl )) 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;
1294 FIXME( "DIPROP_AUTOCENTER stub!\n" );
1295 impl->autocenter = value->dwData;
1296 return DI_OK;
1298 case (DWORD_PTR)DIPROP_FFGAIN:
1300 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1301 impl->device_gain = value->dwData;
1302 if (!is_exclusively_acquired( impl )) return DI_OK;
1303 if (!impl->vtbl->send_device_gain) return DI_OK;
1304 return impl->vtbl->send_device_gain( iface, impl->device_gain );
1306 case (DWORD_PTR)DIPROP_AXISMODE:
1308 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1310 TRACE( "Axis mode: %s\n", value->dwData == DIPROPAXISMODE_ABS ? "absolute" : "relative" );
1312 impl->user_format.dwFlags &= ~DIDFT_AXIS;
1313 impl->user_format.dwFlags |= value->dwData == DIPROPAXISMODE_ABS ? DIDF_ABSAXIS : DIDF_RELAXIS;
1315 return DI_OK;
1317 case (DWORD_PTR)DIPROP_BUFFERSIZE:
1319 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1321 TRACE( "buffersize %lu\n", value->dwData );
1323 impl->buffersize = value->dwData;
1324 impl->queue_len = min( impl->buffersize, 1024 );
1325 free( impl->data_queue );
1327 impl->data_queue = impl->queue_len ? malloc( impl->queue_len * sizeof(DIDEVICEOBJECTDATA) ) : NULL;
1328 impl->queue_head = impl->queue_tail = impl->overflow = 0;
1329 return DI_OK;
1331 case (DWORD_PTR)DIPROP_APPDATA:
1333 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, set_object_property, &params );
1334 if (FAILED(hr)) return hr;
1335 return DI_OK;
1337 default:
1338 FIXME( "Unknown property %s\n", debugstr_guid( guid ) );
1339 return DIERR_UNSUPPORTED;
1342 return DI_OK;
1345 static HRESULT WINAPI dinput_device_SetProperty( IDirectInputDevice8W *iface, const GUID *guid,
1346 const DIPROPHEADER *header )
1348 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1349 HRESULT hr;
1351 TRACE( "iface %p, guid %s, header %p\n", iface, debugstr_guid( guid ), header );
1353 if (!header) return DIERR_INVALIDPARAM;
1354 if (header->dwHeaderSize != sizeof(DIPROPHEADER)) return DIERR_INVALIDPARAM;
1355 if (!IS_DIPROP( guid )) return DI_OK;
1357 EnterCriticalSection( &impl->crit );
1358 hr = dinput_device_set_property( iface, guid, header );
1359 LeaveCriticalSection( &impl->crit );
1361 return hr;
1364 static void dinput_device_set_username( struct dinput_device *impl, const DIPROPSTRING *value )
1366 struct DevicePlayer *device_player;
1367 BOOL found = FALSE;
1369 LIST_FOR_EACH_ENTRY( device_player, &impl->dinput->device_players, struct DevicePlayer, entry )
1371 if (IsEqualGUID( &device_player->instance_guid, &impl->guid ))
1373 found = TRUE;
1374 break;
1377 if (!found && (device_player = malloc( sizeof(struct DevicePlayer) )))
1379 list_add_tail( &impl->dinput->device_players, &device_player->entry );
1380 device_player->instance_guid = impl->guid;
1382 if (device_player)
1383 lstrcpynW( device_player->username, value->wsz, ARRAY_SIZE(device_player->username) );
1386 static BOOL get_object_info( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1387 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1389 DIDEVICEOBJECTINSTANCEW *dest = data;
1390 DWORD size = dest->dwSize;
1392 memcpy( dest, instance, size );
1393 dest->dwSize = size;
1395 return DIENUM_STOP;
1398 static HRESULT WINAPI dinput_device_GetObjectInfo( IDirectInputDevice8W *iface,
1399 DIDEVICEOBJECTINSTANCEW *instance, DWORD obj, DWORD how )
1401 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1402 DIPROPHEADER filter =
1404 .dwSize = sizeof(filter),
1405 .dwHeaderSize = sizeof(filter),
1406 .dwHow = how,
1407 .dwObj = obj
1409 HRESULT hr;
1411 TRACE( "iface %p, instance %p, obj %#lx, how %#lx.\n", iface, instance, obj, how );
1413 if (!instance) return E_POINTER;
1414 if (instance->dwSize != sizeof(DIDEVICEOBJECTINSTANCE_DX3W) && instance->dwSize != sizeof(DIDEVICEOBJECTINSTANCEW))
1415 return DIERR_INVALIDPARAM;
1416 if (how == DIPH_DEVICE) return DIERR_INVALIDPARAM;
1417 if (FAILED(hr = enum_object_filter_init( impl, &filter ))) return hr;
1419 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, get_object_info, instance );
1420 if (FAILED(hr)) return hr;
1421 if (hr == DIENUM_CONTINUE) return DIERR_NOTFOUND;
1422 return DI_OK;
1425 static HRESULT WINAPI dinput_device_GetDeviceState( IDirectInputDevice8W *iface, DWORD size, void *data )
1427 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1428 DIDATAFORMAT *device_format = &impl->device_format, *user_format = &impl->user_format;
1429 DIOBJECTDATAFORMAT *device_obj, *user_obj;
1430 BYTE *user_state = data;
1431 DIPROPHEADER filter =
1433 .dwSize = sizeof(filter),
1434 .dwHeaderSize = sizeof(filter),
1435 .dwHow = DIPH_DEVICE,
1436 .dwObj = 0,
1438 HRESULT hr;
1440 TRACE( "iface %p, size %lu, data %p.\n", iface, size, data );
1442 if (!data) return DIERR_INVALIDPARAM;
1444 IDirectInputDevice2_Poll( iface );
1446 EnterCriticalSection( &impl->crit );
1447 if (impl->status == STATUS_UNPLUGGED)
1448 hr = DIERR_INPUTLOST;
1449 else if (impl->status != STATUS_ACQUIRED)
1450 hr = DIERR_NOTACQUIRED;
1451 else if (!user_format->rgodf)
1452 hr = DIERR_INVALIDPARAM;
1453 else if (size != user_format->dwDataSize)
1454 hr = DIERR_INVALIDPARAM;
1455 else
1457 memset( user_state, 0, size );
1459 user_obj = user_format->rgodf + device_format->dwNumObjs;
1460 device_obj = device_format->rgodf + device_format->dwNumObjs;
1461 while (user_obj-- > user_format->rgodf && device_obj-- > device_format->rgodf)
1463 if (user_obj->dwType & DIDFT_BUTTON)
1464 user_state[user_obj->dwOfs] = impl->device_state[device_obj->dwOfs];
1467 /* reset optional POVs to their default */
1468 user_obj = user_format->rgodf + user_format->dwNumObjs;
1469 while (user_obj-- > user_format->rgodf + device_format->dwNumObjs)
1470 if (user_obj->dwType & DIDFT_POV) *(ULONG *)(user_state + user_obj->dwOfs) = 0xffffffff;
1472 user_obj = user_format->rgodf + device_format->dwNumObjs;
1473 device_obj = device_format->rgodf + device_format->dwNumObjs;
1474 while (user_obj-- > user_format->rgodf && device_obj-- > device_format->rgodf)
1476 if (user_obj->dwType & (DIDFT_POV | DIDFT_AXIS))
1477 *(ULONG *)(user_state + user_obj->dwOfs) = *(ULONG *)(impl->device_state + device_obj->dwOfs);
1478 if (!(user_format->dwFlags & DIDF_ABSAXIS) && (device_obj->dwType & DIDFT_RELAXIS))
1479 *(ULONG *)(impl->device_state + device_obj->dwOfs) = 0;
1482 hr = DI_OK;
1484 LeaveCriticalSection( &impl->crit );
1486 return hr;
1489 static HRESULT WINAPI dinput_device_GetDeviceData( IDirectInputDevice8W *iface, DWORD size, DIDEVICEOBJECTDATA *data,
1490 DWORD *count, DWORD flags )
1492 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
1493 HRESULT ret = DI_OK;
1494 int len;
1496 TRACE( "iface %p, size %lu, data %p, count %p, flags %#lx.\n", iface, size, data, count, flags );
1498 if (This->dinput->dwVersion == 0x0800 || size == sizeof(DIDEVICEOBJECTDATA_DX3))
1500 if (!This->queue_len) return DIERR_NOTBUFFERED;
1501 if (This->status == STATUS_UNPLUGGED) return DIERR_INPUTLOST;
1502 if (This->status != STATUS_ACQUIRED) return DIERR_NOTACQUIRED;
1505 if (!This->queue_len)
1506 return DI_OK;
1507 if (size < sizeof(DIDEVICEOBJECTDATA_DX3)) return DIERR_INVALIDPARAM;
1509 IDirectInputDevice2_Poll(iface);
1510 EnterCriticalSection(&This->crit);
1512 len = This->queue_head - This->queue_tail;
1513 if (len < 0) len += This->queue_len;
1515 if ((*count != INFINITE) && (len > *count)) len = *count;
1517 if (data)
1519 int i;
1520 for (i = 0; i < len; i++)
1522 int n = (This->queue_tail + i) % This->queue_len;
1523 memcpy( (char *)data + size * i, This->data_queue + n, size );
1526 *count = len;
1528 if (This->overflow && This->dinput->dwVersion == 0x0800)
1529 ret = DI_BUFFEROVERFLOW;
1531 if (!(flags & DIGDD_PEEK))
1533 /* Advance reading position */
1534 This->queue_tail = (This->queue_tail + len) % This->queue_len;
1535 This->overflow = FALSE;
1538 LeaveCriticalSection(&This->crit);
1540 TRACE( "Returning %lu events queued\n", *count );
1541 return ret;
1544 static HRESULT WINAPI dinput_device_RunControlPanel( IDirectInputDevice8W *iface, HWND hwnd, DWORD flags )
1546 FIXME( "iface %p, hwnd %p, flags %#lx stub!\n", iface, hwnd, flags );
1547 return DI_OK;
1550 static HRESULT WINAPI dinput_device_Initialize( IDirectInputDevice8W *iface, HINSTANCE instance,
1551 DWORD version, const GUID *guid )
1553 FIXME( "iface %p, instance %p, version %#lx, guid %s stub!\n", iface, instance, version,
1554 debugstr_guid( guid ) );
1555 return DI_OK;
1558 static HRESULT WINAPI dinput_device_CreateEffect( IDirectInputDevice8W *iface, const GUID *guid,
1559 const DIEFFECT *params, IDirectInputEffect **out,
1560 IUnknown *outer )
1562 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1563 DWORD flags;
1564 HRESULT hr;
1566 TRACE( "iface %p, guid %s, params %p, out %p, outer %p\n", iface, debugstr_guid( guid ),
1567 params, out, outer );
1569 if (!out) return E_POINTER;
1570 *out = NULL;
1572 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1573 if (!impl->vtbl->create_effect) return DIERR_UNSUPPORTED;
1574 if (FAILED(hr = impl->vtbl->create_effect( iface, out ))) return hr;
1576 hr = IDirectInputEffect_Initialize( *out, DINPUT_instance, impl->dinput->dwVersion, guid );
1577 if (FAILED(hr)) goto failed;
1578 if (!params) return DI_OK;
1580 flags = params->dwSize == sizeof(DIEFFECT_DX6) ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
1581 if (!is_exclusively_acquired( impl )) flags |= DIEP_NODOWNLOAD;
1582 hr = IDirectInputEffect_SetParameters( *out, params, flags );
1583 if (FAILED(hr)) goto failed;
1584 return DI_OK;
1586 failed:
1587 IDirectInputEffect_Release( *out );
1588 *out = NULL;
1589 return hr;
1592 static HRESULT WINAPI dinput_device_EnumEffects( IDirectInputDevice8W *iface, LPDIENUMEFFECTSCALLBACKW callback,
1593 void *context, DWORD type )
1595 DIEFFECTINFOW info = {.dwSize = sizeof(info)};
1596 HRESULT hr;
1598 TRACE( "iface %p, callback %p, context %p, type %#lx.\n", iface, callback, context, type );
1600 if (!callback) return DIERR_INVALIDPARAM;
1602 type = DIEFT_GETTYPE( type );
1604 if (type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1606 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_ConstantForce );
1607 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1608 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1611 if (type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1613 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_RampForce );
1614 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1615 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1618 if (type == DIEFT_ALL || type == DIEFT_PERIODIC)
1620 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Square );
1621 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1622 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1624 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Sine );
1625 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1626 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1628 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Triangle );
1629 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1630 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1632 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_SawtoothUp );
1633 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1634 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1636 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_SawtoothDown );
1637 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1638 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1641 if (type == DIEFT_ALL || type == DIEFT_CONDITION)
1643 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Spring );
1644 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1645 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1647 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Damper );
1648 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1649 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1651 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Inertia );
1652 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1653 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1655 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Friction );
1656 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1657 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1660 return DI_OK;
1663 static HRESULT WINAPI dinput_device_GetEffectInfo( IDirectInputDevice8W *iface, DIEFFECTINFOW *info,
1664 const GUID *guid )
1666 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1668 TRACE( "iface %p, info %p, guid %s.\n", iface, info, debugstr_guid( guid ) );
1670 if (!info) return E_POINTER;
1671 if (info->dwSize != sizeof(DIEFFECTINFOW)) return DIERR_INVALIDPARAM;
1672 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_DEVICENOTREG;
1673 if (!impl->vtbl->get_effect_info) return DIERR_UNSUPPORTED;
1674 return impl->vtbl->get_effect_info( iface, info, guid );
1677 static HRESULT WINAPI dinput_device_GetForceFeedbackState( IDirectInputDevice8W *iface, DWORD *out )
1679 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1680 HRESULT hr = DI_OK;
1682 TRACE( "iface %p, out %p.\n", iface, out );
1684 if (!out) return E_POINTER;
1685 *out = 0;
1687 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1689 EnterCriticalSection( &impl->crit );
1690 if (!is_exclusively_acquired( impl )) hr = DIERR_NOTEXCLUSIVEACQUIRED;
1691 else *out = impl->force_feedback_state;
1692 LeaveCriticalSection( &impl->crit );
1694 return hr;
1697 static HRESULT WINAPI dinput_device_SendForceFeedbackCommand( IDirectInputDevice8W *iface, DWORD command )
1699 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1700 HRESULT hr;
1702 TRACE( "iface %p, command %#lx.\n", iface, command );
1704 switch (command)
1706 case DISFFC_RESET: break;
1707 case DISFFC_STOPALL: break;
1708 case DISFFC_PAUSE: break;
1709 case DISFFC_CONTINUE: break;
1710 case DISFFC_SETACTUATORSON: break;
1711 case DISFFC_SETACTUATORSOFF: break;
1712 default: return DIERR_INVALIDPARAM;
1715 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1716 if (!impl->vtbl->send_force_feedback_command) return DIERR_UNSUPPORTED;
1718 EnterCriticalSection( &impl->crit );
1719 if (!is_exclusively_acquired( impl )) hr = DIERR_NOTEXCLUSIVEACQUIRED;
1720 else hr = impl->vtbl->send_force_feedback_command( iface, command, FALSE );
1721 LeaveCriticalSection( &impl->crit );
1723 return hr;
1726 static HRESULT WINAPI dinput_device_EnumCreatedEffectObjects( IDirectInputDevice8W *iface,
1727 LPDIENUMCREATEDEFFECTOBJECTSCALLBACK callback,
1728 void *context, DWORD flags )
1730 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1732 TRACE( "iface %p, callback %p, context %p, flags %#lx.\n", iface, callback, context, flags );
1734 if (!callback) return DIERR_INVALIDPARAM;
1735 if (flags) return DIERR_INVALIDPARAM;
1736 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DI_OK;
1737 if (!impl->vtbl->enum_created_effect_objects) return DIERR_UNSUPPORTED;
1739 return impl->vtbl->enum_created_effect_objects( iface, callback, context, flags );
1742 static HRESULT WINAPI dinput_device_Escape( IDirectInputDevice8W *iface, DIEFFESCAPE *escape )
1744 FIXME( "iface %p, escape %p stub!\n", iface, escape );
1745 return DI_OK;
1748 static HRESULT WINAPI dinput_device_Poll( IDirectInputDevice8W *iface )
1750 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1751 HRESULT hr = DI_NOEFFECT;
1753 EnterCriticalSection( &impl->crit );
1754 if (impl->status == STATUS_UNPLUGGED) hr = DIERR_INPUTLOST;
1755 else if (impl->status != STATUS_ACQUIRED) hr = DIERR_NOTACQUIRED;
1756 LeaveCriticalSection( &impl->crit );
1757 if (FAILED(hr)) return hr;
1759 if (impl->vtbl->poll) return impl->vtbl->poll( iface );
1760 return hr;
1763 static HRESULT WINAPI dinput_device_SendDeviceData( IDirectInputDevice8W *iface, DWORD size,
1764 const DIDEVICEOBJECTDATA *data, DWORD *count, DWORD flags )
1766 FIXME( "iface %p, size %lu, data %p, count %p, flags %#lx stub!\n", iface, size, data, count, flags );
1767 return DI_OK;
1770 static HRESULT WINAPI dinput_device_EnumEffectsInFile( IDirectInputDevice8W *iface, const WCHAR *filename,
1771 LPDIENUMEFFECTSINFILECALLBACK callback,
1772 void *context, DWORD flags )
1774 FIXME( "iface %p, filename %s, callback %p, context %p, flags %#lx stub!\n", iface,
1775 debugstr_w(filename), callback, context, flags );
1776 return DI_OK;
1779 static HRESULT WINAPI dinput_device_WriteEffectToFile( IDirectInputDevice8W *iface, const WCHAR *filename,
1780 DWORD count, DIFILEEFFECT *effects, DWORD flags )
1782 FIXME( "iface %p, filename %s, count %lu, effects %p, flags %#lx stub!\n", iface,
1783 debugstr_w(filename), count, effects, flags );
1784 return DI_OK;
1787 BOOL device_object_matches_semantic( const DIDEVICEINSTANCEW *instance, const DIOBJECTDATAFORMAT *object,
1788 DWORD semantic, BOOL exact )
1790 DWORD value = semantic & 0xff, axis = (semantic >> 15) & 3, type;
1792 switch (semantic & 0x700)
1794 case 0x200: type = DIDFT_ABSAXIS; break;
1795 case 0x300: type = DIDFT_RELAXIS; break;
1796 case 0x400: type = DIDFT_BUTTON; break;
1797 case 0x600: type = DIDFT_POV; break;
1798 default: return FALSE;
1801 if (!(DIDFT_GETTYPE( object->dwType ) & type)) return FALSE;
1802 if ((semantic & 0xf0000000) == 0x80000000)
1804 switch (semantic & 0x0f000000)
1806 case 0x01000000: return (instance->dwDevType & 0xf) == DIDEVTYPE_KEYBOARD && object->dwOfs == value;
1807 case 0x02000000: return (instance->dwDevType & 0xf) == DIDEVTYPE_MOUSE && object->dwOfs == value;
1808 default: return FALSE;
1811 if (axis && (axis - 1) != DIDFT_GETINSTANCE( object->dwType )) return FALSE;
1812 return !exact || !value || value == DIDFT_GETINSTANCE( object->dwType ) + 1;
1815 static HRESULT WINAPI dinput_device_BuildActionMap( IDirectInputDevice8W *iface, DIACTIONFORMATW *format,
1816 const WCHAR *username, DWORD flags )
1818 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1819 DIOBJECTDATAFORMAT *object, *object_end;
1820 DIACTIONW *action, *action_end;
1821 DWORD i, username_len = MAX_PATH;
1822 WCHAR username_buf[MAX_PATH];
1823 BOOL *mapped;
1825 TRACE( "iface %p, format %p, username %s, flags %#lx\n", iface, format,
1826 debugstr_w(username), flags );
1828 if (!format) return DIERR_INVALIDPARAM;
1829 if (flags != DIDBAM_DEFAULT && flags != DIDBAM_PRESERVE &&
1830 flags != DIDBAM_INITIALIZE && flags != DIDBAM_HWDEFAULTS)
1831 return DIERR_INVALIDPARAM;
1832 if (format->dwNumActions * 4 != format->dwDataSize)
1833 return DIERR_INVALIDPARAM;
1835 TRACE( "format guid %s, genre %#lx, name %s\n", debugstr_guid(&format->guidActionMap),
1836 format->dwGenre, debugstr_w(format->tszActionMap) );
1837 for (i = 0; i < format->dwNumActions; i++)
1839 DIACTIONW *action = format->rgoAction + i;
1840 TRACE( " %lu: app_data %#Ix, semantic %#lx, flags %#lx, instance %s, obj_id %#lx, how %#lx, name %s\n",
1841 i, action->uAppData, action->dwSemantic, action->dwFlags, debugstr_guid(&action->guidInstance),
1842 action->dwObjID, action->dwHow, debugstr_w(action->lptszActionName) );
1845 action_end = format->rgoAction + format->dwNumActions;
1846 for (action = format->rgoAction; action < action_end; action++)
1848 if (!action->dwSemantic) return DIERR_INVALIDPARAM;
1849 if (flags == DIDBAM_PRESERVE && !IsEqualCLSID( &action->guidInstance, &GUID_NULL ) &&
1850 !IsEqualCLSID( &action->guidInstance, &impl->guid )) continue;
1851 if (action->dwFlags & DIA_APPMAPPED) action->dwHow = DIAH_APPREQUESTED;
1852 else action->dwHow = 0;
1855 /* Unless asked the contrary by these flags, try to load a previous mapping */
1856 if (!(flags & DIDBAM_HWDEFAULTS))
1858 /* Retrieve logged user name if necessary */
1859 if (username == NULL) GetUserNameW( username_buf, &username_len );
1860 else lstrcpynW( username_buf, username, MAX_PATH );
1861 load_mapping_settings( impl, format, username_buf );
1864 action_end = format->rgoAction + format->dwNumActions;
1865 for (action = format->rgoAction; action < action_end; action++)
1867 if (action->dwHow == DIAH_APPREQUESTED || action->dwHow == DIAH_USERCONFIG) continue;
1868 if (flags == DIDBAM_PRESERVE && !IsEqualCLSID( &action->guidInstance, &GUID_NULL ) &&
1869 !IsEqualCLSID( &action->guidInstance, &impl->guid )) continue;
1870 if (action->dwFlags & DIA_APPNOMAP) continue;
1871 action->guidInstance = GUID_NULL;
1872 action->dwHow = 0;
1875 if (!(mapped = calloc( impl->device_format.dwNumObjs, sizeof(*mapped) ))) return DIERR_OUTOFMEMORY;
1877 action_end = format->rgoAction + format->dwNumActions;
1878 for (action = format->rgoAction; action < action_end; action++)
1880 if (action->dwHow || (action->dwFlags & DIA_APPNOMAP)) continue; /* already mapped */
1881 if (action->dwSemantic & 0x4000) continue; /* priority 1 */
1883 object_end = impl->device_format.rgodf + impl->device_format.dwNumObjs;
1884 for (object = impl->device_format.rgodf; object < object_end; object++)
1886 if (mapped[object - impl->device_format.rgodf]) continue;
1887 if (!device_object_matches_semantic( &impl->instance, object, action->dwSemantic, TRUE )) continue;
1888 if ((action->dwFlags & DIA_FORCEFEEDBACK) && !(object->dwType & DIDFT_FFACTUATOR)) continue;
1889 action->dwObjID = object->dwType;
1890 action->guidInstance = impl->guid;
1891 action->dwHow = DIAH_DEFAULT;
1892 mapped[object - impl->device_format.rgodf] = TRUE;
1893 break;
1897 for (action = format->rgoAction; action < action_end; action++)
1899 if (action->dwHow || (action->dwFlags & DIA_APPNOMAP)) continue; /* already mapped */
1900 if (!(action->dwSemantic & 0x4000)) continue; /* priority 2 */
1902 object_end = impl->device_format.rgodf + impl->device_format.dwNumObjs;
1903 for (object = impl->device_format.rgodf; object < object_end; object++)
1905 if (mapped[object - impl->device_format.rgodf]) continue;
1906 if (!device_object_matches_semantic( &impl->instance, object, action->dwSemantic, FALSE )) continue;
1907 if ((action->dwFlags & DIA_FORCEFEEDBACK) && !(object->dwType & DIDFT_FFACTUATOR)) continue;
1908 action->dwObjID = object->dwType;
1909 action->guidInstance = impl->guid;
1910 action->dwHow = DIAH_DEFAULT;
1911 mapped[object - impl->device_format.rgodf] = TRUE;
1912 break;
1916 for (i = 0; i < impl->device_format.dwNumObjs; ++i) if (mapped[i]) break;
1917 free( mapped );
1919 if (i == impl->device_format.dwNumObjs) return DI_NOEFFECT;
1920 return DI_OK;
1923 static BOOL init_object_app_data( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1924 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1926 struct object_properties *properties;
1927 const DIACTIONFORMATW *format = data;
1928 const DIACTIONW *action = format->rgoAction + format->dwNumActions;
1930 if (index == -1) return DIENUM_STOP;
1931 if (instance->wUsagePage == HID_USAGE_PAGE_PID) return DIENUM_CONTINUE;
1933 properties = device->object_properties + index;
1934 properties->app_data = 0;
1936 while (action-- > format->rgoAction)
1938 if (action->dwObjID != instance->dwType) continue;
1939 properties->app_data = action->uAppData;
1940 break;
1943 return DIENUM_CONTINUE;
1946 static HRESULT WINAPI dinput_device_SetActionMap( IDirectInputDevice8W *iface, DIACTIONFORMATW *format,
1947 const WCHAR *username, DWORD flags )
1949 static const DIPROPHEADER filter =
1951 .dwSize = sizeof(filter),
1952 .dwHeaderSize = sizeof(filter),
1953 .dwHow = DIPH_DEVICE,
1955 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1956 DIDATAFORMAT data_format =
1958 .dwSize = sizeof(DIDATAFORMAT),
1959 .dwObjSize = sizeof(DIOBJECTDATAFORMAT),
1960 .dwFlags = DIDF_RELAXIS,
1962 DIPROPDWORD prop_buffer =
1964 .diph =
1966 .dwHeaderSize = sizeof(DIPROPHEADER),
1967 .dwSize = sizeof(DIPROPDWORD),
1968 .dwHow = DIPH_DEVICE,
1971 DIPROPRANGE prop_range =
1973 .diph =
1975 .dwHeaderSize = sizeof(DIPROPHEADER),
1976 .dwSize = sizeof(DIPROPRANGE),
1977 .dwHow = DIPH_DEVICE,
1980 DIPROPSTRING prop_username =
1982 .diph =
1984 .dwHeaderSize = sizeof(DIPROPHEADER),
1985 .dwSize = sizeof(DIPROPSTRING),
1986 .dwHow = DIPH_DEVICE,
1989 WCHAR username_buf[MAX_PATH];
1990 DWORD username_len = MAX_PATH;
1991 unsigned int offset = 0;
1992 int i, index;
1993 HRESULT hr;
1995 TRACE( "iface %p, format %p, username %s, flags %#lx\n", iface, format,
1996 debugstr_w(username), flags );
1998 if (!format) return DIERR_INVALIDPARAM;
1999 if (flags != DIDSAM_DEFAULT && flags != DIDSAM_FORCESAVE && flags != DIDSAM_NOUSER) return DIERR_INVALIDPARAM;
2001 TRACE( "format guid %s, genre %#lx, name %s\n", debugstr_guid(&format->guidActionMap),
2002 format->dwGenre, debugstr_w(format->tszActionMap) );
2003 for (i = 0; i < format->dwNumActions; i++)
2005 DIACTIONW *action = format->rgoAction + i;
2006 TRACE( " %u: app_data %#Ix, semantic %#lx, flags %#lx, instance %s, obj_id %#lx, how %#lx, name %s\n",
2007 i, action->uAppData, action->dwSemantic, action->dwFlags, debugstr_guid(&action->guidInstance),
2008 action->dwObjID, action->dwHow, debugstr_w(action->lptszActionName) );
2011 if (!(data_format.rgodf = malloc( sizeof(DIOBJECTDATAFORMAT) * format->dwNumActions ))) return DIERR_OUTOFMEMORY;
2012 data_format.dwDataSize = format->dwDataSize;
2014 for (i = 0; i < format->dwNumActions; i++, offset += sizeof(ULONG))
2016 if (format->rgoAction[i].dwFlags & DIA_APPNOMAP) continue;
2017 if (!IsEqualGUID( &impl->guid, &format->rgoAction[i].guidInstance )) continue;
2018 if ((index = dinput_device_object_index_from_id( iface, format->rgoAction[i].dwObjID )) < 0) continue;
2020 data_format.rgodf[data_format.dwNumObjs] = impl->device_format.rgodf[index];
2021 data_format.rgodf[data_format.dwNumObjs].dwOfs = offset;
2022 data_format.dwNumObjs++;
2025 EnterCriticalSection( &impl->crit );
2027 if (FAILED(hr = IDirectInputDevice8_SetDataFormat( iface, &data_format )))
2028 WARN( "Failed to set data format from action map, hr %#lx\n", hr );
2029 else
2031 if (FAILED(impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, init_object_app_data, format )))
2032 WARN( "Failed to initialize action map app data\n" );
2034 if (format->lAxisMin != format->lAxisMax)
2036 prop_range.lMin = format->lAxisMin;
2037 prop_range.lMax = format->lAxisMax;
2038 IDirectInputDevice8_SetProperty( iface, DIPROP_RANGE, &prop_range.diph );
2041 prop_buffer.dwData = format->dwBufferSize;
2042 IDirectInputDevice8_SetProperty( iface, DIPROP_BUFFERSIZE, &prop_buffer.diph );
2044 if (username == NULL) GetUserNameW( username_buf, &username_len );
2045 else lstrcpynW( username_buf, username, MAX_PATH );
2047 if (flags & DIDSAM_NOUSER) prop_username.wsz[0] = '\0';
2048 else lstrcpynW( prop_username.wsz, username_buf, ARRAY_SIZE(prop_username.wsz) );
2049 dinput_device_set_username( impl, &prop_username );
2051 save_mapping_settings( iface, format, username_buf );
2054 LeaveCriticalSection( &impl->crit );
2056 free( data_format.rgodf );
2058 if (FAILED(hr)) return hr;
2059 if (flags == DIDSAM_FORCESAVE) return DI_SETTINGSNOTSAVED;
2060 if (!data_format.dwNumObjs) return DI_NOEFFECT;
2061 return hr;
2064 static HRESULT WINAPI dinput_device_GetImageInfo( IDirectInputDevice8W *iface, DIDEVICEIMAGEINFOHEADERW *header )
2066 FIXME( "iface %p, header %p stub!\n", iface, header );
2067 return DI_OK;
2070 extern const IDirectInputDevice8AVtbl dinput_device_a_vtbl;
2071 static const IDirectInputDevice8WVtbl dinput_device_w_vtbl =
2073 /*** IUnknown methods ***/
2074 dinput_device_QueryInterface,
2075 dinput_device_AddRef,
2076 dinput_device_Release,
2077 /*** IDirectInputDevice methods ***/
2078 dinput_device_GetCapabilities,
2079 dinput_device_EnumObjects,
2080 dinput_device_GetProperty,
2081 dinput_device_SetProperty,
2082 dinput_device_Acquire,
2083 dinput_device_Unacquire,
2084 dinput_device_GetDeviceState,
2085 dinput_device_GetDeviceData,
2086 dinput_device_SetDataFormat,
2087 dinput_device_SetEventNotification,
2088 dinput_device_SetCooperativeLevel,
2089 dinput_device_GetObjectInfo,
2090 dinput_device_GetDeviceInfo,
2091 dinput_device_RunControlPanel,
2092 dinput_device_Initialize,
2093 /*** IDirectInputDevice2 methods ***/
2094 dinput_device_CreateEffect,
2095 dinput_device_EnumEffects,
2096 dinput_device_GetEffectInfo,
2097 dinput_device_GetForceFeedbackState,
2098 dinput_device_SendForceFeedbackCommand,
2099 dinput_device_EnumCreatedEffectObjects,
2100 dinput_device_Escape,
2101 dinput_device_Poll,
2102 dinput_device_SendDeviceData,
2103 /*** IDirectInputDevice7 methods ***/
2104 dinput_device_EnumEffectsInFile,
2105 dinput_device_WriteEffectToFile,
2106 /*** IDirectInputDevice8 methods ***/
2107 dinput_device_BuildActionMap,
2108 dinput_device_SetActionMap,
2109 dinput_device_GetImageInfo,
2112 void dinput_device_init( struct dinput_device *device, const struct dinput_device_vtbl *vtbl,
2113 const GUID *guid, struct dinput *dinput )
2115 device->IDirectInputDevice8A_iface.lpVtbl = &dinput_device_a_vtbl;
2116 device->IDirectInputDevice8W_iface.lpVtbl = &dinput_device_w_vtbl;
2117 device->internal_ref = 1;
2118 device->ref = 1;
2119 device->guid = *guid;
2120 device->instance.dwSize = sizeof(DIDEVICEINSTANCEW);
2121 device->caps.dwSize = sizeof(DIDEVCAPS);
2122 device->caps.dwFlags = DIDC_ATTACHED | DIDC_EMULATED;
2123 device->device_gain = 10000;
2124 device->force_feedback_state = DIGFFS_STOPPED | DIGFFS_EMPTY;
2125 InitializeCriticalSection( &device->crit );
2126 dinput_internal_addref( (device->dinput = dinput) );
2127 device->vtbl = vtbl;
2129 input_thread_add_user();
2132 static const GUID *object_instance_guid( const DIDEVICEOBJECTINSTANCEW *instance )
2134 if (IsEqualGUID( &instance->guidType, &GUID_XAxis )) return &GUID_XAxis;
2135 if (IsEqualGUID( &instance->guidType, &GUID_YAxis )) return &GUID_YAxis;
2136 if (IsEqualGUID( &instance->guidType, &GUID_ZAxis )) return &GUID_ZAxis;
2137 if (IsEqualGUID( &instance->guidType, &GUID_RxAxis )) return &GUID_RxAxis;
2138 if (IsEqualGUID( &instance->guidType, &GUID_RyAxis )) return &GUID_RyAxis;
2139 if (IsEqualGUID( &instance->guidType, &GUID_RzAxis )) return &GUID_RzAxis;
2140 if (IsEqualGUID( &instance->guidType, &GUID_Slider )) return &GUID_Slider;
2141 if (IsEqualGUID( &instance->guidType, &GUID_Button )) return &GUID_Button;
2142 if (IsEqualGUID( &instance->guidType, &GUID_Key )) return &GUID_Key;
2143 if (IsEqualGUID( &instance->guidType, &GUID_POV )) return &GUID_POV;
2144 return &GUID_Unknown;
2147 static BOOL enum_objects_count( struct dinput_device *impl, UINT index, struct hid_value_caps *caps,
2148 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
2150 DIDATAFORMAT *format = &impl->device_format;
2152 if (index == -1) return DIENUM_STOP;
2153 format->dwNumObjs++;
2154 if (instance->wUsagePage == HID_USAGE_PAGE_PID) return DIENUM_CONTINUE;
2156 format->dwDataSize = max( format->dwDataSize, instance->dwOfs + sizeof(LONG) );
2157 if (instance->dwType & DIDFT_BUTTON) impl->caps.dwButtons++;
2158 if (instance->dwType & DIDFT_AXIS) impl->caps.dwAxes++;
2159 if (instance->dwType & DIDFT_POV) impl->caps.dwPOVs++;
2160 if (instance->dwType & (DIDFT_BUTTON|DIDFT_AXIS|DIDFT_POV))
2162 if (!impl->device_state_report_id)
2163 impl->device_state_report_id = instance->wReportId;
2164 else if (impl->device_state_report_id != instance->wReportId)
2165 FIXME( "multiple device state reports found!\n" );
2168 return DIENUM_CONTINUE;
2171 static BOOL enum_objects_init( struct dinput_device *impl, UINT index, struct hid_value_caps *caps,
2172 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
2174 static const struct object_properties default_properties =
2176 .range_min = DIPROPRANGE_NOMIN,
2177 .range_max = DIPROPRANGE_NOMAX,
2178 .granularity = 1,
2179 .app_data = -1,
2181 DIDATAFORMAT *format = &impl->device_format;
2182 DIOBJECTDATAFORMAT *object_format;
2184 if (index == -1) return DIENUM_STOP;
2185 if (instance->wUsagePage == HID_USAGE_PAGE_PID) return DIENUM_CONTINUE;
2187 object_format = format->rgodf + index;
2188 object_format->pguid = object_instance_guid( instance );
2189 object_format->dwOfs = instance->dwOfs;
2190 object_format->dwType = instance->dwType;
2191 object_format->dwFlags = instance->dwFlags;
2193 impl->object_properties[index] = default_properties;
2194 if (instance->dwType & (DIDFT_AXIS | DIDFT_POV)) reset_object_value( impl, index, caps, instance, NULL );
2196 return DIENUM_CONTINUE;
2199 HRESULT dinput_device_init_device_format( IDirectInputDevice8W *iface )
2201 static const DIPROPHEADER filter =
2203 .dwSize = sizeof(filter),
2204 .dwHeaderSize = sizeof(filter),
2205 .dwHow = DIPH_DEVICE,
2207 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
2208 DIDATAFORMAT *format = &impl->device_format;
2209 HRESULT hr;
2210 ULONG i;
2212 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, enum_objects_count, NULL );
2213 if (FAILED(hr)) return hr;
2215 if (format->dwDataSize > DEVICE_STATE_MAX_SIZE)
2217 FIXME( "unable to create device, state is too large\n" );
2218 return DIERR_OUTOFMEMORY;
2221 if (!(impl->object_properties = calloc( format->dwNumObjs, sizeof(*impl->object_properties) ))) return DIERR_OUTOFMEMORY;
2222 if (!(format->rgodf = calloc( format->dwNumObjs, sizeof(*format->rgodf) ))) return DIERR_OUTOFMEMORY;
2224 format->dwSize = sizeof(*format);
2225 format->dwObjSize = sizeof(*format->rgodf);
2226 format->dwFlags = DIDF_ABSAXIS;
2228 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_AXIS, enum_objects_init, NULL );
2229 if (FAILED(hr)) return hr;
2230 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_POV, enum_objects_init, NULL );
2231 if (FAILED(hr)) return hr;
2232 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_BUTTON, enum_objects_init, NULL );
2233 if (FAILED(hr)) return hr;
2234 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_NODATA, enum_objects_init, NULL );
2235 if (FAILED(hr)) return hr;
2237 if (TRACE_ON( dinput ))
2239 TRACE( "device format %s\n", debugstr_didataformat( format ) );
2240 for (i = 0; i < format->dwNumObjs; ++i) TRACE( " %lu: object %s\n", i, debugstr_diobjectdataformat( format->rgodf + i ) );
2243 return DI_OK;