dinput: Add tests for IDirectInput::EnumDevices and add a parameter check.
[wine] / dlls / dinput / dinput_main.c
1 /*              DirectInput
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998,1999 Lionel Ulmer
5  * Copyright 2000-2002 TransGaming Technologies Inc.
6  * Copyright 2007 Vitaliy Margolen
7  *
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 /* Status:
24  *
25  * - Tomb Raider 2 Demo:
26  *   Playable using keyboard only.
27  * - WingCommander Prophecy Demo:
28  *   Doesn't get Input Focus.
29  *
30  * - Fallout : works great in X and DGA mode
31  */
32
33 #include "config.h"
34 #include <assert.h>
35 #include <stdarg.h>
36 #include <string.h>
37
38 #define COBJMACROS
39 #define NONAMELESSUNION
40
41 #include "wine/debug.h"
42 #include "wine/unicode.h"
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winuser.h"
46 #include "winerror.h"
47 #include "objbase.h"
48 #include "rpcproxy.h"
49 #include "dinput_private.h"
50 #include "device_private.h"
51
52 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
53
54 static const IDirectInput7AVtbl ddi7avt;
55 static const IDirectInput7WVtbl ddi7wvt;
56 static const IDirectInput8AVtbl ddi8avt;
57 static const IDirectInput8WVtbl ddi8wvt;
58
59 static inline IDirectInputImpl *impl_from_IDirectInput7A( IDirectInput7A *iface )
60 {
61     return CONTAINING_RECORD( iface, IDirectInputImpl, IDirectInput7A_iface );
62 }
63
64 static inline IDirectInputImpl *impl_from_IDirectInput7W( IDirectInput7W *iface )
65 {
66     return CONTAINING_RECORD( iface, IDirectInputImpl, IDirectInput7W_iface );
67 }
68
69 static inline IDirectInputImpl *impl_from_IDirectInput8A( IDirectInput8A *iface )
70 {
71     return CONTAINING_RECORD( iface, IDirectInputImpl, IDirectInput8A_iface );
72 }
73
74 static inline IDirectInputImpl *impl_from_IDirectInput8W( IDirectInput8W *iface )
75 {
76     return CONTAINING_RECORD( iface, IDirectInputImpl, IDirectInput8W_iface );
77 }
78
79 static inline IDirectInputDeviceImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
80 {
81     return CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface);
82 }
83 static inline IDirectInputDeviceImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
84 {
85     return CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface);
86 }
87
88 static const struct dinput_device *dinput_devices[] =
89 {
90     &mouse_device,
91     &keyboard_device,
92     &joystick_linuxinput_device,
93     &joystick_linux_device,
94     &joystick_osx_device
95 };
96 #define NB_DINPUT_DEVICES (sizeof(dinput_devices)/sizeof(dinput_devices[0]))
97
98 static HINSTANCE DINPUT_instance = NULL;
99
100 BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserv)
101 {
102     switch(reason)
103     {
104       case DLL_PROCESS_ATTACH:
105         DisableThreadLibraryCalls(inst);
106         DINPUT_instance = inst;
107         break;
108       case DLL_PROCESS_DETACH:
109         break;
110     }
111     return TRUE;
112 }
113
114 static BOOL check_hook_thread(void);
115 static CRITICAL_SECTION dinput_hook_crit;
116 static struct list direct_input_list = LIST_INIT( direct_input_list );
117
118 /******************************************************************************
119  *      DirectInputCreateEx (DINPUT.@)
120  */
121 HRESULT WINAPI DirectInputCreateEx(
122         HINSTANCE hinst, DWORD dwVersion, REFIID riid, LPVOID *ppDI,
123         LPUNKNOWN punkOuter) 
124 {
125     IDirectInputImpl* This;
126
127     TRACE("(%p,%04x,%s,%p,%p)\n", hinst, dwVersion, debugstr_guid(riid), ppDI, punkOuter);
128
129     if (IsEqualGUID( &IID_IUnknown,       riid ) ||
130         IsEqualGUID( &IID_IDirectInputA,  riid ) ||
131         IsEqualGUID( &IID_IDirectInput2A, riid ) ||
132         IsEqualGUID( &IID_IDirectInput7A, riid ) ||
133         IsEqualGUID( &IID_IDirectInputW,  riid ) ||
134         IsEqualGUID( &IID_IDirectInput2W, riid ) ||
135         IsEqualGUID( &IID_IDirectInput7W, riid ) ||
136         IsEqualGUID( &IID_IDirectInput8A, riid ) ||
137         IsEqualGUID( &IID_IDirectInput8W, riid ))
138     {
139         if (!(This = HeapAlloc( GetProcessHeap(), 0, sizeof(IDirectInputImpl) )))
140             return DIERR_OUTOFMEMORY;
141     }
142     else
143         return DIERR_OLDDIRECTINPUTVERSION;
144
145     This->IDirectInput7A_iface.lpVtbl = &ddi7avt;
146     This->IDirectInput7W_iface.lpVtbl = &ddi7wvt;
147     This->IDirectInput8A_iface.lpVtbl = &ddi8avt;
148     This->IDirectInput8W_iface.lpVtbl = &ddi8wvt;
149     This->ref         = 0;
150     This->dwVersion   = dwVersion;
151     This->evsequence  = 1;
152
153     InitializeCriticalSection(&This->crit);
154     This->crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IDirectInputImpl*->crit");
155
156     list_init( &This->devices_list );
157
158     /* Add self to the list of the IDirectInputs */
159     EnterCriticalSection( &dinput_hook_crit );
160     list_add_head( &direct_input_list, &This->entry );
161     LeaveCriticalSection( &dinput_hook_crit );
162
163     if (!check_hook_thread())
164     {
165         IUnknown_Release( &This->IDirectInput7A_iface );
166         return DIERR_GENERIC;
167     }
168
169     IDirectInput_QueryInterface( &This->IDirectInput7A_iface, riid, ppDI );
170     return DI_OK;
171 }
172
173 /******************************************************************************
174  *      DirectInputCreateA (DINPUT.@)
175  */
176 HRESULT WINAPI DECLSPEC_HOTPATCH DirectInputCreateA(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTA *ppDI, LPUNKNOWN punkOuter)
177 {
178     return DirectInputCreateEx(hinst, dwVersion, &IID_IDirectInput7A, (LPVOID *)ppDI, punkOuter);
179 }
180
181 /******************************************************************************
182  *      DirectInputCreateW (DINPUT.@)
183  */
184 HRESULT WINAPI DECLSPEC_HOTPATCH DirectInputCreateW(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTW *ppDI, LPUNKNOWN punkOuter)
185 {
186     return DirectInputCreateEx(hinst, dwVersion, &IID_IDirectInput7W, (LPVOID *)ppDI, punkOuter);
187 }
188
189 static const char *_dump_DIDEVTYPE_value(DWORD dwDevType) {
190     switch (dwDevType) {
191         case 0: return "All devices";
192         case DIDEVTYPE_MOUSE: return "DIDEVTYPE_MOUSE";
193         case DIDEVTYPE_KEYBOARD: return "DIDEVTYPE_KEYBOARD";
194         case DIDEVTYPE_JOYSTICK: return "DIDEVTYPE_JOYSTICK";
195         case DIDEVTYPE_DEVICE: return "DIDEVTYPE_DEVICE";
196         default: return "Unknown";
197     }
198 }
199
200 static void _dump_EnumDevices_dwFlags(DWORD dwFlags) {
201     if (TRACE_ON(dinput)) {
202         unsigned int   i;
203         static const struct {
204             DWORD       mask;
205             const char  *name;
206         } flags[] = {
207 #define FE(x) { x, #x}
208             FE(DIEDFL_ALLDEVICES),
209             FE(DIEDFL_ATTACHEDONLY),
210             FE(DIEDFL_FORCEFEEDBACK),
211             FE(DIEDFL_INCLUDEALIASES),
212             FE(DIEDFL_INCLUDEPHANTOMS)
213 #undef FE
214         };
215         TRACE(" flags: ");
216         if (dwFlags == 0) {
217             TRACE("DIEDFL_ALLDEVICES\n");
218             return;
219         }
220         for (i = 0; i < (sizeof(flags) / sizeof(flags[0])); i++)
221             if (flags[i].mask & dwFlags)
222                 TRACE("%s ",flags[i].name);
223     }
224     TRACE("\n");
225 }
226
227 void _dump_diactionformatA(LPDIACTIONFORMATA lpdiActionFormat) {
228     unsigned int i;
229
230     FIXME("diaf.dwSize = %d\n", lpdiActionFormat->dwSize);
231     FIXME("diaf.dwActionSize = %d\n", lpdiActionFormat->dwActionSize);
232     FIXME("diaf.dwDataSize = %d\n", lpdiActionFormat->dwDataSize);
233     FIXME("diaf.dwNumActions = %d\n", lpdiActionFormat->dwNumActions);
234     FIXME("diaf.rgoAction = %p\n", lpdiActionFormat->rgoAction);
235     FIXME("diaf.guidActionMap = %s\n", debugstr_guid(&lpdiActionFormat->guidActionMap));
236     FIXME("diaf.dwGenre = 0x%08x\n", lpdiActionFormat->dwGenre);
237     FIXME("diaf.dwBufferSize = %d\n", lpdiActionFormat->dwBufferSize);
238     FIXME("diaf.lAxisMin = %d\n", lpdiActionFormat->lAxisMin);
239     FIXME("diaf.lAxisMax = %d\n", lpdiActionFormat->lAxisMax);
240     FIXME("diaf.hInstString = %p\n", lpdiActionFormat->hInstString);
241     FIXME("diaf.ftTimeStamp ...\n");
242     FIXME("diaf.dwCRC = 0x%x\n", lpdiActionFormat->dwCRC);
243     FIXME("diaf.tszActionMap = %s\n", debugstr_a(lpdiActionFormat->tszActionMap));
244     for (i = 0; i < lpdiActionFormat->dwNumActions; i++)
245     {
246         FIXME("diaf.rgoAction[%u]:\n", i);
247         FIXME("\tuAppData=0x%lx\n", lpdiActionFormat->rgoAction[i].uAppData);
248         FIXME("\tdwSemantic=0x%08x\n", lpdiActionFormat->rgoAction[i].dwSemantic);
249         FIXME("\tdwFlags=0x%x\n", lpdiActionFormat->rgoAction[i].dwFlags);
250         FIXME("\tszActionName=%s\n", debugstr_a(lpdiActionFormat->rgoAction[i].u.lptszActionName));
251         FIXME("\tguidInstance=%s\n", debugstr_guid(&lpdiActionFormat->rgoAction[i].guidInstance));
252         FIXME("\tdwObjID=0x%x\n", lpdiActionFormat->rgoAction[i].dwObjID);
253         FIXME("\tdwHow=0x%x\n", lpdiActionFormat->rgoAction[i].dwHow);
254     }
255 }
256
257 void _copy_diactionformatAtoW(LPDIACTIONFORMATW to, LPDIACTIONFORMATA from)
258 {
259     int i;
260
261     to->dwSize = sizeof(DIACTIONFORMATW);
262     to->dwActionSize = sizeof(DIACTIONW);
263     to->dwDataSize = from->dwDataSize;
264     to->dwNumActions = from->dwNumActions;
265     to->guidActionMap = from->guidActionMap;
266     to->dwGenre = from->dwGenre;
267     to->dwBufferSize = from->dwBufferSize;
268     to->lAxisMin = from->lAxisMin;
269     to->lAxisMax = from->lAxisMax;
270     to->dwCRC = from->dwCRC;
271     to->ftTimeStamp = from->ftTimeStamp;
272
273     for (i=0; i < to->dwNumActions; i++)
274     {
275         to->rgoAction[i].uAppData = from->rgoAction[i].uAppData;
276         to->rgoAction[i].dwSemantic = from->rgoAction[i].dwSemantic;
277         to->rgoAction[i].dwFlags = from->rgoAction[i].dwFlags;
278         to->rgoAction[i].guidInstance = from->rgoAction[i].guidInstance;
279         to->rgoAction[i].dwObjID = from->rgoAction[i].dwObjID;
280         to->rgoAction[i].dwHow = from->rgoAction[i].dwHow;
281     }
282 }
283
284 void _copy_diactionformatWtoA(LPDIACTIONFORMATA to, LPDIACTIONFORMATW from)
285 {
286     int i;
287
288     to->dwSize = sizeof(DIACTIONFORMATA);
289     to->dwActionSize = sizeof(DIACTIONA);
290     to->dwDataSize = from->dwDataSize;
291     to->dwNumActions = from->dwNumActions;
292     to->guidActionMap = from->guidActionMap;
293     to->dwGenre = from->dwGenre;
294     to->dwBufferSize = from->dwBufferSize;
295     to->lAxisMin = from->lAxisMin;
296     to->lAxisMax = from->lAxisMax;
297     to->dwCRC = from->dwCRC;
298     to->ftTimeStamp = from->ftTimeStamp;
299
300     for (i=0; i < to->dwNumActions; i++)
301     {
302         to->rgoAction[i].uAppData = from->rgoAction[i].uAppData;
303         to->rgoAction[i].dwSemantic = from->rgoAction[i].dwSemantic;
304         to->rgoAction[i].dwFlags = from->rgoAction[i].dwFlags;
305         to->rgoAction[i].guidInstance = from->rgoAction[i].guidInstance;
306         to->rgoAction[i].dwObjID = from->rgoAction[i].dwObjID;
307         to->rgoAction[i].dwHow = from->rgoAction[i].dwHow;
308     }
309 }
310
311 /******************************************************************************
312  *      IDirectInputA_EnumDevices
313  */
314 static HRESULT WINAPI IDirectInputAImpl_EnumDevices(
315         LPDIRECTINPUT7A iface, DWORD dwDevType, LPDIENUMDEVICESCALLBACKA lpCallback,
316         LPVOID pvRef, DWORD dwFlags)
317 {
318     IDirectInputImpl *This = impl_from_IDirectInput7A(iface);
319     DIDEVICEINSTANCEA devInstance;
320     unsigned int i;
321     int j, r;
322
323     TRACE("(this=%p,0x%04x '%s',%p,%p,%04x)\n",
324           This, dwDevType, _dump_DIDEVTYPE_value(dwDevType),
325           lpCallback, pvRef, dwFlags);
326     _dump_EnumDevices_dwFlags(dwFlags);
327
328     if (!lpCallback)
329         return DIERR_INVALIDPARAM;
330
331     for (i = 0; i < NB_DINPUT_DEVICES; i++) {
332         if (!dinput_devices[i]->enum_deviceA) continue;
333         for (j = 0, r = -1; r != 0; j++) {
334             devInstance.dwSize = sizeof(devInstance);
335             TRACE("  - checking device %u ('%s')\n", i, dinput_devices[i]->name);
336             if ((r = dinput_devices[i]->enum_deviceA(dwDevType, dwFlags, &devInstance, This->dwVersion, j))) {
337                 if (lpCallback(&devInstance,pvRef) == DIENUM_STOP)
338                     return 0;
339             }
340         }
341     }
342     
343     return 0;
344 }
345 /******************************************************************************
346  *      IDirectInputW_EnumDevices
347  */
348 static HRESULT WINAPI IDirectInputWImpl_EnumDevices(
349         LPDIRECTINPUT7W iface, DWORD dwDevType, LPDIENUMDEVICESCALLBACKW lpCallback,
350         LPVOID pvRef, DWORD dwFlags) 
351 {
352     IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
353     DIDEVICEINSTANCEW devInstance;
354     unsigned int i;
355     int j, r;
356
357     TRACE("(this=%p,0x%04x '%s',%p,%p,%04x)\n",
358           This, dwDevType, _dump_DIDEVTYPE_value(dwDevType),
359           lpCallback, pvRef, dwFlags);
360     _dump_EnumDevices_dwFlags(dwFlags);
361
362     if (!lpCallback)
363         return DIERR_INVALIDPARAM;
364
365     for (i = 0; i < NB_DINPUT_DEVICES; i++) {
366         if (!dinput_devices[i]->enum_deviceW) continue;
367         for (j = 0, r = -1; r != 0; j++) {
368             devInstance.dwSize = sizeof(devInstance);
369             TRACE("  - checking device %u ('%s')\n", i, dinput_devices[i]->name);
370             if ((r = dinput_devices[i]->enum_deviceW(dwDevType, dwFlags, &devInstance, This->dwVersion, j))) {
371                 if (lpCallback(&devInstance,pvRef) == DIENUM_STOP)
372                     return 0;
373             }
374         }
375     }
376     
377     return 0;
378 }
379
380 static ULONG WINAPI IDirectInputAImpl_AddRef(LPDIRECTINPUT7A iface)
381 {
382     IDirectInputImpl *This = impl_from_IDirectInput7A( iface );
383     ULONG ref = InterlockedIncrement(&This->ref);
384
385     TRACE( "(%p) incrementing from %d\n", This, ref - 1);
386     return ref;
387 }
388
389 static ULONG WINAPI IDirectInputWImpl_AddRef(LPDIRECTINPUT7W iface)
390 {
391     IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
392     return IDirectInputAImpl_AddRef( &This->IDirectInput7A_iface );
393 }
394
395 static ULONG WINAPI IDirectInputAImpl_Release(LPDIRECTINPUT7A iface)
396 {
397     IDirectInputImpl *This = impl_from_IDirectInput7A( iface );
398     ULONG ref = InterlockedDecrement( &This->ref );
399
400     TRACE( "(%p) releasing from %d\n", This, ref + 1 );
401
402     if (ref) return ref;
403
404     /* Remove self from the list of the IDirectInputs */
405     EnterCriticalSection( &dinput_hook_crit );
406     list_remove( &This->entry );
407     LeaveCriticalSection( &dinput_hook_crit );
408
409     check_hook_thread();
410
411     This->crit.DebugInfo->Spare[0] = 0;
412     DeleteCriticalSection( &This->crit );
413     HeapFree( GetProcessHeap(), 0, This );
414
415     return 0;
416 }
417
418 static ULONG WINAPI IDirectInputWImpl_Release(LPDIRECTINPUT7W iface)
419 {
420     IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
421     return IDirectInputAImpl_Release( &This->IDirectInput7A_iface );
422 }
423
424 static HRESULT WINAPI IDirectInputAImpl_QueryInterface(LPDIRECTINPUT7A iface, REFIID riid, LPVOID *ppobj)
425 {
426     IDirectInputImpl *This = impl_from_IDirectInput7A( iface );
427
428     TRACE( "(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppobj );
429
430     if (!riid || !ppobj)
431         return E_POINTER;
432
433     if (IsEqualGUID( &IID_IUnknown, riid ) ||
434         IsEqualGUID( &IID_IDirectInputA,  riid ) ||
435         IsEqualGUID( &IID_IDirectInput2A, riid ) ||
436         IsEqualGUID( &IID_IDirectInput7A, riid ))
437     {
438         *ppobj = &This->IDirectInput7A_iface;
439         IUnknown_AddRef( (IUnknown*)*ppobj );
440
441         return DI_OK;
442     }
443
444     if (IsEqualGUID( &IID_IDirectInputW,  riid ) ||
445         IsEqualGUID( &IID_IDirectInput2W, riid ) ||
446         IsEqualGUID( &IID_IDirectInput7W, riid ))
447     {
448         *ppobj = &This->IDirectInput7W_iface;
449         IUnknown_AddRef( (IUnknown*)*ppobj );
450
451         return DI_OK;
452     }
453
454     if (IsEqualGUID( &IID_IDirectInput8A, riid ))
455     {
456         *ppobj = &This->IDirectInput8A_iface;
457         IUnknown_AddRef( (IUnknown*)*ppobj );
458
459         return DI_OK;
460     }
461
462     if (IsEqualGUID( &IID_IDirectInput8W, riid ))
463     {
464         *ppobj = &This->IDirectInput8W_iface;
465         IUnknown_AddRef( (IUnknown*)*ppobj );
466
467         return DI_OK;
468     }
469
470     FIXME( "Unsupported interface: %s\n", debugstr_guid(riid));
471     *ppobj = NULL;
472     return E_NOINTERFACE;
473 }
474
475 static HRESULT WINAPI IDirectInputWImpl_QueryInterface(LPDIRECTINPUT7W iface, REFIID riid, LPVOID *ppobj)
476 {
477     IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
478     return IDirectInputAImpl_QueryInterface( &This->IDirectInput7A_iface, riid, ppobj );
479 }
480
481 enum directinput_versions
482 {
483     DIRECTINPUT_VERSION_300 = 0x0300,
484     DIRECTINPUT_VERSION_500 = 0x0500,
485     DIRECTINPUT_VERSION_50A = 0x050A,
486     DIRECTINPUT_VERSION_5B2 = 0x05B2,
487     DIRECTINPUT_VERSION_602 = 0x0602,
488     DIRECTINPUT_VERSION_61A = 0x061A,
489     DIRECTINPUT_VERSION_700 = 0x0700,
490 };
491
492 static HRESULT WINAPI IDirectInputAImpl_Initialize(LPDIRECTINPUT7A iface, HINSTANCE hinst, DWORD version)
493 {
494     TRACE("(%p)->(%p, 0x%04x)\n", iface, hinst, version);
495
496     if (!hinst)
497         return DIERR_INVALIDPARAM;
498     else if (version == 0)
499         return DIERR_NOTINITIALIZED;
500     /* We need to accept version 8, even though native rejects it. */
501     else if (version > DIRECTINPUT_VERSION_700 && version != DIRECTINPUT_VERSION)
502         return DIERR_OLDDIRECTINPUTVERSION;
503     else if (version != DIRECTINPUT_VERSION_300 && version != DIRECTINPUT_VERSION_500 &&
504              version != DIRECTINPUT_VERSION_50A && version != DIRECTINPUT_VERSION_5B2 &&
505              version != DIRECTINPUT_VERSION_602 && version != DIRECTINPUT_VERSION_61A &&
506              version != DIRECTINPUT_VERSION_700 && version != DIRECTINPUT_VERSION)
507         return DIERR_BETADIRECTINPUTVERSION;
508
509     return DI_OK;
510 }
511
512 static HRESULT WINAPI IDirectInputWImpl_Initialize(LPDIRECTINPUT7W iface, HINSTANCE hinst, DWORD x)
513 {
514     IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
515     return IDirectInputAImpl_Initialize( &This->IDirectInput7A_iface, hinst, x );
516 }
517
518 static HRESULT WINAPI IDirectInputAImpl_GetDeviceStatus(LPDIRECTINPUT7A iface, REFGUID rguid)
519 {
520     IDirectInputImpl *This = impl_from_IDirectInput7A( iface );
521     HRESULT hr;
522     LPDIRECTINPUTDEVICEA device;
523
524     TRACE( "(%p)->(%s)\n", This, debugstr_guid(rguid) );
525
526     hr = IDirectInput_CreateDevice( iface, rguid, &device, NULL );
527     if (hr != DI_OK) return DI_NOTATTACHED;
528
529     IUnknown_Release( device );
530
531     return DI_OK;
532 }
533
534 static HRESULT WINAPI IDirectInputWImpl_GetDeviceStatus(LPDIRECTINPUT7W iface, REFGUID rguid)
535 {
536     IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
537     return IDirectInputAImpl_GetDeviceStatus( &This->IDirectInput7A_iface, rguid );
538 }
539
540 static HRESULT WINAPI IDirectInputAImpl_RunControlPanel(LPDIRECTINPUT7A iface,
541                                                         HWND hwndOwner,
542                                                         DWORD dwFlags)
543 {
544     WCHAR control_exeW[] = {'c','o','n','t','r','o','l','.','e','x','e',0};
545     STARTUPINFOW si = {0};
546     PROCESS_INFORMATION pi;
547
548     IDirectInputImpl *This = impl_from_IDirectInput7A( iface );
549
550     TRACE( "(%p)->(%p, %08x)\n", This, hwndOwner, dwFlags );
551
552     if (hwndOwner && !IsWindow(hwndOwner))
553         return E_HANDLE;
554
555     if (dwFlags)
556         return DIERR_INVALIDPARAM;
557
558     if (!CreateProcessW(NULL, control_exeW, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
559         return HRESULT_FROM_WIN32(GetLastError());
560
561     return DI_OK;
562 }
563
564 static HRESULT WINAPI IDirectInputWImpl_RunControlPanel(LPDIRECTINPUT7W iface, HWND hwndOwner, DWORD dwFlags)
565 {
566     IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
567     return IDirectInputAImpl_RunControlPanel( &This->IDirectInput7A_iface, hwndOwner, dwFlags );
568 }
569
570 static HRESULT WINAPI IDirectInput2AImpl_FindDevice(LPDIRECTINPUT7A iface, REFGUID rguid,
571                                                     LPCSTR pszName, LPGUID pguidInstance)
572 {
573     IDirectInputImpl *This = impl_from_IDirectInput7A( iface );
574
575     FIXME( "(%p)->(%s, %s, %p): stub\n", This, debugstr_guid(rguid), pszName, pguidInstance );
576
577     return DI_OK;
578 }
579
580 static HRESULT WINAPI IDirectInput2WImpl_FindDevice(LPDIRECTINPUT7W iface, REFGUID rguid,
581                                                     LPCWSTR pszName, LPGUID pguidInstance)
582 {
583     IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
584
585     FIXME( "(%p)->(%s, %s, %p): stub\n", This, debugstr_guid(rguid), debugstr_w(pszName), pguidInstance );
586
587     return DI_OK;
588 }
589
590 static HRESULT create_device(IDirectInputImpl *This, REFGUID rguid, REFIID riid, LPVOID *pvOut, BOOL unicode)
591 {
592     unsigned int i;
593
594     if (pvOut)
595         *pvOut = NULL;
596
597     if (!rguid || !pvOut)
598         return E_POINTER;
599
600     /* Loop on all the devices to see if anyone matches the given GUID */
601     for (i = 0; i < NB_DINPUT_DEVICES; i++)
602     {
603         HRESULT ret;
604
605         if (!dinput_devices[i]->create_device) continue;
606         if ((ret = dinput_devices[i]->create_device(This, rguid, riid, pvOut, unicode)) == DI_OK)
607             return DI_OK;
608     }
609
610     WARN("invalid device GUID %s\n", debugstr_guid(rguid));
611     return DIERR_DEVICENOTREG;
612 }
613
614 static HRESULT WINAPI IDirectInput7AImpl_CreateDeviceEx(LPDIRECTINPUT7A iface, REFGUID rguid,
615                                                         REFIID riid, LPVOID* pvOut, LPUNKNOWN lpUnknownOuter)
616 {
617     IDirectInputImpl *This = impl_from_IDirectInput7A( iface );
618
619     TRACE("(%p)->(%s, %s, %p, %p)\n", This, debugstr_guid(rguid), debugstr_guid(riid), pvOut, lpUnknownOuter);
620
621     return create_device(This, rguid, riid, pvOut, FALSE);
622 }
623
624 static HRESULT WINAPI IDirectInput7WImpl_CreateDeviceEx(LPDIRECTINPUT7W iface, REFGUID rguid,
625                                                         REFIID riid, LPVOID* pvOut, LPUNKNOWN lpUnknownOuter)
626 {
627     IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
628
629     TRACE("(%p)->(%s, %s, %p, %p)\n", This, debugstr_guid(rguid), debugstr_guid(riid), pvOut, lpUnknownOuter);
630
631     return create_device(This, rguid, riid, pvOut, TRUE);
632 }
633
634 static HRESULT WINAPI IDirectInputAImpl_CreateDevice(LPDIRECTINPUT7A iface, REFGUID rguid,
635                                                      LPDIRECTINPUTDEVICEA* pdev, LPUNKNOWN punk)
636 {
637     return IDirectInput7AImpl_CreateDeviceEx(iface, rguid, NULL, (LPVOID*)pdev, punk);
638 }
639
640 static HRESULT WINAPI IDirectInputWImpl_CreateDevice(LPDIRECTINPUT7W iface, REFGUID rguid,
641                                                      LPDIRECTINPUTDEVICEW* pdev, LPUNKNOWN punk)
642 {
643     return IDirectInput7WImpl_CreateDeviceEx(iface, rguid, NULL, (LPVOID*)pdev, punk);
644 }
645
646 /*******************************************************************************
647  *      DirectInput8
648  */
649
650 static ULONG WINAPI IDirectInput8AImpl_AddRef(LPDIRECTINPUT8A iface)
651 {
652     IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
653     return IDirectInputAImpl_AddRef( &This->IDirectInput7A_iface );
654 }
655
656 static ULONG WINAPI IDirectInput8WImpl_AddRef(LPDIRECTINPUT8W iface)
657 {
658     IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
659     return IDirectInputAImpl_AddRef( &This->IDirectInput7A_iface );
660 }
661
662 static HRESULT WINAPI IDirectInput8AImpl_QueryInterface(LPDIRECTINPUT8A iface, REFIID riid, LPVOID *ppobj)
663 {
664     IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
665     return IDirectInputAImpl_QueryInterface( &This->IDirectInput7A_iface, riid, ppobj );
666 }
667
668 static HRESULT WINAPI IDirectInput8WImpl_QueryInterface(LPDIRECTINPUT8W iface, REFIID riid, LPVOID *ppobj)
669 {
670     IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
671     return IDirectInputAImpl_QueryInterface( &This->IDirectInput7A_iface, riid, ppobj );
672 }
673
674 static ULONG WINAPI IDirectInput8AImpl_Release(LPDIRECTINPUT8A iface)
675 {
676     IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
677     return IDirectInputAImpl_Release( &This->IDirectInput7A_iface );
678 }
679
680 static ULONG WINAPI IDirectInput8WImpl_Release(LPDIRECTINPUT8W iface)
681 {
682     IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
683     return IDirectInputAImpl_Release( &This->IDirectInput7A_iface );
684 }
685
686 static HRESULT WINAPI IDirectInput8AImpl_CreateDevice(LPDIRECTINPUT8A iface, REFGUID rguid,
687                                                       LPDIRECTINPUTDEVICE8A* pdev, LPUNKNOWN punk)
688 {
689     IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
690     return IDirectInput7AImpl_CreateDeviceEx( &This->IDirectInput7A_iface, rguid, NULL, (LPVOID*)pdev, punk );
691 }
692
693 static HRESULT WINAPI IDirectInput8WImpl_CreateDevice(LPDIRECTINPUT8W iface, REFGUID rguid,
694                                                       LPDIRECTINPUTDEVICE8W* pdev, LPUNKNOWN punk)
695 {
696     IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
697     return IDirectInput7WImpl_CreateDeviceEx( &This->IDirectInput7W_iface, rguid, NULL, (LPVOID*)pdev, punk );
698 }
699
700 static HRESULT WINAPI IDirectInput8AImpl_EnumDevices(LPDIRECTINPUT8A iface, DWORD dwDevType, LPDIENUMDEVICESCALLBACKA lpCallback,
701                                                      LPVOID pvRef, DWORD dwFlags)
702 {
703     IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
704     return IDirectInputAImpl_EnumDevices( &This->IDirectInput7A_iface, dwDevType, lpCallback, pvRef, dwFlags );
705 }
706
707 static HRESULT WINAPI IDirectInput8WImpl_EnumDevices(LPDIRECTINPUT8W iface, DWORD dwDevType, LPDIENUMDEVICESCALLBACKW lpCallback,
708                                                      LPVOID pvRef, DWORD dwFlags)
709 {
710     IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
711     return IDirectInputWImpl_EnumDevices( &This->IDirectInput7W_iface, dwDevType, lpCallback, pvRef, dwFlags );
712 }
713
714 static HRESULT WINAPI IDirectInput8AImpl_GetDeviceStatus(LPDIRECTINPUT8A iface, REFGUID rguid)
715 {
716     IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
717     return IDirectInputAImpl_GetDeviceStatus( &This->IDirectInput7A_iface, rguid );
718 }
719
720 static HRESULT WINAPI IDirectInput8WImpl_GetDeviceStatus(LPDIRECTINPUT8W iface, REFGUID rguid)
721 {
722     IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
723     return IDirectInputAImpl_GetDeviceStatus( &This->IDirectInput7A_iface, rguid );
724 }
725
726 static HRESULT WINAPI IDirectInput8AImpl_RunControlPanel(LPDIRECTINPUT8A iface, HWND hwndOwner, DWORD dwFlags)
727 {
728     IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
729     return IDirectInputAImpl_RunControlPanel( &This->IDirectInput7A_iface, hwndOwner, dwFlags );
730 }
731
732 static HRESULT WINAPI IDirectInput8WImpl_RunControlPanel(LPDIRECTINPUT8W iface, HWND hwndOwner, DWORD dwFlags)
733 {
734     IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
735     return IDirectInputAImpl_RunControlPanel( &This->IDirectInput7A_iface, hwndOwner, dwFlags );
736 }
737
738 static HRESULT WINAPI IDirectInput8AImpl_Initialize(LPDIRECTINPUT8A iface, HINSTANCE hinst, DWORD x)
739 {
740     IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
741     return IDirectInputAImpl_Initialize( &This->IDirectInput7A_iface, hinst, x );
742 }
743
744 static HRESULT WINAPI IDirectInput8WImpl_Initialize(LPDIRECTINPUT8W iface, HINSTANCE hinst, DWORD x)
745 {
746     IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
747     return IDirectInputAImpl_Initialize( &This->IDirectInput7A_iface, hinst, x );
748 }
749
750 static HRESULT WINAPI IDirectInput8AImpl_FindDevice(LPDIRECTINPUT8A iface, REFGUID rguid, LPCSTR pszName, LPGUID pguidInstance)
751 {
752     IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
753     return IDirectInput2AImpl_FindDevice( &This->IDirectInput7A_iface, rguid, pszName, pguidInstance );
754 }
755
756 static HRESULT WINAPI IDirectInput8WImpl_FindDevice(LPDIRECTINPUT8W iface, REFGUID rguid, LPCWSTR pszName, LPGUID pguidInstance)
757 {
758     IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
759     return IDirectInput2WImpl_FindDevice( &This->IDirectInput7W_iface, rguid, pszName, pguidInstance );
760 }
761
762 static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics(
763       LPDIRECTINPUT8A iface, LPCSTR ptszUserName, LPDIACTIONFORMATA lpdiActionFormat,
764       LPDIENUMDEVICESBYSEMANTICSCBA lpCallback,
765       LPVOID pvRef, DWORD dwFlags
766 )
767 {
768     IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
769     DIDEVICEINSTANCEA didevi;
770     LPDIRECTINPUTDEVICE8A lpdid;
771     BOOL ret;
772     DWORD callbackFlags = 0;
773     int i;
774
775     FIXME("(this=%p,%s,%p,%p,%p,%04x): semi-stub\n", This, ptszUserName, lpdiActionFormat,
776           lpCallback, pvRef, dwFlags);
777 #define X(x) if (dwFlags & x) FIXME("\tdwFlags |= "#x"\n");
778         X(DIEDBSFL_ATTACHEDONLY)
779         X(DIEDBSFL_THISUSER)
780         X(DIEDBSFL_FORCEFEEDBACK)
781         X(DIEDBSFL_AVAILABLEDEVICES)
782         X(DIEDBSFL_MULTIMICEKEYBOARDS)
783         X(DIEDBSFL_NONGAMINGDEVICES)
784 #undef X
785
786     _dump_diactionformatA(lpdiActionFormat);
787
788     didevi.dwSize = sizeof(didevi);
789
790     /* enum the keyboard first */
791     IDirectInput_CreateDevice(iface, &GUID_SysKeyboard, &lpdid, NULL);
792     IDirectInputDevice_GetDeviceInfo(lpdid, &didevi);
793     /* if there's any DIKEYBOARD action, keyboard is priority 1*/
794     for(i=0; i < lpdiActionFormat->dwActionSize; i++)
795         if ((lpdiActionFormat->rgoAction[i].dwSemantic & DIKEYBOARD_MASK) == DIKEYBOARD_MASK)
796             callbackFlags |= DIEDBS_MAPPEDPRI1;
797
798     ret = lpCallback(&didevi, lpdid, callbackFlags, 1, pvRef);
799
800     if (ret == DIENUM_STOP) return DI_OK;
801
802     callbackFlags = 0;
803
804     /* and then the mouse */
805     IDirectInput_CreateDevice(iface, &GUID_SysMouse, &lpdid, NULL);
806     IDirectInputDevice_GetDeviceInfo(lpdid, &didevi);
807     /* same as above, but for the mouse */
808     for(i=0; i < lpdiActionFormat->dwActionSize; i++)
809         if ((lpdiActionFormat->rgoAction[i].dwSemantic & DIMOUSE_MASK) == DIMOUSE_MASK)
810             callbackFlags |= DIEDBS_MAPPEDPRI1;
811
812     lpCallback(&didevi, lpdid, callbackFlags, 0, pvRef);
813
814     return DI_OK;
815 }
816
817 static HRESULT WINAPI IDirectInput8WImpl_EnumDevicesBySemantics(
818       LPDIRECTINPUT8W iface, LPCWSTR ptszUserName, LPDIACTIONFORMATW lpdiActionFormat,
819       LPDIENUMDEVICESBYSEMANTICSCBW lpCallback,
820       LPVOID pvRef, DWORD dwFlags
821 )
822 {
823     IDirectInputImpl *This = impl_from_IDirectInput8W(iface);
824     DIDEVICEINSTANCEW didevi;
825     LPDIRECTINPUTDEVICE8W lpdid;
826     BOOL ret;
827     DWORD callbackFlags = 0;
828     int i;
829
830     FIXME("(this=%p,%s,%p,%p,%p,%04x): semi-stub\n", This, debugstr_w(ptszUserName), lpdiActionFormat,
831           lpCallback, pvRef, dwFlags);
832
833     didevi.dwSize = sizeof(didevi);
834
835     /* enum the keyboard first */
836     IDirectInput_CreateDevice(iface, &GUID_SysKeyboard, &lpdid, NULL);
837     IDirectInputDevice_GetDeviceInfo(lpdid, &didevi);
838     /* if there's any DIKEYBOARD action, keyboard is priority 1*/
839     for(i=0; i < lpdiActionFormat->dwActionSize; i++)
840         if ((lpdiActionFormat->rgoAction[i].dwSemantic & DIKEYBOARD_MASK) == DIKEYBOARD_MASK)
841             callbackFlags |= DIEDBS_MAPPEDPRI1;
842
843     ret = lpCallback(&didevi, lpdid, callbackFlags, 1, pvRef);
844
845     if (ret == DIENUM_STOP) return DI_OK;
846
847     callbackFlags = 0;
848
849     /* and then the mouse */
850     IDirectInput_CreateDevice(iface, &GUID_SysMouse, &lpdid, NULL);
851     IDirectInputDevice_GetDeviceInfo(lpdid, &didevi);
852     /* same as above, but for the mouse */
853     for(i=0; i < lpdiActionFormat->dwActionSize; i++)
854         if ((lpdiActionFormat->rgoAction[i].dwSemantic & DIMOUSE_MASK) == DIMOUSE_MASK)
855             callbackFlags |= DIEDBS_MAPPEDPRI1;
856
857     lpCallback(&didevi, lpdid, callbackFlags, 0, pvRef);
858
859     return DI_OK;
860 }
861
862 static HRESULT WINAPI IDirectInput8AImpl_ConfigureDevices(
863       LPDIRECTINPUT8A iface, LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
864       LPDICONFIGUREDEVICESPARAMSA lpdiCDParams, DWORD dwFlags, LPVOID pvRefData
865 )
866 {
867       IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
868
869       FIXME("(this=%p,%p,%p,%04x,%p): stub\n", This, lpdiCallback, lpdiCDParams,
870             dwFlags, pvRefData);
871       return 0;
872 }
873
874 static HRESULT WINAPI IDirectInput8WImpl_ConfigureDevices(
875       LPDIRECTINPUT8W iface, LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
876       LPDICONFIGUREDEVICESPARAMSW lpdiCDParams, DWORD dwFlags, LPVOID pvRefData
877 )
878 {
879       IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
880
881       FIXME("(this=%p,%p,%p,%04x,%p): stub\n", This, lpdiCallback, lpdiCDParams,
882             dwFlags, pvRefData);
883       return 0;
884 }
885
886 static const IDirectInput7AVtbl ddi7avt = {
887     IDirectInputAImpl_QueryInterface,
888     IDirectInputAImpl_AddRef,
889     IDirectInputAImpl_Release,
890     IDirectInputAImpl_CreateDevice,
891     IDirectInputAImpl_EnumDevices,
892     IDirectInputAImpl_GetDeviceStatus,
893     IDirectInputAImpl_RunControlPanel,
894     IDirectInputAImpl_Initialize,
895     IDirectInput2AImpl_FindDevice,
896     IDirectInput7AImpl_CreateDeviceEx
897 };
898
899 static const IDirectInput7WVtbl ddi7wvt = {
900     IDirectInputWImpl_QueryInterface,
901     IDirectInputWImpl_AddRef,
902     IDirectInputWImpl_Release,
903     IDirectInputWImpl_CreateDevice,
904     IDirectInputWImpl_EnumDevices,
905     IDirectInputWImpl_GetDeviceStatus,
906     IDirectInputWImpl_RunControlPanel,
907     IDirectInputWImpl_Initialize,
908     IDirectInput2WImpl_FindDevice,
909     IDirectInput7WImpl_CreateDeviceEx
910 };
911
912 static const IDirectInput8AVtbl ddi8avt = {
913     IDirectInput8AImpl_QueryInterface,
914     IDirectInput8AImpl_AddRef,
915     IDirectInput8AImpl_Release,
916     IDirectInput8AImpl_CreateDevice,
917     IDirectInput8AImpl_EnumDevices,
918     IDirectInput8AImpl_GetDeviceStatus,
919     IDirectInput8AImpl_RunControlPanel,
920     IDirectInput8AImpl_Initialize,
921     IDirectInput8AImpl_FindDevice,
922     IDirectInput8AImpl_EnumDevicesBySemantics,
923     IDirectInput8AImpl_ConfigureDevices
924 };
925
926 static const IDirectInput8WVtbl ddi8wvt = {
927     IDirectInput8WImpl_QueryInterface,
928     IDirectInput8WImpl_AddRef,
929     IDirectInput8WImpl_Release,
930     IDirectInput8WImpl_CreateDevice,
931     IDirectInput8WImpl_EnumDevices,
932     IDirectInput8WImpl_GetDeviceStatus,
933     IDirectInput8WImpl_RunControlPanel,
934     IDirectInput8WImpl_Initialize,
935     IDirectInput8WImpl_FindDevice,
936     IDirectInput8WImpl_EnumDevicesBySemantics,
937     IDirectInput8WImpl_ConfigureDevices
938 };
939
940 /*******************************************************************************
941  * DirectInput ClassFactory
942  */
943 typedef struct
944 {
945     /* IUnknown fields */
946     IClassFactory IClassFactory_iface;
947     LONG          ref;
948 } IClassFactoryImpl;
949
950 static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface)
951 {
952         return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface);
953 }
954
955 static HRESULT WINAPI DICF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
956         IClassFactoryImpl *This = impl_from_IClassFactory(iface);
957
958         FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
959         return E_NOINTERFACE;
960 }
961
962 static ULONG WINAPI DICF_AddRef(LPCLASSFACTORY iface) {
963         IClassFactoryImpl *This = impl_from_IClassFactory(iface);
964         return InterlockedIncrement(&(This->ref));
965 }
966
967 static ULONG WINAPI DICF_Release(LPCLASSFACTORY iface) {
968         IClassFactoryImpl *This = impl_from_IClassFactory(iface);
969         /* static class, won't be  freed */
970         return InterlockedDecrement(&(This->ref));
971 }
972
973 static HRESULT WINAPI DICF_CreateInstance(
974         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
975 ) {
976         IClassFactoryImpl *This = impl_from_IClassFactory(iface);
977
978         TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
979         if ( IsEqualGUID( &IID_IUnknown, riid ) ||
980              IsEqualGUID( &IID_IDirectInputA, riid ) ||
981              IsEqualGUID( &IID_IDirectInputW, riid ) ||
982              IsEqualGUID( &IID_IDirectInput2A, riid ) ||
983              IsEqualGUID( &IID_IDirectInput2W, riid ) ||
984              IsEqualGUID( &IID_IDirectInput7A, riid ) ||
985              IsEqualGUID( &IID_IDirectInput7W, riid ) ||
986              IsEqualGUID( &IID_IDirectInput8A, riid ) ||
987              IsEqualGUID( &IID_IDirectInput8W, riid ) ) {
988                 /* FIXME: reuse already created dinput if present? */
989                 return DirectInputCreateEx(0,0,riid,ppobj,pOuter);
990         }
991
992         FIXME("(%p,%p,%s,%p) Interface not found!\n",This,pOuter,debugstr_guid(riid),ppobj);    
993         return E_NOINTERFACE;
994 }
995
996 static HRESULT WINAPI DICF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
997         IClassFactoryImpl *This = impl_from_IClassFactory(iface);
998         FIXME("(%p)->(%d),stub!\n",This,dolock);
999         return S_OK;
1000 }
1001
1002 static const IClassFactoryVtbl DICF_Vtbl = {
1003         DICF_QueryInterface,
1004         DICF_AddRef,
1005         DICF_Release,
1006         DICF_CreateInstance,
1007         DICF_LockServer
1008 };
1009 static IClassFactoryImpl DINPUT_CF = {{&DICF_Vtbl}, 1 };
1010
1011 /***********************************************************************
1012  *              DllCanUnloadNow (DINPUT.@)
1013  */
1014 HRESULT WINAPI DllCanUnloadNow(void)
1015 {
1016     return S_FALSE;
1017 }
1018
1019 /***********************************************************************
1020  *              DllGetClassObject (DINPUT.@)
1021  */
1022 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
1023 {
1024     TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1025     if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
1026         *ppv = &DINPUT_CF;
1027         IClassFactory_AddRef((IClassFactory*)*ppv);
1028     return S_OK;
1029     }
1030
1031     FIXME("(%s,%s,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1032     return CLASS_E_CLASSNOTAVAILABLE;
1033 }
1034
1035 /***********************************************************************
1036  *              DllRegisterServer (DINPUT.@)
1037  */
1038 HRESULT WINAPI DllRegisterServer(void)
1039 {
1040     return __wine_register_resources( DINPUT_instance, NULL );
1041 }
1042
1043 /***********************************************************************
1044  *              DllUnregisterServer (DINPUT.@)
1045  */
1046 HRESULT WINAPI DllUnregisterServer(void)
1047 {
1048     return __wine_unregister_resources( DINPUT_instance, NULL );
1049 }
1050
1051 /******************************************************************************
1052  *      DInput hook thread
1053  */
1054
1055 static LRESULT CALLBACK LL_hook_proc( int code, WPARAM wparam, LPARAM lparam )
1056 {
1057     IDirectInputImpl *dinput;
1058     int skip = 0;
1059
1060     if (code != HC_ACTION) return CallNextHookEx( 0, code, wparam, lparam );
1061
1062     EnterCriticalSection( &dinput_hook_crit );
1063     LIST_FOR_EACH_ENTRY( dinput, &direct_input_list, IDirectInputImpl, entry )
1064     {
1065         IDirectInputDeviceImpl *dev;
1066
1067         EnterCriticalSection( &dinput->crit );
1068         LIST_FOR_EACH_ENTRY( dev, &dinput->devices_list, IDirectInputDeviceImpl, entry )
1069             if (dev->acquired && dev->event_proc)
1070             {
1071                 TRACE("calling %p->%p (%lx %lx)\n", dev, dev->event_proc, wparam, lparam);
1072                 skip |= dev->event_proc( &dev->IDirectInputDevice8A_iface, wparam, lparam );
1073             }
1074         LeaveCriticalSection( &dinput->crit );
1075     }
1076     LeaveCriticalSection( &dinput_hook_crit );
1077
1078     return skip ? 1 : CallNextHookEx( 0, code, wparam, lparam );
1079 }
1080
1081 static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam )
1082 {
1083     CWPSTRUCT *msg = (CWPSTRUCT *)lparam;
1084     IDirectInputImpl *dinput;
1085     HWND foreground;
1086
1087     if (code != HC_ACTION || (msg->message != WM_KILLFOCUS &&
1088         msg->message != WM_ACTIVATEAPP && msg->message != WM_ACTIVATE))
1089         return CallNextHookEx( 0, code, wparam, lparam );
1090
1091     foreground = GetForegroundWindow();
1092
1093     EnterCriticalSection( &dinput_hook_crit );
1094
1095     LIST_FOR_EACH_ENTRY( dinput, &direct_input_list, IDirectInputImpl, entry )
1096     {
1097         IDirectInputDeviceImpl *dev;
1098
1099         EnterCriticalSection( &dinput->crit );
1100         LIST_FOR_EACH_ENTRY( dev, &dinput->devices_list, IDirectInputDeviceImpl, entry )
1101         {
1102             if (!dev->acquired) continue;
1103
1104             if (msg->hwnd == dev->win && msg->hwnd != foreground)
1105             {
1106                 TRACE( "%p window is not foreground - unacquiring %p\n", dev->win, dev );
1107                 IDirectInputDevice_Unacquire( &dev->IDirectInputDevice8A_iface );
1108             }
1109         }
1110         LeaveCriticalSection( &dinput->crit );
1111     }
1112     LeaveCriticalSection( &dinput_hook_crit );
1113
1114     return CallNextHookEx( 0, code, wparam, lparam );
1115 }
1116
1117 static DWORD WINAPI hook_thread_proc(void *param)
1118 {
1119     static HHOOK kbd_hook, mouse_hook;
1120     MSG msg;
1121
1122     /* Force creation of the message queue */
1123     PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE );
1124     SetEvent(*(LPHANDLE)param);
1125
1126     while (GetMessageW( &msg, 0, 0, 0 ))
1127     {
1128         UINT kbd_cnt = 0, mice_cnt = 0;
1129
1130         if (msg.message == WM_USER+0x10)
1131         {
1132             IDirectInputImpl *dinput;
1133
1134             TRACE( "Processing hook change notification lp:%ld\n", msg.lParam );
1135
1136             if (!msg.wParam && !msg.lParam)
1137             {
1138                 if (kbd_hook) UnhookWindowsHookEx( kbd_hook );
1139                 if (mouse_hook) UnhookWindowsHookEx( mouse_hook );
1140                 kbd_hook = mouse_hook = NULL;
1141                 break;
1142             }
1143
1144             EnterCriticalSection( &dinput_hook_crit );
1145
1146             /* Count acquired keyboards and mice*/
1147             LIST_FOR_EACH_ENTRY( dinput, &direct_input_list, IDirectInputImpl, entry )
1148             {
1149                 IDirectInputDeviceImpl *dev;
1150
1151                 EnterCriticalSection( &dinput->crit );
1152                 LIST_FOR_EACH_ENTRY( dev, &dinput->devices_list, IDirectInputDeviceImpl, entry )
1153                 {
1154                     if (!dev->acquired || !dev->event_proc) continue;
1155
1156                     if (IsEqualGUID( &dev->guid, &GUID_SysKeyboard ) ||
1157                         IsEqualGUID( &dev->guid, &DInput_Wine_Keyboard_GUID ))
1158                         kbd_cnt++;
1159                     else
1160                         if (IsEqualGUID( &dev->guid, &GUID_SysMouse ) ||
1161                             IsEqualGUID( &dev->guid, &DInput_Wine_Mouse_GUID ))
1162                             mice_cnt++;
1163                 }
1164                 LeaveCriticalSection( &dinput->crit );
1165             }
1166             LeaveCriticalSection( &dinput_hook_crit );
1167
1168             if (kbd_cnt && !kbd_hook)
1169                 kbd_hook = SetWindowsHookExW( WH_KEYBOARD_LL, LL_hook_proc, DINPUT_instance, 0 );
1170             else if (!kbd_cnt && kbd_hook)
1171             {
1172                 UnhookWindowsHookEx( kbd_hook );
1173                 kbd_hook = NULL;
1174             }
1175
1176             if (mice_cnt && !mouse_hook)
1177                 mouse_hook = SetWindowsHookExW( WH_MOUSE_LL, LL_hook_proc, DINPUT_instance, 0 );
1178             else if (!mice_cnt && mouse_hook)
1179             {
1180                 UnhookWindowsHookEx( mouse_hook );
1181                 mouse_hook = NULL;
1182             }
1183         }
1184         TranslateMessage(&msg);
1185         DispatchMessageW(&msg);
1186     }
1187
1188     return 0;
1189 }
1190
1191 static DWORD hook_thread_id;
1192
1193 static CRITICAL_SECTION_DEBUG dinput_critsect_debug =
1194 {
1195     0, 0, &dinput_hook_crit,
1196     { &dinput_critsect_debug.ProcessLocksList, &dinput_critsect_debug.ProcessLocksList },
1197       0, 0, { (DWORD_PTR)(__FILE__ ": dinput_hook_crit") }
1198 };
1199 static CRITICAL_SECTION dinput_hook_crit = { &dinput_critsect_debug, -1, 0, 0, 0, 0 };
1200
1201 static BOOL check_hook_thread(void)
1202 {
1203     static HANDLE hook_thread;
1204
1205     EnterCriticalSection(&dinput_hook_crit);
1206
1207     TRACE("IDirectInputs left: %d\n", list_count(&direct_input_list));
1208     if (!list_empty(&direct_input_list) && !hook_thread)
1209     {
1210         HANDLE event;
1211
1212         event = CreateEventW(NULL, FALSE, FALSE, NULL);
1213         hook_thread = CreateThread(NULL, 0, hook_thread_proc, &event, 0, &hook_thread_id);
1214         if (event && hook_thread)
1215         {
1216             HANDLE handles[2];
1217             handles[0] = event;
1218             handles[1] = hook_thread;
1219             WaitForMultipleObjects(2, handles, FALSE, INFINITE);
1220         }
1221         LeaveCriticalSection(&dinput_hook_crit);
1222         CloseHandle(event);
1223     }
1224     else if (list_empty(&direct_input_list) && hook_thread)
1225     {
1226         DWORD tid = hook_thread_id;
1227
1228         hook_thread_id = 0;
1229         PostThreadMessageW(tid, WM_USER+0x10, 0, 0);
1230         LeaveCriticalSection(&dinput_hook_crit);
1231
1232         /* wait for hook thread to exit */
1233         WaitForSingleObject(hook_thread, INFINITE);
1234         CloseHandle(hook_thread);
1235         hook_thread = NULL;
1236     }
1237     else
1238         LeaveCriticalSection(&dinput_hook_crit);
1239
1240     return hook_thread_id != 0;
1241 }
1242
1243 void check_dinput_hooks(LPDIRECTINPUTDEVICE8W iface)
1244 {
1245     static HHOOK callwndproc_hook;
1246     static ULONG foreground_cnt;
1247     IDirectInputDeviceImpl *dev = impl_from_IDirectInputDevice8W(iface);
1248
1249     EnterCriticalSection(&dinput_hook_crit);
1250
1251     if (dev->dwCoopLevel & DISCL_FOREGROUND)
1252     {
1253         if (dev->acquired)
1254             foreground_cnt++;
1255         else
1256             foreground_cnt--;
1257     }
1258
1259     if (foreground_cnt && !callwndproc_hook)
1260         callwndproc_hook = SetWindowsHookExW( WH_CALLWNDPROC, callwndproc_proc,
1261                                               DINPUT_instance, GetCurrentThreadId() );
1262     else if (!foreground_cnt && callwndproc_hook)
1263     {
1264         UnhookWindowsHookEx( callwndproc_hook );
1265         callwndproc_hook = NULL;
1266     }
1267
1268     PostThreadMessageW( hook_thread_id, WM_USER+0x10, 1, 0 );
1269
1270     LeaveCriticalSection(&dinput_hook_crit);
1271 }