Fixed a race condition on RPC worker thread creation, and a typo.
[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  *
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 /* Status:
23  *
24  * - Tomb Raider 2 Demo:
25  *   Playable using keyboard only.
26  * - WingCommander Prophecy Demo:
27  *   Doesn't get Input Focus.
28  *
29  * - Fallout : works great in X and DGA mode
30  */
31
32 #include "config.h"
33 #include <assert.h>
34 #include <string.h>
35
36 #include "wine/debug.h"
37 #include "winbase.h"
38 #include "winuser.h"
39 #include "winerror.h"
40 #include "windef.h"
41 #include "dinput_private.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
44
45 static ICOM_VTABLE(IDirectInput7A) ddi7avt;
46 static ICOM_VTABLE(IDirectInput8A) ddi8avt;
47
48 /* This array will be filled a dinput.so loading */
49 #define MAX_WINE_DINPUT_DEVICES 4
50 static dinput_device * dinput_devices[MAX_WINE_DINPUT_DEVICES];
51 static int nrof_dinput_devices = 0;
52
53 HINSTANCE DINPUT_instance = NULL;
54
55 BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserv)
56 {
57     switch(reason)
58     {
59       case DLL_PROCESS_ATTACH:
60         DINPUT_instance = inst;
61         break;
62       case DLL_PROCESS_DETACH:
63         break;
64     }
65     return TRUE;
66 }
67
68
69 /* register a direct draw driver. We better not use malloc for we are in
70  * the ELF startup initialisation at this point.
71  */
72 void dinput_register_device(dinput_device *device) {
73     int i;
74
75     /* insert according to priority */
76     for (i=0;i<nrof_dinput_devices;i++) {
77         if (dinput_devices[i]->pref <= device->pref) {
78             memcpy(dinput_devices+i+1,dinput_devices+i,sizeof(dinput_devices[0])*(nrof_dinput_devices-i));
79             dinput_devices[i] = device;
80             break;
81         }
82     }
83     if (i==nrof_dinput_devices) /* not found, or too low priority */
84         dinput_devices[nrof_dinput_devices] = device;
85
86     nrof_dinput_devices++;
87
88     /* increase MAX_DDRAW_DRIVERS if the line below triggers */
89     assert(nrof_dinput_devices <= MAX_WINE_DINPUT_DEVICES);
90 }
91
92 /******************************************************************************
93  *      DirectInputCreateEx (DINPUT.@)
94  */
95 HRESULT WINAPI DirectInputCreateEx(
96         HINSTANCE hinst, DWORD dwVersion, REFIID riid, LPVOID *ppDI,
97         LPUNKNOWN punkOuter
98 ) {
99         IDirectInputAImpl* This;
100
101         TRACE("(0x%08lx,%04lx,%s,%p,%p)\n",
102                 (DWORD)hinst,dwVersion,debugstr_guid(riid),ppDI,punkOuter
103         );
104         if (IsEqualGUID(&IID_IDirectInputA,riid) ||
105             IsEqualGUID(&IID_IDirectInput2A,riid) ||
106             IsEqualGUID(&IID_IDirectInput7A,riid)) {
107           This = (IDirectInputAImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputAImpl));
108           This->lpVtbl = &ddi7avt;
109           This->ref = 1;
110           *ppDI = This;
111
112           return DI_OK;
113         }
114
115
116         if (IsEqualGUID(&IID_IDirectInput8A,riid)) {
117           This = (IDirectInputAImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputAImpl));
118           This->lpVtbl = &ddi8avt;
119           This->ref = 1;
120           *ppDI = This;
121
122           return DI_OK;
123         }
124
125         return DIERR_OLDDIRECTINPUTVERSION;
126 }
127
128 /******************************************************************************
129  *      DirectInputCreateA (DINPUT.@)
130  */
131 HRESULT WINAPI DirectInputCreateA(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTA *ppDI, LPUNKNOWN punkOuter)
132 {
133         IDirectInputAImpl* This;
134         TRACE("(0x%08lx,%04lx,%p,%p)\n",
135                 (DWORD)hinst,dwVersion,ppDI,punkOuter
136         );
137         This = (IDirectInputAImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputAImpl));
138         This->lpVtbl = &ddi7avt;
139         This->ref = 1;
140         *ppDI=(IDirectInputA*)This;
141         return 0;
142
143 }
144 /******************************************************************************
145  *      IDirectInputA_EnumDevices
146  */
147 static HRESULT WINAPI IDirectInputAImpl_EnumDevices(
148         LPDIRECTINPUT7A iface, DWORD dwDevType, LPDIENUMDEVICESCALLBACKA lpCallback,
149         LPVOID pvRef, DWORD dwFlags
150 )
151 {
152         ICOM_THIS(IDirectInputAImpl,iface);
153         DIDEVICEINSTANCEA devInstance;
154         int i;
155
156         TRACE("(this=%p,0x%04lx,%p,%p,%04lx)\n", This, dwDevType, lpCallback, pvRef, dwFlags);
157
158         for (i = 0; i < nrof_dinput_devices; i++) {
159           devInstance.dwSize = sizeof(devInstance);
160           if (dinput_devices[i]->enum_device(dwDevType, dwFlags, &devInstance)) {
161             if (lpCallback(&devInstance,pvRef) == DIENUM_STOP)
162               return 0;
163           }
164         }
165
166         return 0;
167 }
168
169 static HRESULT WINAPI IDirectInputAImpl_QueryInterface(
170         LPDIRECTINPUT7A iface,REFIID riid,LPVOID *ppobj
171 ) {
172         ICOM_THIS(IDirectInputAImpl,iface);
173
174         TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
175         if (IsEqualGUID(&IID_IUnknown,riid) ||
176             IsEqualGUID(&IID_IDirectInputA,riid) ||
177             IsEqualGUID(&IID_IDirectInput2A,riid) ||
178             IsEqualGUID(&IID_IDirectInput7A,riid)) {
179                 IDirectInputA_AddRef(iface);
180                 *ppobj = This;
181                 return 0;
182         }
183         TRACE("Unsupported interface !\n");
184         return E_FAIL;
185 }
186
187 static ULONG WINAPI IDirectInputAImpl_AddRef(LPDIRECTINPUT7A iface)
188 {
189         ICOM_THIS(IDirectInputAImpl,iface);
190         return ++(This->ref);
191 }
192
193 static ULONG WINAPI IDirectInputAImpl_Release(LPDIRECTINPUT7A iface)
194 {
195         ICOM_THIS(IDirectInputAImpl,iface);
196         if (!(--This->ref)) {
197                 HeapFree(GetProcessHeap(),0,This);
198                 return 0;
199         }
200         return This->ref;
201 }
202
203 static HRESULT WINAPI IDirectInputAImpl_CreateDevice(
204         LPDIRECTINPUT7A iface,REFGUID rguid,LPDIRECTINPUTDEVICEA* pdev,
205         LPUNKNOWN punk
206 ) {
207         ICOM_THIS(IDirectInputAImpl,iface);
208         HRESULT ret_value = DIERR_DEVICENOTREG;
209         int i;
210
211         TRACE("(this=%p,%s,%p,%p)\n",This,debugstr_guid(rguid),pdev,punk);
212
213         /* Loop on all the devices to see if anyone matches the given GUID */
214         for (i = 0; i < nrof_dinput_devices; i++) {
215           HRESULT ret;
216           if ((ret = dinput_devices[i]->create_device(This, rguid, NULL, pdev)) == DI_OK)
217             return DI_OK;
218
219           if (ret == DIERR_NOINTERFACE)
220             ret_value = DIERR_NOINTERFACE;
221         }
222
223         return ret_value;
224 }
225
226 static HRESULT WINAPI IDirectInputAImpl_Initialize(
227         LPDIRECTINPUT7A iface,HINSTANCE hinst,DWORD x
228 ) {
229         return DIERR_ALREADYINITIALIZED;
230 }
231
232 static HRESULT WINAPI IDirectInputAImpl_GetDeviceStatus(LPDIRECTINPUT7A iface,
233                                                         REFGUID rguid) {
234   ICOM_THIS(IDirectInputAImpl,iface);
235
236   FIXME("(%p)->(%s): stub\n",This,debugstr_guid(rguid));
237
238   return DI_OK;
239 }
240
241 static HRESULT WINAPI IDirectInputAImpl_RunControlPanel(LPDIRECTINPUT7A iface,
242                                                         HWND hwndOwner,
243                                                         DWORD dwFlags) {
244   ICOM_THIS(IDirectInputAImpl,iface);
245   FIXME("(%p)->(%08lx,%08lx): stub\n",This, (DWORD) hwndOwner, dwFlags);
246
247   return DI_OK;
248 }
249
250 static HRESULT WINAPI IDirectInput2AImpl_FindDevice(LPDIRECTINPUT7A iface, REFGUID rguid,
251                                                     LPCSTR pszName, LPGUID pguidInstance) {
252   ICOM_THIS(IDirectInputAImpl,iface);
253   FIXME("(%p)->(%s, %s, %p): stub\n", This, debugstr_guid(rguid), pszName, pguidInstance);
254
255   return DI_OK;
256 }
257
258 static HRESULT WINAPI IDirectInput7AImpl_CreateDeviceEx(LPDIRECTINPUT7A iface, REFGUID rguid,
259                                                         REFIID riid, LPVOID* pvOut, LPUNKNOWN lpUnknownOuter)
260 {
261   ICOM_THIS(IDirectInputAImpl,iface);
262   HRESULT ret_value = DIERR_DEVICENOTREG;
263   int i;
264
265   TRACE("(%p)->(%s, %s, %p, %p)\n", This, debugstr_guid(rguid), debugstr_guid(riid), pvOut, lpUnknownOuter);
266
267   /* Loop on all the devices to see if anyone matches the given GUID */
268   for (i = 0; i < nrof_dinput_devices; i++) {
269     HRESULT ret;
270     if ((ret = dinput_devices[i]->create_device(This, rguid, riid, (LPDIRECTINPUTDEVICEA*) pvOut)) == DI_OK)
271       return DI_OK;
272
273     if (ret == DIERR_NOINTERFACE)
274       ret_value = DIERR_NOINTERFACE;
275   }
276
277   return ret_value;
278 }
279
280 static HRESULT WINAPI IDirectInput8AImpl_QueryInterface(
281       LPDIRECTINPUT8A iface,REFIID riid,LPVOID *ppobj
282 ) {
283       ICOM_THIS(IDirectInputAImpl,iface);
284
285       TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
286       if (IsEqualGUID(&IID_IUnknown,riid) ||
287           IsEqualGUID(&IID_IDirectInput8A,riid)) {
288               IDirectInputA_AddRef(iface);
289               *ppobj = This;
290               return 0;
291       }
292       TRACE("Unsupported interface !\n");
293       return E_FAIL;
294 }
295
296 static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics(
297       LPDIRECTINPUT8A iface, LPCSTR ptszUserName, LPDIACTIONFORMATA lpdiActionFormat,
298       LPDIENUMDEVICESBYSEMANTICSCBA lpCallback,
299       LPVOID pvRef, DWORD dwFlags
300 )
301 {
302       ICOM_THIS(IDirectInputAImpl,iface);
303
304       FIXME("(this=%p,%s,%p,%p,%p,%04lx): stub\n", This, ptszUserName, lpdiActionFormat,
305             lpCallback, pvRef, dwFlags);
306       return 0;
307 }
308
309 static HRESULT WINAPI IDirectInput8AImpl_ConfigureDevices(
310       LPDIRECTINPUT8A iface, LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
311       LPDICONFIGUREDEVICESPARAMSA lpdiCDParams, DWORD dwFlags, LPVOID pvRefData
312 )
313 {
314       ICOM_THIS(IDirectInputAImpl,iface);
315
316       FIXME("(this=%p,%p,%p,%04lx,%p): stub\n", This, lpdiCallback, lpdiCDParams,
317             dwFlags, pvRefData);
318       return 0;
319 }
320
321 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
322 # define XCAST(fun)   (typeof(ddi7avt.fun))
323 #else
324 # define XCAST(fun)     (void*)
325 #endif
326
327 static ICOM_VTABLE(IDirectInput7A) ddi7avt = {
328         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
329         XCAST(QueryInterface)IDirectInputAImpl_QueryInterface,
330         XCAST(AddRef)IDirectInputAImpl_AddRef,
331         XCAST(Release)IDirectInputAImpl_Release,
332         XCAST(CreateDevice)IDirectInputAImpl_CreateDevice,
333         XCAST(EnumDevices)IDirectInputAImpl_EnumDevices,
334         XCAST(GetDeviceStatus)IDirectInputAImpl_GetDeviceStatus,
335         XCAST(RunControlPanel)IDirectInputAImpl_RunControlPanel,
336         XCAST(Initialize)IDirectInputAImpl_Initialize,
337         XCAST(FindDevice)IDirectInput2AImpl_FindDevice,
338         IDirectInput7AImpl_CreateDeviceEx
339 };
340 #undef XCAST
341
342 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
343 # define XCAST(fun)     (typeof(ddi8avt.fun))
344 #else
345 # define XCAST(fun)     (void*)
346 #endif
347
348 static ICOM_VTABLE(IDirectInput8A) ddi8avt = {
349         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
350         XCAST(QueryInterface)IDirectInput8AImpl_QueryInterface,
351         XCAST(AddRef)IDirectInputAImpl_AddRef,
352         XCAST(Release)IDirectInputAImpl_Release,
353         XCAST(CreateDevice)IDirectInputAImpl_CreateDevice,
354         XCAST(EnumDevices)IDirectInputAImpl_EnumDevices,
355         XCAST(GetDeviceStatus)IDirectInputAImpl_GetDeviceStatus,
356         XCAST(RunControlPanel)IDirectInputAImpl_RunControlPanel,
357         XCAST(Initialize)IDirectInputAImpl_Initialize,
358         XCAST(FindDevice)IDirectInput2AImpl_FindDevice,
359         IDirectInput8AImpl_EnumDevicesBySemantics,
360         IDirectInput8AImpl_ConfigureDevices
361 };
362 #undef XCAST
363
364 /***********************************************************************
365  *              DllCanUnloadNow (DINPUT.@)
366  */
367 HRESULT WINAPI DINPUT_DllCanUnloadNow(void)
368 {
369     FIXME("(void): stub\n");
370
371     return S_FALSE;
372 }
373
374 /***********************************************************************
375  *              DllGetClassObject (DINPUT.@)
376  */
377 HRESULT WINAPI DINPUT_DllGetClassObject(REFCLSID rclsid, REFIID riid,
378                                         LPVOID *ppv)
379 {
380     FIXME("(%p, %p, %p): stub\n", debugstr_guid(rclsid),
381           debugstr_guid(riid), ppv);
382
383     return CLASS_E_CLASSNOTAVAILABLE;
384 }
385
386 /***********************************************************************
387  *              DllRegisterServer (DINPUT.@)
388  */
389 HRESULT WINAPI DINPUT_DllRegisterServer(void)
390 {
391     FIXME("(void): stub\n");
392
393     return S_OK;
394 }
395
396 /***********************************************************************
397  *              DllUnregisterServer (DINPUT.@)
398  */
399 HRESULT WINAPI DINPUT_DllUnregisterServer(void)
400 {
401     FIXME("(void): stub\n");
402
403     return S_OK;
404 }