mshtml: Implement MediaQueryList's addListener method.
[wine.git] / dlls / dinput / device.c
blob4db8bd943cb7565318246f2c3948d9444b7b5637
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)
328 static const WCHAR *subkey = L"Software\\Wine\\DirectInput\\Mappings\\%s\\%s\\%s";
329 HKEY hkey;
330 WCHAR *keyname;
332 SIZE_T len = wcslen( subkey ) + wcslen( username ) + wcslen( device ) + wcslen( guid ) + 1;
333 keyname = malloc( sizeof(WCHAR) * len );
334 swprintf( keyname, len, subkey, username, device, guid );
336 /* The key used is HKCU\Software\Wine\DirectInput\Mappings\[username]\[device]\[mapping_guid] */
337 if (RegCreateKeyW(HKEY_CURRENT_USER, keyname, &hkey))
338 hkey = 0;
340 free( keyname );
342 return hkey;
345 static HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername)
347 WCHAR *guid_str = NULL;
348 DIDEVICEINSTANCEW didev;
349 HKEY hkey;
350 int i;
352 didev.dwSize = sizeof(didev);
353 IDirectInputDevice8_GetDeviceInfo(iface, &didev);
355 if (StringFromCLSID(&lpdiaf->guidActionMap, &guid_str) != S_OK)
356 return DI_SETTINGSNOTSAVED;
358 hkey = get_mapping_key(didev.tszInstanceName, lpszUsername, guid_str);
360 if (!hkey)
362 CoTaskMemFree(guid_str);
363 return DI_SETTINGSNOTSAVED;
366 /* Write each of the actions mapped for this device.
367 Format is "dwSemantic"="dwObjID" and key is of type REG_DWORD
369 for (i = 0; i < lpdiaf->dwNumActions; i++)
371 WCHAR label[9];
373 if (IsEqualGUID(&didev.guidInstance, &lpdiaf->rgoAction[i].guidInstance) &&
374 lpdiaf->rgoAction[i].dwHow != DIAH_UNMAPPED)
376 swprintf( label, 9, L"%x", lpdiaf->rgoAction[i].dwSemantic );
377 RegSetValueExW( hkey, label, 0, REG_DWORD, (const BYTE *)&lpdiaf->rgoAction[i].dwObjID,
378 sizeof(DWORD) );
382 RegCloseKey(hkey);
383 CoTaskMemFree(guid_str);
385 return DI_OK;
388 static BOOL load_mapping_settings( struct dinput_device *This, LPDIACTIONFORMATW lpdiaf, const WCHAR *username )
390 HKEY hkey;
391 WCHAR *guid_str;
392 DIDEVICEINSTANCEW didev;
393 int i, mapped = 0;
395 didev.dwSize = sizeof(didev);
396 IDirectInputDevice8_GetDeviceInfo(&This->IDirectInputDevice8W_iface, &didev);
398 if (StringFromCLSID(&lpdiaf->guidActionMap, &guid_str) != S_OK)
399 return FALSE;
401 hkey = get_mapping_key(didev.tszInstanceName, username, guid_str);
403 if (!hkey)
405 CoTaskMemFree(guid_str);
406 return FALSE;
409 /* Try to read each action in the DIACTIONFORMAT from registry */
410 for (i = 0; i < lpdiaf->dwNumActions; i++)
412 DWORD id, size = sizeof(DWORD);
413 WCHAR label[9];
415 swprintf( label, 9, L"%x", lpdiaf->rgoAction[i].dwSemantic );
417 if (!RegQueryValueExW(hkey, label, 0, NULL, (LPBYTE) &id, &size))
419 lpdiaf->rgoAction[i].dwObjID = id;
420 lpdiaf->rgoAction[i].guidInstance = didev.guidInstance;
421 lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT;
422 mapped += 1;
426 RegCloseKey(hkey);
427 CoTaskMemFree(guid_str);
429 return mapped > 0;
432 void queue_event( IDirectInputDevice8W *iface, int index, DWORD data, DWORD time, DWORD seq )
434 static ULONGLONG notify_ms = 0;
435 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
436 struct object_properties *properties = This->object_properties + index;
437 const DIOBJECTDATAFORMAT *user_obj = This->user_format.rgodf + index;
438 ULONGLONG time_ms = GetTickCount64();
439 int next_pos;
441 if (time_ms - notify_ms > 1000)
443 PostMessageW(GetDesktopWindow(), WM_WINE_NOTIFY_ACTIVITY, 0, 0);
444 notify_ms = time_ms;
447 if (!This->queue_len || This->overflow || !user_obj->dwType) return;
449 next_pos = (This->queue_head + 1) % This->queue_len;
450 if (next_pos == This->queue_tail)
452 TRACE(" queue overflowed\n");
453 This->overflow = TRUE;
454 return;
457 TRACE( " queueing %lu at offset %lu (queue head %u / size %u)\n", data, user_obj->dwOfs, This->queue_head, This->queue_len );
459 This->data_queue[This->queue_head].dwOfs = user_obj->dwOfs;
460 This->data_queue[This->queue_head].dwData = data;
461 This->data_queue[This->queue_head].dwTimeStamp = time;
462 This->data_queue[This->queue_head].dwSequence = seq;
463 This->data_queue[This->queue_head].uAppData = properties->app_data;
465 This->queue_head = next_pos;
466 /* Send event if asked */
469 static HRESULT WINAPI dinput_device_Acquire( IDirectInputDevice8W *iface )
471 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
472 HRESULT hr = DI_OK;
473 DWORD pid;
475 TRACE( "iface %p.\n", iface );
477 EnterCriticalSection( &impl->crit );
478 if (impl->status == STATUS_ACQUIRED)
479 hr = DI_NOEFFECT;
480 else if (!impl->user_format.rgodf)
481 hr = DIERR_INVALIDPARAM;
482 else if ((impl->dwCoopLevel & DISCL_FOREGROUND) && impl->win != GetForegroundWindow())
483 hr = DIERR_OTHERAPPHASPRIO;
484 else if ((impl->dwCoopLevel & DISCL_FOREGROUND) && (!GetWindowThreadProcessId( impl->win, &pid ) || pid != GetCurrentProcessId()))
485 hr = DIERR_INVALIDPARAM;
486 else
488 impl->status = STATUS_ACQUIRED;
489 if (FAILED(hr = impl->vtbl->acquire( iface ))) impl->status = STATUS_UNACQUIRED;
491 LeaveCriticalSection( &impl->crit );
492 if (hr != DI_OK) return hr;
494 dinput_hooks_acquire_device( iface );
496 return hr;
499 static HRESULT WINAPI dinput_device_Unacquire( IDirectInputDevice8W *iface )
501 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
502 HRESULT hr = DI_OK;
504 TRACE( "iface %p.\n", iface );
506 EnterCriticalSection( &impl->crit );
507 if (impl->status != STATUS_ACQUIRED) hr = DI_NOEFFECT;
508 else hr = impl->vtbl->unacquire( iface );
509 impl->status = STATUS_UNACQUIRED;
510 LeaveCriticalSection( &impl->crit );
511 if (hr != DI_OK) return hr;
513 dinput_hooks_unacquire_device( iface );
515 return hr;
518 static HRESULT WINAPI dinput_device_SetDataFormat( IDirectInputDevice8W *iface, const DIDATAFORMAT *format )
520 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
521 HRESULT res = DI_OK;
522 ULONG i;
524 TRACE( "iface %p, format %p.\n", iface, format );
526 if (!format) return E_POINTER;
527 if (TRACE_ON( dinput ))
529 TRACE( "user format %s\n", debugstr_didataformat( format ) );
530 for (i = 0; i < format->dwNumObjs; ++i) TRACE( " %lu: object %s\n", i, debugstr_diobjectdataformat( format->rgodf + i ) );
533 if (format->dwSize != sizeof(DIDATAFORMAT)) return DIERR_INVALIDPARAM;
534 if (format->dwObjSize != sizeof(DIOBJECTDATAFORMAT)) return DIERR_INVALIDPARAM;
535 if (This->status == STATUS_ACQUIRED) return DIERR_ACQUIRED;
537 EnterCriticalSection(&This->crit);
539 dinput_device_release_user_format( This );
540 res = dinput_device_init_user_format( This, format );
542 LeaveCriticalSection(&This->crit);
543 return res;
546 static HRESULT WINAPI dinput_device_SetCooperativeLevel( IDirectInputDevice8W *iface, HWND hwnd, DWORD flags )
548 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
549 HRESULT hr;
551 TRACE( "iface %p, hwnd %p, flags %#lx.\n", iface, hwnd, flags );
553 _dump_cooperativelevel_DI( flags );
555 if ((flags & (DISCL_EXCLUSIVE | DISCL_NONEXCLUSIVE)) == 0 ||
556 (flags & (DISCL_EXCLUSIVE | DISCL_NONEXCLUSIVE)) == (DISCL_EXCLUSIVE | DISCL_NONEXCLUSIVE) ||
557 (flags & (DISCL_FOREGROUND | DISCL_BACKGROUND)) == 0 ||
558 (flags & (DISCL_FOREGROUND | DISCL_BACKGROUND)) == (DISCL_FOREGROUND | DISCL_BACKGROUND))
559 return DIERR_INVALIDPARAM;
561 if (hwnd && GetWindowLongW(hwnd, GWL_STYLE) & WS_CHILD) return E_HANDLE;
563 if (!hwnd && flags == (DISCL_NONEXCLUSIVE | DISCL_BACKGROUND)) hwnd = GetDesktopWindow();
565 if (!IsWindow(hwnd)) return E_HANDLE;
567 /* For security reasons native does not allow exclusive background level
568 for mouse and keyboard only */
569 if (flags & DISCL_EXCLUSIVE && flags & DISCL_BACKGROUND &&
570 (IsEqualGUID( &This->guid, &GUID_SysMouse ) || IsEqualGUID( &This->guid, &GUID_SysKeyboard )))
571 return DIERR_UNSUPPORTED;
573 EnterCriticalSection(&This->crit);
574 if (This->status == STATUS_ACQUIRED) hr = DIERR_ACQUIRED;
575 else
577 This->win = hwnd;
578 This->dwCoopLevel = flags;
579 hr = DI_OK;
581 LeaveCriticalSection(&This->crit);
583 return hr;
586 static HRESULT WINAPI dinput_device_GetDeviceInfo( IDirectInputDevice8W *iface, DIDEVICEINSTANCEW *instance )
588 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
589 DWORD size;
591 TRACE( "iface %p, instance %p.\n", iface, instance );
593 if (!instance) return E_POINTER;
594 if (instance->dwSize != sizeof(DIDEVICEINSTANCE_DX3W) &&
595 instance->dwSize != sizeof(DIDEVICEINSTANCEW))
596 return DIERR_INVALIDPARAM;
598 size = instance->dwSize;
599 memcpy( instance, &impl->instance, size );
600 instance->dwSize = size;
602 return S_OK;
605 static HRESULT WINAPI dinput_device_SetEventNotification( IDirectInputDevice8W *iface, HANDLE event )
607 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
609 TRACE( "iface %p, event %p.\n", iface, event );
611 EnterCriticalSection(&This->crit);
612 This->hEvent = event;
613 LeaveCriticalSection(&This->crit);
614 return DI_OK;
617 void dinput_device_internal_addref( struct dinput_device *impl )
619 ULONG ref = InterlockedIncrement( &impl->internal_ref );
620 TRACE( "impl %p, internal ref %lu.\n", impl, ref );
623 void dinput_device_internal_release( struct dinput_device *impl )
625 ULONG ref = InterlockedDecrement( &impl->internal_ref );
626 TRACE( "impl %p, internal ref %lu.\n", impl, ref );
628 if (!ref)
630 if (impl->vtbl->destroy) impl->vtbl->destroy( &impl->IDirectInputDevice8W_iface );
632 free( impl->object_properties );
633 free( impl->data_queue );
635 free( impl->device_format.rgodf );
636 dinput_device_release_user_format( impl );
638 dinput_internal_release( impl->dinput );
639 impl->crit.DebugInfo->Spare[0] = 0;
640 DeleteCriticalSection( &impl->crit );
642 free( impl );
646 static ULONG WINAPI dinput_device_Release( IDirectInputDevice8W *iface )
648 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
649 ULONG ref = InterlockedDecrement( &impl->ref );
651 TRACE( "iface %p, ref %lu.\n", iface, ref );
653 if (!ref)
655 IDirectInputDevice_Unacquire( iface );
656 input_thread_remove_user();
657 dinput_device_internal_release( impl );
660 return ref;
663 static HRESULT WINAPI dinput_device_GetCapabilities( IDirectInputDevice8W *iface, DIDEVCAPS *caps )
665 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
666 DWORD size;
668 TRACE( "iface %p, caps %p.\n", iface, caps );
670 if (!caps) return E_POINTER;
671 if (caps->dwSize != sizeof(DIDEVCAPS_DX3) &&
672 caps->dwSize != sizeof(DIDEVCAPS))
673 return DIERR_INVALIDPARAM;
675 size = caps->dwSize;
676 memcpy( caps, &impl->caps, size );
677 caps->dwSize = size;
679 return DI_OK;
682 static HRESULT WINAPI dinput_device_QueryInterface( IDirectInputDevice8W *iface, const GUID *iid, void **out )
684 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
686 TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out );
688 if (IsEqualGUID( &IID_IDirectInputDeviceA, iid ) ||
689 IsEqualGUID( &IID_IDirectInputDevice2A, iid ) ||
690 IsEqualGUID( &IID_IDirectInputDevice7A, iid ) ||
691 IsEqualGUID( &IID_IDirectInputDevice8A, iid ))
693 IDirectInputDevice2_AddRef(iface);
694 *out = IDirectInputDevice8A_from_impl( This );
695 return DI_OK;
698 if (IsEqualGUID( &IID_IUnknown, iid ) ||
699 IsEqualGUID( &IID_IDirectInputDeviceW, iid ) ||
700 IsEqualGUID( &IID_IDirectInputDevice2W, iid ) ||
701 IsEqualGUID( &IID_IDirectInputDevice7W, iid ) ||
702 IsEqualGUID( &IID_IDirectInputDevice8W, iid ))
704 IDirectInputDevice2_AddRef(iface);
705 *out = IDirectInputDevice8W_from_impl( This );
706 return DI_OK;
709 WARN( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
710 return E_NOINTERFACE;
713 static ULONG WINAPI dinput_device_AddRef( IDirectInputDevice8W *iface )
715 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
716 ULONG ref = InterlockedIncrement( &impl->ref );
717 TRACE( "iface %p, ref %lu.\n", iface, ref );
718 return ref;
721 struct enum_objects_params
723 LPDIENUMDEVICEOBJECTSCALLBACKW callback;
724 void *context;
727 static BOOL enum_objects_callback( struct dinput_device *impl, UINT index, struct hid_value_caps *caps,
728 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
730 struct enum_objects_params *params = data;
731 if (instance->wUsagePage == HID_USAGE_PAGE_PID && !(instance->dwType & DIDFT_NODATA))
732 return DIENUM_CONTINUE;
734 /* Applications may return non-zero values instead of DIENUM_CONTINUE. */
735 return params->callback( instance, params->context ) ? DIENUM_CONTINUE : DIENUM_STOP;
738 static HRESULT WINAPI dinput_device_EnumObjects( IDirectInputDevice8W *iface, LPDIENUMDEVICEOBJECTSCALLBACKW callback,
739 void *context, DWORD flags )
741 static const DIPROPHEADER filter =
743 .dwSize = sizeof(filter),
744 .dwHeaderSize = sizeof(filter),
745 .dwHow = DIPH_DEVICE,
747 struct enum_objects_params params = {.callback = callback, .context = context};
748 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
749 HRESULT hr;
751 TRACE( "iface %p, callback %p, context %p, flags %#lx.\n", iface, callback, context, flags );
753 if (!callback) return DIERR_INVALIDPARAM;
754 if (flags & ~(DIDFT_AXIS | DIDFT_POV | DIDFT_BUTTON | DIDFT_NODATA | DIDFT_COLLECTION))
755 return DIERR_INVALIDPARAM;
757 if (flags == DIDFT_ALL || (flags & DIDFT_AXIS))
759 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_AXIS, enum_objects_callback, &params );
760 if (FAILED(hr)) return hr;
761 if (hr != DIENUM_CONTINUE) return DI_OK;
763 if (flags == DIDFT_ALL || (flags & DIDFT_POV))
765 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_POV, enum_objects_callback, &params );
766 if (FAILED(hr)) return hr;
767 if (hr != DIENUM_CONTINUE) return DI_OK;
769 if (flags == DIDFT_ALL || (flags & DIDFT_BUTTON))
771 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_BUTTON, enum_objects_callback, &params );
772 if (FAILED(hr)) return hr;
773 if (hr != DIENUM_CONTINUE) return DI_OK;
775 if (flags == DIDFT_ALL || (flags & (DIDFT_NODATA | DIDFT_COLLECTION)))
777 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_NODATA, enum_objects_callback, &params );
778 if (FAILED(hr)) return hr;
779 if (hr != DIENUM_CONTINUE) return DI_OK;
782 return DI_OK;
785 static HRESULT enum_object_filter_init( struct dinput_device *impl, DIPROPHEADER *filter )
787 DIOBJECTDATAFORMAT *user_objs = impl->user_format.rgodf;
788 DWORD i, count = impl->device_format.dwNumObjs;
790 if (filter->dwHow > DIPH_BYUSAGE) return DIERR_INVALIDPARAM;
791 if (filter->dwHow == DIPH_BYUSAGE && !(impl->instance.dwDevType & DIDEVTYPE_HID)) return DIERR_UNSUPPORTED;
792 if (filter->dwHow != DIPH_BYOFFSET) return DI_OK;
794 if (!user_objs) return DIERR_NOTFOUND;
796 for (i = 0; i < count; i++)
798 if (!user_objs[i].dwType) continue;
799 if (user_objs[i].dwOfs == filter->dwObj) break;
801 if (i == count) return DIERR_NOTFOUND;
803 filter->dwObj = impl->device_format.rgodf[i].dwOfs;
804 return DI_OK;
807 static HRESULT check_property( struct dinput_device *impl, const GUID *guid, const DIPROPHEADER *header, BOOL set )
809 switch (LOWORD( guid ))
811 case (DWORD_PTR)DIPROP_VIDPID:
812 case (DWORD_PTR)DIPROP_TYPENAME:
813 case (DWORD_PTR)DIPROP_USERNAME:
814 case (DWORD_PTR)DIPROP_KEYNAME:
815 case (DWORD_PTR)DIPROP_LOGICALRANGE:
816 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
817 case (DWORD_PTR)DIPROP_APPDATA:
818 if (impl->dinput->dwVersion < 0x0800) return DIERR_UNSUPPORTED;
819 break;
822 switch (LOWORD( guid ))
824 case (DWORD_PTR)DIPROP_INSTANCENAME:
825 case (DWORD_PTR)DIPROP_KEYNAME:
826 case (DWORD_PTR)DIPROP_PRODUCTNAME:
827 case (DWORD_PTR)DIPROP_TYPENAME:
828 case (DWORD_PTR)DIPROP_USERNAME:
829 if (header->dwSize != sizeof(DIPROPSTRING)) return DIERR_INVALIDPARAM;
830 break;
832 case (DWORD_PTR)DIPROP_AUTOCENTER:
833 case (DWORD_PTR)DIPROP_AXISMODE:
834 case (DWORD_PTR)DIPROP_BUFFERSIZE:
835 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
836 case (DWORD_PTR)DIPROP_DEADZONE:
837 case (DWORD_PTR)DIPROP_FFGAIN:
838 case (DWORD_PTR)DIPROP_FFLOAD:
839 case (DWORD_PTR)DIPROP_GRANULARITY:
840 case (DWORD_PTR)DIPROP_JOYSTICKID:
841 case (DWORD_PTR)DIPROP_SATURATION:
842 case (DWORD_PTR)DIPROP_SCANCODE:
843 case (DWORD_PTR)DIPROP_VIDPID:
844 if (header->dwSize != sizeof(DIPROPDWORD)) return DIERR_INVALIDPARAM;
845 break;
847 case (DWORD_PTR)DIPROP_APPDATA:
848 if (header->dwSize != sizeof(DIPROPPOINTER)) return DIERR_INVALIDPARAM;
849 break;
851 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
852 case (DWORD_PTR)DIPROP_LOGICALRANGE:
853 case (DWORD_PTR)DIPROP_RANGE:
854 if (header->dwSize != sizeof(DIPROPRANGE)) return DIERR_INVALIDPARAM;
855 break;
857 case (DWORD_PTR)DIPROP_GUIDANDPATH:
858 if (header->dwSize != sizeof(DIPROPGUIDANDPATH)) return DIERR_INVALIDPARAM;
859 break;
862 switch (LOWORD( guid ))
864 case (DWORD_PTR)DIPROP_PRODUCTNAME:
865 case (DWORD_PTR)DIPROP_INSTANCENAME:
866 case (DWORD_PTR)DIPROP_VIDPID:
867 case (DWORD_PTR)DIPROP_JOYSTICKID:
868 case (DWORD_PTR)DIPROP_GUIDANDPATH:
869 case (DWORD_PTR)DIPROP_BUFFERSIZE:
870 case (DWORD_PTR)DIPROP_FFGAIN:
871 case (DWORD_PTR)DIPROP_TYPENAME:
872 case (DWORD_PTR)DIPROP_USERNAME:
873 case (DWORD_PTR)DIPROP_AUTOCENTER:
874 case (DWORD_PTR)DIPROP_AXISMODE:
875 case (DWORD_PTR)DIPROP_FFLOAD:
876 if (header->dwHow != DIPH_DEVICE) return DIERR_UNSUPPORTED;
877 if (header->dwObj) return DIERR_INVALIDPARAM;
878 break;
880 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
881 case (DWORD_PTR)DIPROP_LOGICALRANGE:
882 case (DWORD_PTR)DIPROP_RANGE:
883 case (DWORD_PTR)DIPROP_DEADZONE:
884 case (DWORD_PTR)DIPROP_SATURATION:
885 case (DWORD_PTR)DIPROP_GRANULARITY:
886 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
887 if (header->dwHow == DIPH_DEVICE && !set) return DIERR_UNSUPPORTED;
888 break;
890 case (DWORD_PTR)DIPROP_KEYNAME:
891 if (header->dwHow == DIPH_DEVICE) return DIERR_INVALIDPARAM;
892 break;
894 case (DWORD_PTR)DIPROP_SCANCODE:
895 case (DWORD_PTR)DIPROP_APPDATA:
896 if (header->dwHow == DIPH_DEVICE) return DIERR_UNSUPPORTED;
897 break;
900 if (set)
902 switch (LOWORD( guid ))
904 case (DWORD_PTR)DIPROP_AUTOCENTER:
905 if (impl->status == STATUS_ACQUIRED && !is_exclusively_acquired( impl )) return DIERR_ACQUIRED;
906 break;
907 case (DWORD_PTR)DIPROP_AXISMODE:
908 case (DWORD_PTR)DIPROP_BUFFERSIZE:
909 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
910 case (DWORD_PTR)DIPROP_LOGICALRANGE:
911 if (impl->status == STATUS_ACQUIRED) return DIERR_ACQUIRED;
912 break;
913 case (DWORD_PTR)DIPROP_FFLOAD:
914 case (DWORD_PTR)DIPROP_GRANULARITY:
915 case (DWORD_PTR)DIPROP_VIDPID:
916 case (DWORD_PTR)DIPROP_TYPENAME:
917 case (DWORD_PTR)DIPROP_USERNAME:
918 case (DWORD_PTR)DIPROP_GUIDANDPATH:
919 return DIERR_READONLY;
922 switch (LOWORD( guid ))
924 case (DWORD_PTR)DIPROP_RANGE:
926 const DIPROPRANGE *value = (const DIPROPRANGE *)header;
927 if (value->lMin > value->lMax) return DIERR_INVALIDPARAM;
928 break;
930 case (DWORD_PTR)DIPROP_DEADZONE:
931 case (DWORD_PTR)DIPROP_SATURATION:
932 case (DWORD_PTR)DIPROP_FFGAIN:
934 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
935 if (value->dwData > 10000) return DIERR_INVALIDPARAM;
936 break;
938 case (DWORD_PTR)DIPROP_AUTOCENTER:
939 case (DWORD_PTR)DIPROP_AXISMODE:
940 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
942 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
943 if (value->dwData > 1) return DIERR_INVALIDPARAM;
944 break;
946 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
947 case (DWORD_PTR)DIPROP_LOGICALRANGE:
948 return DIERR_UNSUPPORTED;
951 else
953 switch (LOWORD( guid ))
955 case (DWORD_PTR)DIPROP_RANGE:
956 case (DWORD_PTR)DIPROP_GRANULARITY:
957 if (!impl->caps.dwAxes) return DIERR_UNSUPPORTED;
958 break;
960 case (DWORD_PTR)DIPROP_KEYNAME:
961 /* not supported on the mouse */
962 if (impl->caps.dwAxes && !(impl->caps.dwDevType & DIDEVTYPE_HID)) return DIERR_UNSUPPORTED;
963 break;
965 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
966 case (DWORD_PTR)DIPROP_LOGICALRANGE:
967 case (DWORD_PTR)DIPROP_DEADZONE:
968 case (DWORD_PTR)DIPROP_SATURATION:
969 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
970 /* not supported on the mouse or keyboard */
971 if (!(impl->caps.dwDevType & DIDEVTYPE_HID)) return DIERR_UNSUPPORTED;
972 break;
974 case (DWORD_PTR)DIPROP_FFLOAD:
975 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
976 if (!is_exclusively_acquired( impl )) return DIERR_NOTEXCLUSIVEACQUIRED;
977 /* fallthrough */
978 case (DWORD_PTR)DIPROP_PRODUCTNAME:
979 case (DWORD_PTR)DIPROP_INSTANCENAME:
980 case (DWORD_PTR)DIPROP_VIDPID:
981 case (DWORD_PTR)DIPROP_JOYSTICKID:
982 case (DWORD_PTR)DIPROP_GUIDANDPATH:
983 if (!impl->vtbl->get_property) return DIERR_UNSUPPORTED;
984 break;
988 return DI_OK;
991 struct get_object_property_params
993 IDirectInputDevice8W *iface;
994 DIPROPHEADER *header;
995 DWORD property;
998 static BOOL get_object_property( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
999 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1001 struct get_object_property_params *params = data;
1002 struct dinput_device *impl = impl_from_IDirectInputDevice8W( params->iface );
1003 const struct object_properties *properties;
1005 if (index == -1) return DIENUM_STOP;
1006 properties = impl->object_properties + index;
1008 switch (params->property)
1010 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
1012 DIPROPRANGE *value = (DIPROPRANGE *)params->header;
1013 value->lMin = properties->physical_min;
1014 value->lMax = properties->physical_max;
1015 return DI_OK;
1017 case (DWORD_PTR)DIPROP_LOGICALRANGE:
1019 DIPROPRANGE *value = (DIPROPRANGE *)params->header;
1020 value->lMin = properties->logical_min;
1021 value->lMax = properties->logical_max;
1022 return DI_OK;
1024 case (DWORD_PTR)DIPROP_RANGE:
1026 DIPROPRANGE *value = (DIPROPRANGE *)params->header;
1027 value->lMin = properties->range_min;
1028 value->lMax = properties->range_max;
1029 return DIENUM_STOP;
1031 case (DWORD_PTR)DIPROP_DEADZONE:
1033 DIPROPDWORD *value = (DIPROPDWORD *)params->header;
1034 value->dwData = properties->deadzone;
1035 return DIENUM_STOP;
1037 case (DWORD_PTR)DIPROP_SATURATION:
1039 DIPROPDWORD *value = (DIPROPDWORD *)params->header;
1040 value->dwData = properties->saturation;
1041 return DIENUM_STOP;
1043 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
1045 DIPROPDWORD *value = (DIPROPDWORD *)params->header;
1046 value->dwData = properties->calibration_mode;
1047 return DI_OK;
1049 case (DWORD_PTR)DIPROP_GRANULARITY:
1051 DIPROPDWORD *value = (DIPROPDWORD *)params->header;
1052 value->dwData = properties->granularity;
1053 return DIENUM_STOP;
1055 case (DWORD_PTR)DIPROP_KEYNAME:
1057 DIPROPSTRING *value = (DIPROPSTRING *)params->header;
1058 lstrcpynW( value->wsz, instance->tszName, ARRAY_SIZE(value->wsz) );
1059 return DIENUM_STOP;
1061 case (DWORD_PTR)DIPROP_APPDATA:
1063 DIPROPPOINTER *value = (DIPROPPOINTER *)params->header;
1064 value->uData = properties->app_data;
1065 return DIENUM_STOP;
1069 return DIENUM_STOP;
1072 static HRESULT dinput_device_get_property( IDirectInputDevice8W *iface, const GUID *guid, DIPROPHEADER *header )
1074 struct get_object_property_params params = {.iface = iface, .header = header, .property = LOWORD( guid )};
1075 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1076 DWORD object_mask = DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV;
1077 DIPROPHEADER filter;
1078 HRESULT hr;
1080 filter = *header;
1081 if (FAILED(hr = enum_object_filter_init( impl, &filter ))) return hr;
1082 if (FAILED(hr = check_property( impl, guid, header, FALSE ))) return hr;
1084 switch (LOWORD( guid ))
1086 case (DWORD_PTR)DIPROP_PRODUCTNAME:
1087 case (DWORD_PTR)DIPROP_INSTANCENAME:
1088 case (DWORD_PTR)DIPROP_VIDPID:
1089 case (DWORD_PTR)DIPROP_JOYSTICKID:
1090 case (DWORD_PTR)DIPROP_GUIDANDPATH:
1091 case (DWORD_PTR)DIPROP_FFLOAD:
1092 return impl->vtbl->get_property( iface, LOWORD( guid ), header, NULL );
1094 case (DWORD_PTR)DIPROP_RANGE:
1095 case (DWORD_PTR)DIPROP_PHYSICALRANGE:
1096 case (DWORD_PTR)DIPROP_LOGICALRANGE:
1097 case (DWORD_PTR)DIPROP_DEADZONE:
1098 case (DWORD_PTR)DIPROP_SATURATION:
1099 case (DWORD_PTR)DIPROP_GRANULARITY:
1100 case (DWORD_PTR)DIPROP_KEYNAME:
1101 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
1102 case (DWORD_PTR)DIPROP_APPDATA:
1103 hr = impl->vtbl->enum_objects( iface, &filter, object_mask, get_object_property, &params );
1104 if (FAILED(hr)) return hr;
1105 if (hr == DIENUM_CONTINUE) return DIERR_NOTFOUND;
1106 return DI_OK;
1108 case (DWORD_PTR)DIPROP_AUTOCENTER:
1110 DIPROPDWORD *value = (DIPROPDWORD *)header;
1111 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1112 value->dwData = impl->autocenter;
1113 return DI_OK;
1115 case (DWORD_PTR)DIPROP_BUFFERSIZE:
1117 DIPROPDWORD *value = (DIPROPDWORD *)header;
1118 value->dwData = impl->buffersize;
1119 return DI_OK;
1121 case (DWORD_PTR)DIPROP_USERNAME:
1123 DIPROPSTRING *value = (DIPROPSTRING *)header;
1124 struct DevicePlayer *device_player;
1125 LIST_FOR_EACH_ENTRY( device_player, &impl->dinput->device_players, struct DevicePlayer, entry )
1127 if (IsEqualGUID( &device_player->instance_guid, &impl->guid ))
1129 if (!*device_player->username) break;
1130 lstrcpynW( value->wsz, device_player->username, ARRAY_SIZE(value->wsz) );
1131 return DI_OK;
1134 return S_FALSE;
1136 case (DWORD_PTR)DIPROP_FFGAIN:
1138 DIPROPDWORD *value = (DIPROPDWORD *)header;
1139 value->dwData = impl->device_gain;
1140 return DI_OK;
1142 case (DWORD_PTR)DIPROP_CALIBRATION:
1143 return DIERR_INVALIDPARAM;
1144 default:
1145 FIXME( "Unknown property %s\n", debugstr_guid( guid ) );
1146 return DIERR_UNSUPPORTED;
1149 return DI_OK;
1152 static HRESULT WINAPI dinput_device_GetProperty( IDirectInputDevice8W *iface, const GUID *guid, DIPROPHEADER *header )
1154 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1155 HRESULT hr;
1157 TRACE( "iface %p, guid %s, header %p\n", iface, debugstr_guid( guid ), header );
1159 if (!header) return DIERR_INVALIDPARAM;
1160 if (header->dwHeaderSize != sizeof(DIPROPHEADER)) return DIERR_INVALIDPARAM;
1161 if (!IS_DIPROP( guid )) return DI_OK;
1163 EnterCriticalSection( &impl->crit );
1164 hr = dinput_device_get_property( iface, guid, header );
1165 LeaveCriticalSection( &impl->crit );
1167 return hr;
1170 struct set_object_property_params
1172 IDirectInputDevice8W *iface;
1173 const DIPROPHEADER *header;
1174 DWORD property;
1177 static BOOL set_object_property( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1178 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1180 struct set_object_property_params *params = data;
1181 struct dinput_device *impl = impl_from_IDirectInputDevice8W( params->iface );
1182 struct object_properties *properties;
1184 if (index == -1) return DIENUM_STOP;
1185 properties = impl->object_properties + index;
1187 switch (params->property)
1189 case (DWORD_PTR)DIPROP_RANGE:
1191 const DIPROPRANGE *value = (const DIPROPRANGE *)params->header;
1192 properties->range_min = value->lMin;
1193 properties->range_max = value->lMax;
1194 return DIENUM_CONTINUE;
1196 case (DWORD_PTR)DIPROP_DEADZONE:
1198 const DIPROPDWORD *value = (const DIPROPDWORD *)params->header;
1199 properties->deadzone = value->dwData;
1200 return DIENUM_CONTINUE;
1202 case (DWORD_PTR)DIPROP_SATURATION:
1204 const DIPROPDWORD *value = (const DIPROPDWORD *)params->header;
1205 properties->saturation = value->dwData;
1206 return DIENUM_CONTINUE;
1208 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
1210 const DIPROPDWORD *value = (const DIPROPDWORD *)params->header;
1211 properties->calibration_mode = value->dwData;
1212 return DIENUM_CONTINUE;
1214 case (DWORD_PTR)DIPROP_APPDATA:
1216 DIPROPPOINTER *value = (DIPROPPOINTER *)params->header;
1217 properties->app_data = value->uData;
1218 return DIENUM_CONTINUE;
1222 return DIENUM_STOP;
1225 static BOOL reset_object_value( struct dinput_device *impl, UINT index, struct hid_value_caps *caps,
1226 const DIDEVICEOBJECTINSTANCEW *instance, void *context )
1228 struct object_properties *properties;
1229 LONG tmp = -1;
1231 if (index == -1) return DIENUM_STOP;
1232 properties = impl->object_properties + index;
1234 if (instance->dwType & DIDFT_AXIS)
1236 LONG range_min = 0, range_max = 0xfffe;
1237 if (properties->range_min != DIPROPRANGE_NOMIN) range_min = properties->range_min;
1238 if (properties->range_max != DIPROPRANGE_NOMAX) range_max = properties->range_max;
1239 tmp = round( (range_min + range_max) / 2.0 );
1242 *(LONG *)(impl->device_state + instance->dwOfs) = tmp;
1243 return DIENUM_CONTINUE;
1246 static void reset_device_state( IDirectInputDevice8W *iface )
1248 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1249 DIPROPHEADER filter =
1251 .dwHeaderSize = sizeof(DIPROPHEADER),
1252 .dwSize = sizeof(DIPROPHEADER),
1253 .dwHow = DIPH_DEVICE,
1254 .dwObj = 0,
1257 impl->vtbl->enum_objects( iface, &filter, DIDFT_AXIS | DIDFT_POV, reset_object_value, impl );
1260 static HRESULT dinput_device_set_property( IDirectInputDevice8W *iface, const GUID *guid,
1261 const DIPROPHEADER *header )
1263 struct set_object_property_params params = {.iface = iface, .header = header, .property = LOWORD( guid )};
1264 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1265 DIPROPHEADER filter;
1266 HRESULT hr;
1268 filter = *header;
1269 if (FAILED(hr = enum_object_filter_init( impl, &filter ))) return hr;
1270 if (FAILED(hr = check_property( impl, guid, header, TRUE ))) return hr;
1272 switch (LOWORD( guid ))
1274 case (DWORD_PTR)DIPROP_RANGE:
1275 case (DWORD_PTR)DIPROP_DEADZONE:
1276 case (DWORD_PTR)DIPROP_SATURATION:
1278 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_AXIS, set_object_property, &params );
1279 if (FAILED(hr)) return hr;
1280 reset_device_state( iface );
1281 return DI_OK;
1283 case (DWORD_PTR)DIPROP_CALIBRATIONMODE:
1285 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1286 if (value->dwData > DIPROPCALIBRATIONMODE_RAW) return DIERR_INVALIDPARAM;
1287 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_AXIS, set_object_property, &params );
1288 if (FAILED(hr)) return hr;
1289 reset_device_state( iface );
1290 return DI_OK;
1292 case (DWORD_PTR)DIPROP_AUTOCENTER:
1294 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1295 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1297 FIXME( "DIPROP_AUTOCENTER stub!\n" );
1298 impl->autocenter = value->dwData;
1299 return DI_OK;
1301 case (DWORD_PTR)DIPROP_FFGAIN:
1303 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1304 impl->device_gain = value->dwData;
1305 if (!is_exclusively_acquired( impl )) return DI_OK;
1306 if (!impl->vtbl->send_device_gain) return DI_OK;
1307 return impl->vtbl->send_device_gain( iface, impl->device_gain );
1309 case (DWORD_PTR)DIPROP_AXISMODE:
1311 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1313 TRACE( "Axis mode: %s\n", value->dwData == DIPROPAXISMODE_ABS ? "absolute" : "relative" );
1315 impl->user_format.dwFlags &= ~DIDFT_AXIS;
1316 impl->user_format.dwFlags |= value->dwData == DIPROPAXISMODE_ABS ? DIDF_ABSAXIS : DIDF_RELAXIS;
1318 return DI_OK;
1320 case (DWORD_PTR)DIPROP_BUFFERSIZE:
1322 const DIPROPDWORD *value = (const DIPROPDWORD *)header;
1324 TRACE( "buffersize %lu\n", value->dwData );
1326 impl->buffersize = value->dwData;
1327 impl->queue_len = min( impl->buffersize, 1024 );
1328 free( impl->data_queue );
1330 impl->data_queue = impl->queue_len ? malloc( impl->queue_len * sizeof(DIDEVICEOBJECTDATA) ) : NULL;
1331 impl->queue_head = impl->queue_tail = impl->overflow = 0;
1332 return DI_OK;
1334 case (DWORD_PTR)DIPROP_APPDATA:
1336 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, set_object_property, &params );
1337 if (FAILED(hr)) return hr;
1338 return DI_OK;
1340 default:
1341 FIXME( "Unknown property %s\n", debugstr_guid( guid ) );
1342 return DIERR_UNSUPPORTED;
1345 return DI_OK;
1348 static HRESULT WINAPI dinput_device_SetProperty( IDirectInputDevice8W *iface, const GUID *guid,
1349 const DIPROPHEADER *header )
1351 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1352 HRESULT hr;
1354 TRACE( "iface %p, guid %s, header %p\n", iface, debugstr_guid( guid ), header );
1356 if (!header) return DIERR_INVALIDPARAM;
1357 if (header->dwHeaderSize != sizeof(DIPROPHEADER)) return DIERR_INVALIDPARAM;
1358 if (!IS_DIPROP( guid )) return DI_OK;
1360 EnterCriticalSection( &impl->crit );
1361 hr = dinput_device_set_property( iface, guid, header );
1362 LeaveCriticalSection( &impl->crit );
1364 return hr;
1367 static void dinput_device_set_username( struct dinput_device *impl, const DIPROPSTRING *value )
1369 struct DevicePlayer *device_player;
1370 BOOL found = FALSE;
1372 LIST_FOR_EACH_ENTRY( device_player, &impl->dinput->device_players, struct DevicePlayer, entry )
1374 if (IsEqualGUID( &device_player->instance_guid, &impl->guid ))
1376 found = TRUE;
1377 break;
1380 if (!found && (device_player = malloc( sizeof(struct DevicePlayer) )))
1382 list_add_tail( &impl->dinput->device_players, &device_player->entry );
1383 device_player->instance_guid = impl->guid;
1385 if (device_player)
1386 lstrcpynW( device_player->username, value->wsz, ARRAY_SIZE(device_player->username) );
1389 static BOOL get_object_info( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1390 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1392 DIDEVICEOBJECTINSTANCEW *dest = data;
1393 DWORD size = dest->dwSize;
1395 memcpy( dest, instance, size );
1396 dest->dwSize = size;
1398 return DIENUM_STOP;
1401 static HRESULT WINAPI dinput_device_GetObjectInfo( IDirectInputDevice8W *iface,
1402 DIDEVICEOBJECTINSTANCEW *instance, DWORD obj, DWORD how )
1404 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1405 DIPROPHEADER filter =
1407 .dwSize = sizeof(filter),
1408 .dwHeaderSize = sizeof(filter),
1409 .dwHow = how,
1410 .dwObj = obj
1412 HRESULT hr;
1414 TRACE( "iface %p, instance %p, obj %#lx, how %#lx.\n", iface, instance, obj, how );
1416 if (!instance) return E_POINTER;
1417 if (instance->dwSize != sizeof(DIDEVICEOBJECTINSTANCE_DX3W) && instance->dwSize != sizeof(DIDEVICEOBJECTINSTANCEW))
1418 return DIERR_INVALIDPARAM;
1419 if (how == DIPH_DEVICE) return DIERR_INVALIDPARAM;
1420 if (FAILED(hr = enum_object_filter_init( impl, &filter ))) return hr;
1422 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, get_object_info, instance );
1423 if (FAILED(hr)) return hr;
1424 if (hr == DIENUM_CONTINUE) return DIERR_NOTFOUND;
1425 return DI_OK;
1428 static HRESULT WINAPI dinput_device_GetDeviceState( IDirectInputDevice8W *iface, DWORD size, void *data )
1430 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1431 DIDATAFORMAT *device_format = &impl->device_format, *user_format = &impl->user_format;
1432 DIOBJECTDATAFORMAT *device_obj, *user_obj;
1433 BYTE *user_state = data;
1434 DIPROPHEADER filter =
1436 .dwSize = sizeof(filter),
1437 .dwHeaderSize = sizeof(filter),
1438 .dwHow = DIPH_DEVICE,
1439 .dwObj = 0,
1441 HRESULT hr;
1443 TRACE( "iface %p, size %lu, data %p.\n", iface, size, data );
1445 if (!data) return DIERR_INVALIDPARAM;
1447 IDirectInputDevice2_Poll( iface );
1449 EnterCriticalSection( &impl->crit );
1450 if (impl->status == STATUS_UNPLUGGED)
1451 hr = DIERR_INPUTLOST;
1452 else if (impl->status != STATUS_ACQUIRED)
1453 hr = DIERR_NOTACQUIRED;
1454 else if (!user_format->rgodf)
1455 hr = DIERR_INVALIDPARAM;
1456 else if (size != user_format->dwDataSize)
1457 hr = DIERR_INVALIDPARAM;
1458 else
1460 memset( user_state, 0, size );
1462 user_obj = user_format->rgodf + device_format->dwNumObjs;
1463 device_obj = device_format->rgodf + device_format->dwNumObjs;
1464 while (user_obj-- > user_format->rgodf && device_obj-- > device_format->rgodf)
1466 if (user_obj->dwType & DIDFT_BUTTON)
1467 user_state[user_obj->dwOfs] = impl->device_state[device_obj->dwOfs];
1470 /* reset optional POVs to their default */
1471 user_obj = user_format->rgodf + user_format->dwNumObjs;
1472 while (user_obj-- > user_format->rgodf + device_format->dwNumObjs)
1473 if (user_obj->dwType & DIDFT_POV) *(ULONG *)(user_state + user_obj->dwOfs) = 0xffffffff;
1475 user_obj = user_format->rgodf + device_format->dwNumObjs;
1476 device_obj = device_format->rgodf + device_format->dwNumObjs;
1477 while (user_obj-- > user_format->rgodf && device_obj-- > device_format->rgodf)
1479 if (user_obj->dwType & (DIDFT_POV | DIDFT_AXIS))
1480 *(ULONG *)(user_state + user_obj->dwOfs) = *(ULONG *)(impl->device_state + device_obj->dwOfs);
1481 if (!(user_format->dwFlags & DIDF_ABSAXIS) && (device_obj->dwType & DIDFT_RELAXIS))
1482 *(ULONG *)(impl->device_state + device_obj->dwOfs) = 0;
1485 hr = DI_OK;
1487 LeaveCriticalSection( &impl->crit );
1489 return hr;
1492 static HRESULT WINAPI dinput_device_GetDeviceData( IDirectInputDevice8W *iface, DWORD size, DIDEVICEOBJECTDATA *data,
1493 DWORD *count, DWORD flags )
1495 struct dinput_device *This = impl_from_IDirectInputDevice8W( iface );
1496 HRESULT ret = DI_OK;
1497 int len;
1499 TRACE( "iface %p, size %lu, data %p, count %p, flags %#lx.\n", iface, size, data, count, flags );
1501 if (This->dinput->dwVersion == 0x0800 || size == sizeof(DIDEVICEOBJECTDATA_DX3))
1503 if (!This->queue_len) return DIERR_NOTBUFFERED;
1504 if (This->status == STATUS_UNPLUGGED) return DIERR_INPUTLOST;
1505 if (This->status != STATUS_ACQUIRED) return DIERR_NOTACQUIRED;
1508 if (!This->queue_len)
1509 return DI_OK;
1510 if (size < sizeof(DIDEVICEOBJECTDATA_DX3)) return DIERR_INVALIDPARAM;
1512 IDirectInputDevice2_Poll(iface);
1513 EnterCriticalSection(&This->crit);
1515 len = This->queue_head - This->queue_tail;
1516 if (len < 0) len += This->queue_len;
1518 if ((*count != INFINITE) && (len > *count)) len = *count;
1520 if (data)
1522 int i;
1523 for (i = 0; i < len; i++)
1525 int n = (This->queue_tail + i) % This->queue_len;
1526 memcpy( (char *)data + size * i, This->data_queue + n, size );
1529 *count = len;
1531 if (This->overflow && This->dinput->dwVersion == 0x0800)
1532 ret = DI_BUFFEROVERFLOW;
1534 if (!(flags & DIGDD_PEEK))
1536 /* Advance reading position */
1537 This->queue_tail = (This->queue_tail + len) % This->queue_len;
1538 This->overflow = FALSE;
1541 LeaveCriticalSection(&This->crit);
1543 TRACE( "Returning %lu events queued\n", *count );
1544 return ret;
1547 static HRESULT WINAPI dinput_device_RunControlPanel( IDirectInputDevice8W *iface, HWND hwnd, DWORD flags )
1549 FIXME( "iface %p, hwnd %p, flags %#lx stub!\n", iface, hwnd, flags );
1550 return DI_OK;
1553 static HRESULT WINAPI dinput_device_Initialize( IDirectInputDevice8W *iface, HINSTANCE instance,
1554 DWORD version, const GUID *guid )
1556 FIXME( "iface %p, instance %p, version %#lx, guid %s stub!\n", iface, instance, version,
1557 debugstr_guid( guid ) );
1558 return DI_OK;
1561 static HRESULT WINAPI dinput_device_CreateEffect( IDirectInputDevice8W *iface, const GUID *guid,
1562 const DIEFFECT *params, IDirectInputEffect **out,
1563 IUnknown *outer )
1565 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1566 DWORD flags;
1567 HRESULT hr;
1569 TRACE( "iface %p, guid %s, params %p, out %p, outer %p\n", iface, debugstr_guid( guid ),
1570 params, out, outer );
1572 if (!out) return E_POINTER;
1573 *out = NULL;
1575 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1576 if (!impl->vtbl->create_effect) return DIERR_UNSUPPORTED;
1577 if (FAILED(hr = impl->vtbl->create_effect( iface, out ))) return hr;
1579 hr = IDirectInputEffect_Initialize( *out, DINPUT_instance, impl->dinput->dwVersion, guid );
1580 if (FAILED(hr)) goto failed;
1581 if (!params) return DI_OK;
1583 flags = params->dwSize == sizeof(DIEFFECT_DX6) ? DIEP_ALLPARAMS : DIEP_ALLPARAMS_DX5;
1584 if (!is_exclusively_acquired( impl )) flags |= DIEP_NODOWNLOAD;
1585 hr = IDirectInputEffect_SetParameters( *out, params, flags );
1586 if (FAILED(hr)) goto failed;
1587 return DI_OK;
1589 failed:
1590 IDirectInputEffect_Release( *out );
1591 *out = NULL;
1592 return hr;
1595 static HRESULT WINAPI dinput_device_EnumEffects( IDirectInputDevice8W *iface, LPDIENUMEFFECTSCALLBACKW callback,
1596 void *context, DWORD type )
1598 DIEFFECTINFOW info = {.dwSize = sizeof(info)};
1599 HRESULT hr;
1601 TRACE( "iface %p, callback %p, context %p, type %#lx.\n", iface, callback, context, type );
1603 if (!callback) return DIERR_INVALIDPARAM;
1605 type = DIEFT_GETTYPE( type );
1607 if (type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1609 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_ConstantForce );
1610 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1611 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1614 if (type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1616 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_RampForce );
1617 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1618 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1621 if (type == DIEFT_ALL || type == DIEFT_PERIODIC)
1623 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Square );
1624 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1625 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1627 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Sine );
1628 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1629 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1631 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Triangle );
1632 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1633 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1635 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_SawtoothUp );
1636 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1637 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1639 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_SawtoothDown );
1640 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1641 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1644 if (type == DIEFT_ALL || type == DIEFT_CONDITION)
1646 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Spring );
1647 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1648 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1650 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Damper );
1651 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1652 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1654 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Inertia );
1655 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1656 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1658 hr = IDirectInputDevice8_GetEffectInfo( iface, &info, &GUID_Friction );
1659 if (FAILED(hr) && hr != DIERR_DEVICENOTREG) return hr;
1660 if (hr == DI_OK && callback( &info, context ) == DIENUM_STOP) return DI_OK;
1663 return DI_OK;
1666 static HRESULT WINAPI dinput_device_GetEffectInfo( IDirectInputDevice8W *iface, DIEFFECTINFOW *info,
1667 const GUID *guid )
1669 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1671 TRACE( "iface %p, info %p, guid %s.\n", iface, info, debugstr_guid( guid ) );
1673 if (!info) return E_POINTER;
1674 if (info->dwSize != sizeof(DIEFFECTINFOW)) return DIERR_INVALIDPARAM;
1675 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_DEVICENOTREG;
1676 if (!impl->vtbl->get_effect_info) return DIERR_UNSUPPORTED;
1677 return impl->vtbl->get_effect_info( iface, info, guid );
1680 static HRESULT WINAPI dinput_device_GetForceFeedbackState( IDirectInputDevice8W *iface, DWORD *out )
1682 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1683 HRESULT hr = DI_OK;
1685 TRACE( "iface %p, out %p.\n", iface, out );
1687 if (!out) return E_POINTER;
1688 *out = 0;
1690 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1692 EnterCriticalSection( &impl->crit );
1693 if (!is_exclusively_acquired( impl )) hr = DIERR_NOTEXCLUSIVEACQUIRED;
1694 else *out = impl->force_feedback_state;
1695 LeaveCriticalSection( &impl->crit );
1697 return hr;
1700 static HRESULT WINAPI dinput_device_SendForceFeedbackCommand( IDirectInputDevice8W *iface, DWORD command )
1702 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1703 HRESULT hr;
1705 TRACE( "iface %p, command %#lx.\n", iface, command );
1707 switch (command)
1709 case DISFFC_RESET: break;
1710 case DISFFC_STOPALL: break;
1711 case DISFFC_PAUSE: break;
1712 case DISFFC_CONTINUE: break;
1713 case DISFFC_SETACTUATORSON: break;
1714 case DISFFC_SETACTUATORSOFF: break;
1715 default: return DIERR_INVALIDPARAM;
1718 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
1719 if (!impl->vtbl->send_force_feedback_command) return DIERR_UNSUPPORTED;
1721 EnterCriticalSection( &impl->crit );
1722 if (!is_exclusively_acquired( impl )) hr = DIERR_NOTEXCLUSIVEACQUIRED;
1723 else hr = impl->vtbl->send_force_feedback_command( iface, command, FALSE );
1724 LeaveCriticalSection( &impl->crit );
1726 return hr;
1729 static HRESULT WINAPI dinput_device_EnumCreatedEffectObjects( IDirectInputDevice8W *iface,
1730 LPDIENUMCREATEDEFFECTOBJECTSCALLBACK callback,
1731 void *context, DWORD flags )
1733 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1735 TRACE( "iface %p, callback %p, context %p, flags %#lx.\n", iface, callback, context, flags );
1737 if (!callback) return DIERR_INVALIDPARAM;
1738 if (flags) return DIERR_INVALIDPARAM;
1739 if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DI_OK;
1740 if (!impl->vtbl->enum_created_effect_objects) return DIERR_UNSUPPORTED;
1742 return impl->vtbl->enum_created_effect_objects( iface, callback, context, flags );
1745 static HRESULT WINAPI dinput_device_Escape( IDirectInputDevice8W *iface, DIEFFESCAPE *escape )
1747 FIXME( "iface %p, escape %p stub!\n", iface, escape );
1748 return DI_OK;
1751 static HRESULT WINAPI dinput_device_Poll( IDirectInputDevice8W *iface )
1753 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1754 HRESULT hr = DI_NOEFFECT;
1756 EnterCriticalSection( &impl->crit );
1757 if (impl->status == STATUS_UNPLUGGED) hr = DIERR_INPUTLOST;
1758 else if (impl->status != STATUS_ACQUIRED) hr = DIERR_NOTACQUIRED;
1759 LeaveCriticalSection( &impl->crit );
1760 if (FAILED(hr)) return hr;
1762 if (impl->vtbl->poll) return impl->vtbl->poll( iface );
1763 return hr;
1766 static HRESULT WINAPI dinput_device_SendDeviceData( IDirectInputDevice8W *iface, DWORD size,
1767 const DIDEVICEOBJECTDATA *data, DWORD *count, DWORD flags )
1769 FIXME( "iface %p, size %lu, data %p, count %p, flags %#lx stub!\n", iface, size, data, count, flags );
1770 return DI_OK;
1773 static HRESULT WINAPI dinput_device_EnumEffectsInFile( IDirectInputDevice8W *iface, const WCHAR *filename,
1774 LPDIENUMEFFECTSINFILECALLBACK callback,
1775 void *context, DWORD flags )
1777 FIXME( "iface %p, filename %s, callback %p, context %p, flags %#lx stub!\n", iface,
1778 debugstr_w(filename), callback, context, flags );
1779 return DI_OK;
1782 static HRESULT WINAPI dinput_device_WriteEffectToFile( IDirectInputDevice8W *iface, const WCHAR *filename,
1783 DWORD count, DIFILEEFFECT *effects, DWORD flags )
1785 FIXME( "iface %p, filename %s, count %lu, effects %p, flags %#lx stub!\n", iface,
1786 debugstr_w(filename), count, effects, flags );
1787 return DI_OK;
1790 BOOL device_object_matches_semantic( const DIDEVICEINSTANCEW *instance, const DIOBJECTDATAFORMAT *object,
1791 DWORD semantic, BOOL exact )
1793 DWORD value = semantic & 0xff, axis = (semantic >> 15) & 3, type;
1795 switch (semantic & 0x700)
1797 case 0x200: type = DIDFT_ABSAXIS; break;
1798 case 0x300: type = DIDFT_RELAXIS; break;
1799 case 0x400: type = DIDFT_BUTTON; break;
1800 case 0x600: type = DIDFT_POV; break;
1801 default: return FALSE;
1804 if (!(DIDFT_GETTYPE( object->dwType ) & type)) return FALSE;
1805 if ((semantic & 0xf0000000) == 0x80000000)
1807 switch (semantic & 0x0f000000)
1809 case 0x01000000: return (instance->dwDevType & 0xf) == DIDEVTYPE_KEYBOARD && object->dwOfs == value;
1810 case 0x02000000: return (instance->dwDevType & 0xf) == DIDEVTYPE_MOUSE && object->dwOfs == value;
1811 default: return FALSE;
1814 if (axis && (axis - 1) != DIDFT_GETINSTANCE( object->dwType )) return FALSE;
1815 return !exact || !value || value == DIDFT_GETINSTANCE( object->dwType ) + 1;
1818 static HRESULT WINAPI dinput_device_BuildActionMap( IDirectInputDevice8W *iface, DIACTIONFORMATW *format,
1819 const WCHAR *username, DWORD flags )
1821 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1822 DIOBJECTDATAFORMAT *object, *object_end;
1823 DIACTIONW *action, *action_end;
1824 DWORD i, username_len = MAX_PATH;
1825 WCHAR username_buf[MAX_PATH];
1826 BOOL *mapped;
1828 TRACE( "iface %p, format %p, username %s, flags %#lx\n", iface, format,
1829 debugstr_w(username), flags );
1831 if (!format) return DIERR_INVALIDPARAM;
1832 if (flags != DIDBAM_DEFAULT && flags != DIDBAM_PRESERVE &&
1833 flags != DIDBAM_INITIALIZE && flags != DIDBAM_HWDEFAULTS)
1834 return DIERR_INVALIDPARAM;
1835 if (format->dwNumActions * 4 != format->dwDataSize)
1836 return DIERR_INVALIDPARAM;
1838 TRACE( "format guid %s, genre %#lx, name %s\n", debugstr_guid(&format->guidActionMap),
1839 format->dwGenre, debugstr_w(format->tszActionMap) );
1840 for (i = 0; i < format->dwNumActions; i++)
1842 DIACTIONW *action = format->rgoAction + i;
1843 TRACE( " %lu: app_data %#Ix, semantic %#lx, flags %#lx, instance %s, obj_id %#lx, how %#lx, name %s\n",
1844 i, action->uAppData, action->dwSemantic, action->dwFlags, debugstr_guid(&action->guidInstance),
1845 action->dwObjID, action->dwHow, debugstr_w(action->lptszActionName) );
1848 action_end = format->rgoAction + format->dwNumActions;
1849 for (action = format->rgoAction; action < action_end; action++)
1851 if (!action->dwSemantic) return DIERR_INVALIDPARAM;
1852 if (flags == DIDBAM_PRESERVE && !IsEqualCLSID( &action->guidInstance, &GUID_NULL ) &&
1853 !IsEqualCLSID( &action->guidInstance, &impl->guid )) continue;
1854 if (action->dwFlags & DIA_APPMAPPED) action->dwHow = DIAH_APPREQUESTED;
1855 else action->dwHow = 0;
1858 /* Unless asked the contrary by these flags, try to load a previous mapping */
1859 if (!(flags & DIDBAM_HWDEFAULTS))
1861 /* Retrieve logged user name if necessary */
1862 if (username == NULL) GetUserNameW( username_buf, &username_len );
1863 else lstrcpynW( username_buf, username, MAX_PATH );
1864 load_mapping_settings( impl, format, username_buf );
1867 action_end = format->rgoAction + format->dwNumActions;
1868 for (action = format->rgoAction; action < action_end; action++)
1870 if (action->dwHow == DIAH_APPREQUESTED || action->dwHow == DIAH_USERCONFIG) continue;
1871 if (flags == DIDBAM_PRESERVE && !IsEqualCLSID( &action->guidInstance, &GUID_NULL ) &&
1872 !IsEqualCLSID( &action->guidInstance, &impl->guid )) continue;
1873 if (action->dwFlags & DIA_APPNOMAP) continue;
1874 action->guidInstance = GUID_NULL;
1875 action->dwHow = 0;
1878 if (!(mapped = calloc( impl->device_format.dwNumObjs, sizeof(*mapped) ))) return DIERR_OUTOFMEMORY;
1880 action_end = format->rgoAction + format->dwNumActions;
1881 for (action = format->rgoAction; action < action_end; action++)
1883 if (action->dwHow || (action->dwFlags & DIA_APPNOMAP)) continue; /* already mapped */
1884 if (action->dwSemantic & 0x4000) continue; /* priority 1 */
1886 object_end = impl->device_format.rgodf + impl->device_format.dwNumObjs;
1887 for (object = impl->device_format.rgodf; object < object_end; object++)
1889 if (mapped[object - impl->device_format.rgodf]) continue;
1890 if (!device_object_matches_semantic( &impl->instance, object, action->dwSemantic, TRUE )) continue;
1891 if ((action->dwFlags & DIA_FORCEFEEDBACK) && !(object->dwType & DIDFT_FFACTUATOR)) continue;
1892 action->dwObjID = object->dwType;
1893 action->guidInstance = impl->guid;
1894 action->dwHow = DIAH_DEFAULT;
1895 mapped[object - impl->device_format.rgodf] = TRUE;
1896 break;
1900 for (action = format->rgoAction; action < action_end; action++)
1902 if (action->dwHow || (action->dwFlags & DIA_APPNOMAP)) continue; /* already mapped */
1903 if (!(action->dwSemantic & 0x4000)) continue; /* priority 2 */
1905 object_end = impl->device_format.rgodf + impl->device_format.dwNumObjs;
1906 for (object = impl->device_format.rgodf; object < object_end; object++)
1908 if (mapped[object - impl->device_format.rgodf]) continue;
1909 if (!device_object_matches_semantic( &impl->instance, object, action->dwSemantic, FALSE )) continue;
1910 if ((action->dwFlags & DIA_FORCEFEEDBACK) && !(object->dwType & DIDFT_FFACTUATOR)) continue;
1911 action->dwObjID = object->dwType;
1912 action->guidInstance = impl->guid;
1913 action->dwHow = DIAH_DEFAULT;
1914 mapped[object - impl->device_format.rgodf] = TRUE;
1915 break;
1919 for (i = 0; i < impl->device_format.dwNumObjs; ++i) if (mapped[i]) break;
1920 free( mapped );
1922 if (i == impl->device_format.dwNumObjs) return DI_NOEFFECT;
1923 return DI_OK;
1926 static BOOL init_object_app_data( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1927 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1929 struct object_properties *properties;
1930 const DIACTIONFORMATW *format = data;
1931 const DIACTIONW *action = format->rgoAction + format->dwNumActions;
1933 if (index == -1) return DIENUM_STOP;
1934 if (instance->wUsagePage == HID_USAGE_PAGE_PID) return DIENUM_CONTINUE;
1936 properties = device->object_properties + index;
1937 properties->app_data = 0;
1939 while (action-- > format->rgoAction)
1941 if (action->dwObjID != instance->dwType) continue;
1942 properties->app_data = action->uAppData;
1943 break;
1946 return DIENUM_CONTINUE;
1949 static HRESULT WINAPI dinput_device_SetActionMap( IDirectInputDevice8W *iface, DIACTIONFORMATW *format,
1950 const WCHAR *username, DWORD flags )
1952 static const DIPROPHEADER filter =
1954 .dwSize = sizeof(filter),
1955 .dwHeaderSize = sizeof(filter),
1956 .dwHow = DIPH_DEVICE,
1958 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
1959 DIDATAFORMAT data_format =
1961 .dwSize = sizeof(DIDATAFORMAT),
1962 .dwObjSize = sizeof(DIOBJECTDATAFORMAT),
1963 .dwFlags = DIDF_RELAXIS,
1965 DIPROPDWORD prop_buffer =
1967 .diph =
1969 .dwHeaderSize = sizeof(DIPROPHEADER),
1970 .dwSize = sizeof(DIPROPDWORD),
1971 .dwHow = DIPH_DEVICE,
1974 DIPROPRANGE prop_range =
1976 .diph =
1978 .dwHeaderSize = sizeof(DIPROPHEADER),
1979 .dwSize = sizeof(DIPROPRANGE),
1980 .dwHow = DIPH_DEVICE,
1983 DIPROPSTRING prop_username =
1985 .diph =
1987 .dwHeaderSize = sizeof(DIPROPHEADER),
1988 .dwSize = sizeof(DIPROPSTRING),
1989 .dwHow = DIPH_DEVICE,
1992 WCHAR username_buf[MAX_PATH];
1993 DWORD username_len = MAX_PATH;
1994 unsigned int offset = 0;
1995 int i, index;
1996 HRESULT hr;
1998 TRACE( "iface %p, format %p, username %s, flags %#lx\n", iface, format,
1999 debugstr_w(username), flags );
2001 if (!format) return DIERR_INVALIDPARAM;
2002 if (flags != DIDSAM_DEFAULT && flags != DIDSAM_FORCESAVE && flags != DIDSAM_NOUSER) return DIERR_INVALIDPARAM;
2004 TRACE( "format guid %s, genre %#lx, name %s\n", debugstr_guid(&format->guidActionMap),
2005 format->dwGenre, debugstr_w(format->tszActionMap) );
2006 for (i = 0; i < format->dwNumActions; i++)
2008 DIACTIONW *action = format->rgoAction + i;
2009 TRACE( " %u: app_data %#Ix, semantic %#lx, flags %#lx, instance %s, obj_id %#lx, how %#lx, name %s\n",
2010 i, action->uAppData, action->dwSemantic, action->dwFlags, debugstr_guid(&action->guidInstance),
2011 action->dwObjID, action->dwHow, debugstr_w(action->lptszActionName) );
2014 if (!(data_format.rgodf = malloc( sizeof(DIOBJECTDATAFORMAT) * format->dwNumActions ))) return DIERR_OUTOFMEMORY;
2015 data_format.dwDataSize = format->dwDataSize;
2017 for (i = 0; i < format->dwNumActions; i++, offset += sizeof(ULONG))
2019 if (format->rgoAction[i].dwFlags & DIA_APPNOMAP) continue;
2020 if (!IsEqualGUID( &impl->guid, &format->rgoAction[i].guidInstance )) continue;
2021 if ((index = dinput_device_object_index_from_id( iface, format->rgoAction[i].dwObjID )) < 0) continue;
2023 data_format.rgodf[data_format.dwNumObjs] = impl->device_format.rgodf[index];
2024 data_format.rgodf[data_format.dwNumObjs].dwOfs = offset;
2025 data_format.dwNumObjs++;
2028 EnterCriticalSection( &impl->crit );
2030 if (FAILED(hr = IDirectInputDevice8_SetDataFormat( iface, &data_format )))
2031 WARN( "Failed to set data format from action map, hr %#lx\n", hr );
2032 else
2034 if (FAILED(impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, init_object_app_data, format )))
2035 WARN( "Failed to initialize action map app data\n" );
2037 if (format->lAxisMin != format->lAxisMax)
2039 prop_range.lMin = format->lAxisMin;
2040 prop_range.lMax = format->lAxisMax;
2041 IDirectInputDevice8_SetProperty( iface, DIPROP_RANGE, &prop_range.diph );
2044 prop_buffer.dwData = format->dwBufferSize;
2045 IDirectInputDevice8_SetProperty( iface, DIPROP_BUFFERSIZE, &prop_buffer.diph );
2047 if (username == NULL) GetUserNameW( username_buf, &username_len );
2048 else lstrcpynW( username_buf, username, MAX_PATH );
2050 if (flags & DIDSAM_NOUSER) prop_username.wsz[0] = '\0';
2051 else lstrcpynW( prop_username.wsz, username_buf, ARRAY_SIZE(prop_username.wsz) );
2052 dinput_device_set_username( impl, &prop_username );
2054 save_mapping_settings( iface, format, username_buf );
2057 LeaveCriticalSection( &impl->crit );
2059 free( data_format.rgodf );
2061 if (FAILED(hr)) return hr;
2062 if (flags == DIDSAM_FORCESAVE) return DI_SETTINGSNOTSAVED;
2063 if (!data_format.dwNumObjs) return DI_NOEFFECT;
2064 return hr;
2067 static HRESULT WINAPI dinput_device_GetImageInfo( IDirectInputDevice8W *iface, DIDEVICEIMAGEINFOHEADERW *header )
2069 FIXME( "iface %p, header %p stub!\n", iface, header );
2070 return DI_OK;
2073 extern const IDirectInputDevice8AVtbl dinput_device_a_vtbl;
2074 static const IDirectInputDevice8WVtbl dinput_device_w_vtbl =
2076 /*** IUnknown methods ***/
2077 dinput_device_QueryInterface,
2078 dinput_device_AddRef,
2079 dinput_device_Release,
2080 /*** IDirectInputDevice methods ***/
2081 dinput_device_GetCapabilities,
2082 dinput_device_EnumObjects,
2083 dinput_device_GetProperty,
2084 dinput_device_SetProperty,
2085 dinput_device_Acquire,
2086 dinput_device_Unacquire,
2087 dinput_device_GetDeviceState,
2088 dinput_device_GetDeviceData,
2089 dinput_device_SetDataFormat,
2090 dinput_device_SetEventNotification,
2091 dinput_device_SetCooperativeLevel,
2092 dinput_device_GetObjectInfo,
2093 dinput_device_GetDeviceInfo,
2094 dinput_device_RunControlPanel,
2095 dinput_device_Initialize,
2096 /*** IDirectInputDevice2 methods ***/
2097 dinput_device_CreateEffect,
2098 dinput_device_EnumEffects,
2099 dinput_device_GetEffectInfo,
2100 dinput_device_GetForceFeedbackState,
2101 dinput_device_SendForceFeedbackCommand,
2102 dinput_device_EnumCreatedEffectObjects,
2103 dinput_device_Escape,
2104 dinput_device_Poll,
2105 dinput_device_SendDeviceData,
2106 /*** IDirectInputDevice7 methods ***/
2107 dinput_device_EnumEffectsInFile,
2108 dinput_device_WriteEffectToFile,
2109 /*** IDirectInputDevice8 methods ***/
2110 dinput_device_BuildActionMap,
2111 dinput_device_SetActionMap,
2112 dinput_device_GetImageInfo,
2115 void dinput_device_init( struct dinput_device *device, const struct dinput_device_vtbl *vtbl,
2116 const GUID *guid, struct dinput *dinput )
2118 device->IDirectInputDevice8A_iface.lpVtbl = &dinput_device_a_vtbl;
2119 device->IDirectInputDevice8W_iface.lpVtbl = &dinput_device_w_vtbl;
2120 device->internal_ref = 1;
2121 device->ref = 1;
2122 device->guid = *guid;
2123 device->instance.dwSize = sizeof(DIDEVICEINSTANCEW);
2124 device->caps.dwSize = sizeof(DIDEVCAPS);
2125 device->caps.dwFlags = DIDC_ATTACHED | DIDC_EMULATED;
2126 device->device_gain = 10000;
2127 device->force_feedback_state = DIGFFS_STOPPED | DIGFFS_EMPTY;
2128 InitializeCriticalSection( &device->crit );
2129 dinput_internal_addref( (device->dinput = dinput) );
2130 device->vtbl = vtbl;
2132 input_thread_add_user();
2135 static const GUID *object_instance_guid( const DIDEVICEOBJECTINSTANCEW *instance )
2137 if (IsEqualGUID( &instance->guidType, &GUID_XAxis )) return &GUID_XAxis;
2138 if (IsEqualGUID( &instance->guidType, &GUID_YAxis )) return &GUID_YAxis;
2139 if (IsEqualGUID( &instance->guidType, &GUID_ZAxis )) return &GUID_ZAxis;
2140 if (IsEqualGUID( &instance->guidType, &GUID_RxAxis )) return &GUID_RxAxis;
2141 if (IsEqualGUID( &instance->guidType, &GUID_RyAxis )) return &GUID_RyAxis;
2142 if (IsEqualGUID( &instance->guidType, &GUID_RzAxis )) return &GUID_RzAxis;
2143 if (IsEqualGUID( &instance->guidType, &GUID_Slider )) return &GUID_Slider;
2144 if (IsEqualGUID( &instance->guidType, &GUID_Button )) return &GUID_Button;
2145 if (IsEqualGUID( &instance->guidType, &GUID_Key )) return &GUID_Key;
2146 if (IsEqualGUID( &instance->guidType, &GUID_POV )) return &GUID_POV;
2147 return &GUID_Unknown;
2150 static BOOL enum_objects_count( struct dinput_device *impl, UINT index, struct hid_value_caps *caps,
2151 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
2153 DIDATAFORMAT *format = &impl->device_format;
2155 if (index == -1) return DIENUM_STOP;
2156 format->dwNumObjs++;
2157 if (instance->wUsagePage == HID_USAGE_PAGE_PID) return DIENUM_CONTINUE;
2159 format->dwDataSize = max( format->dwDataSize, instance->dwOfs + sizeof(LONG) );
2160 if (instance->dwType & DIDFT_BUTTON) impl->caps.dwButtons++;
2161 if (instance->dwType & DIDFT_AXIS) impl->caps.dwAxes++;
2162 if (instance->dwType & DIDFT_POV) impl->caps.dwPOVs++;
2163 if (instance->dwType & (DIDFT_BUTTON|DIDFT_AXIS|DIDFT_POV))
2165 if (!impl->device_state_report_id)
2166 impl->device_state_report_id = instance->wReportId;
2167 else if (impl->device_state_report_id != instance->wReportId)
2168 FIXME( "multiple device state reports found!\n" );
2171 return DIENUM_CONTINUE;
2174 static BOOL enum_objects_init( struct dinput_device *impl, UINT index, struct hid_value_caps *caps,
2175 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
2177 static const struct object_properties default_properties =
2179 .range_min = DIPROPRANGE_NOMIN,
2180 .range_max = DIPROPRANGE_NOMAX,
2181 .granularity = 1,
2182 .app_data = -1,
2184 DIDATAFORMAT *format = &impl->device_format;
2185 DIOBJECTDATAFORMAT *object_format;
2187 if (index == -1) return DIENUM_STOP;
2188 if (instance->wUsagePage == HID_USAGE_PAGE_PID) return DIENUM_CONTINUE;
2190 object_format = format->rgodf + index;
2191 object_format->pguid = object_instance_guid( instance );
2192 object_format->dwOfs = instance->dwOfs;
2193 object_format->dwType = instance->dwType;
2194 object_format->dwFlags = instance->dwFlags;
2196 impl->object_properties[index] = default_properties;
2197 if (instance->dwType & (DIDFT_AXIS | DIDFT_POV)) reset_object_value( impl, index, caps, instance, NULL );
2199 return DIENUM_CONTINUE;
2202 HRESULT dinput_device_init_device_format( IDirectInputDevice8W *iface )
2204 static const DIPROPHEADER filter =
2206 .dwSize = sizeof(filter),
2207 .dwHeaderSize = sizeof(filter),
2208 .dwHow = DIPH_DEVICE,
2210 struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
2211 DIDATAFORMAT *format = &impl->device_format;
2212 HRESULT hr;
2213 ULONG i;
2215 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_ALL, enum_objects_count, NULL );
2216 if (FAILED(hr)) return hr;
2218 if (format->dwDataSize > DEVICE_STATE_MAX_SIZE)
2220 FIXME( "unable to create device, state is too large\n" );
2221 return DIERR_OUTOFMEMORY;
2224 if (!(impl->object_properties = calloc( format->dwNumObjs, sizeof(*impl->object_properties) ))) return DIERR_OUTOFMEMORY;
2225 if (!(format->rgodf = calloc( format->dwNumObjs, sizeof(*format->rgodf) ))) return DIERR_OUTOFMEMORY;
2227 format->dwSize = sizeof(*format);
2228 format->dwObjSize = sizeof(*format->rgodf);
2229 format->dwFlags = DIDF_ABSAXIS;
2231 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_AXIS, enum_objects_init, NULL );
2232 if (FAILED(hr)) return hr;
2233 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_POV, enum_objects_init, NULL );
2234 if (FAILED(hr)) return hr;
2235 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_BUTTON, enum_objects_init, NULL );
2236 if (FAILED(hr)) return hr;
2237 hr = impl->vtbl->enum_objects( iface, &filter, DIDFT_NODATA, enum_objects_init, NULL );
2238 if (FAILED(hr)) return hr;
2240 if (TRACE_ON( dinput ))
2242 TRACE( "device format %s\n", debugstr_didataformat( format ) );
2243 for (i = 0; i < format->dwNumObjs; ++i) TRACE( " %lu: object %s\n", i, debugstr_diobjectdataformat( format->rgodf + i ) );
2246 return DI_OK;