Check for NULL before doing IsEqualGUID().
[wine] / dlls / ddraw / main.c
1 /*              DirectDraw Base Functions
2  *
3  * Copyright 1997-1999 Marcus Meissner
4  * Copyright 1998 Lionel Ulmer (most of Direct3D stuff)
5  */
6
7 #include "config.h"
8
9 #include <unistd.h>
10 #include <assert.h>
11 #include <fcntl.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15
16 #include "winerror.h"
17 #include "heap.h"
18 #include "wine/exception.h"
19 #include "debugtools.h"
20
21 /* This for all the enumeration and creation of D3D-related objects */
22 #include "ddraw_private.h"
23
24 #define MAX_DDRAW_DRIVERS       3
25 static ddraw_driver * ddraw_drivers[MAX_DDRAW_DRIVERS];
26 static int nrof_ddraw_drivers                   = 0;
27
28 DEFAULT_DEBUG_CHANNEL(ddraw);
29
30 /* register a direct draw driver. We better not use malloc for we are in 
31  * the ELF startup initialisation at this point.
32  */
33 void ddraw_register_driver(ddraw_driver *driver) {
34     ddraw_drivers[nrof_ddraw_drivers++] = driver;
35
36     /* increase MAX_DDRAW_DRIVERS if the line below triggers */
37     assert(nrof_ddraw_drivers <= MAX_DDRAW_DRIVERS);
38 }
39
40 /**********************************************************************/
41
42 typedef struct {
43     LPVOID lpCallback;
44     LPVOID lpContext; 
45 } DirectDrawEnumerateProcData;
46
47 /***********************************************************************
48  *              DirectDrawEnumerateExA (DDRAW.*)
49  */
50 HRESULT WINAPI DirectDrawEnumerateExA(
51     LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags)
52 {
53     int i;
54     GUID        zeroGUID;
55     TRACE("(%p,%p, %08lx)\n", lpCallback, lpContext, dwFlags);
56
57     if (TRACE_ON(ddraw)) {
58         DPRINTF("  Flags : ");
59         if (dwFlags & DDENUM_ATTACHEDSECONDARYDEVICES)
60             DPRINTF("DDENUM_ATTACHEDSECONDARYDEVICES ");
61         if (dwFlags & DDENUM_DETACHEDSECONDARYDEVICES)
62             DPRINTF("DDENUM_DETACHEDSECONDARYDEVICES ");
63         if (dwFlags & DDENUM_NONDISPLAYDEVICES)
64             DPRINTF("DDENUM_NONDISPLAYDEVICES ");
65         DPRINTF("\n");
66     }
67     if (dwFlags & DDENUM_ATTACHEDSECONDARYDEVICES) {
68         FIXME("no attached secondary devices supported.\n");
69         /*return E_FAIL;*/
70     }
71
72     memset(&zeroGUID,0,sizeof(zeroGUID));
73
74     /* we have at least one DDRAW driver */
75     if (ddraw_drivers[0]) {
76         if (!lpCallback(
77             &zeroGUID, /* FIXME: or NULL? -MM */
78             "WINE DirectDraw",
79             "display",
80             lpContext,
81             0           /* FIXME: flags not supported here */
82         ))
83             return DD_OK;
84     }
85     /* Invoke callback for what flags we do support */
86     for (i=0;i<MAX_DDRAW_DRIVERS;i++) {
87         if (!ddraw_drivers[i])
88             continue;
89         if (ddraw_drivers[i]->createDDRAW(NULL)) /* !0 is failing */
90             continue;
91         TRACE("Enumerating %s/%s interface\n",ddraw_drivers[i]->name,ddraw_drivers[i]->type);
92         if (!lpCallback(
93             ddraw_drivers[i]->guid,
94             (LPSTR)ddraw_drivers[i]->name,
95             (LPSTR)ddraw_drivers[i]->type,
96             lpContext,
97             0           /* FIXME: flags not supported here */
98         ))
99             return DD_OK;
100     }
101     if (nrof_ddraw_drivers) {
102         TRACE("Enumerating the default interface\n");
103         if (!lpCallback(NULL,"WINE (default)", "display", lpContext, 0))
104             return DD_OK;
105     }
106
107     /* Unsupported flags */
108     if (dwFlags & DDENUM_NONDISPLAYDEVICES) {
109         FIXME("no non-display devices supported.\n");
110     }
111     if (dwFlags & DDENUM_DETACHEDSECONDARYDEVICES) {
112         FIXME("no detached secondary devices supported.\n");
113     }
114
115     return DD_OK;
116 }
117
118 /***********************************************************************
119  *              DirectDrawEnumerateExW (DDRAW.*)
120  */
121
122 static BOOL CALLBACK DirectDrawEnumerateExProcW(
123     GUID *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, 
124     LPVOID lpContext, HMONITOR hm)
125 {
126     DirectDrawEnumerateProcData *pEPD = (DirectDrawEnumerateProcData*)lpContext;
127     LPWSTR lpDriverDescriptionW =
128         HEAP_strdupAtoW(GetProcessHeap(), 0, lpDriverDescription);
129     LPWSTR lpDriverNameW =
130         HEAP_strdupAtoW(GetProcessHeap(), 0, lpDriverName);
131
132     BOOL bResult = (*(LPDDENUMCALLBACKEXW *) pEPD->lpCallback)(
133         lpGUID, lpDriverDescriptionW, lpDriverNameW, pEPD->lpContext, hm);
134
135     HeapFree(GetProcessHeap(), 0, lpDriverDescriptionW);
136     HeapFree(GetProcessHeap(), 0, lpDriverNameW);
137     return bResult;
138 }
139
140 /**********************************************************************/
141
142 HRESULT WINAPI DirectDrawEnumerateExW(
143   LPDDENUMCALLBACKEXW lpCallback, LPVOID lpContext, DWORD dwFlags)
144 {
145     DirectDrawEnumerateProcData epd;
146     epd.lpCallback = (LPVOID) lpCallback;
147     epd.lpContext = lpContext;
148
149     return DirectDrawEnumerateExA(DirectDrawEnumerateExProcW, (LPVOID) &epd, 0);
150 }
151
152 /***********************************************************************
153  *              DirectDrawEnumerateA (DDRAW.*)
154  */
155
156 static BOOL CALLBACK DirectDrawEnumerateProcA(
157         GUID *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, 
158         LPVOID lpContext, HMONITOR hm)
159 {
160     DirectDrawEnumerateProcData *pEPD = (DirectDrawEnumerateProcData*)lpContext;
161
162     return ((LPDDENUMCALLBACKA) pEPD->lpCallback)(
163         lpGUID, lpDriverDescription, lpDriverName, pEPD->lpContext);
164 }
165
166 /**********************************************************************/
167
168 HRESULT WINAPI DirectDrawEnumerateA(
169   LPDDENUMCALLBACKA lpCallback, LPVOID lpContext) 
170 {
171     DirectDrawEnumerateProcData epd;  
172     epd.lpCallback = (LPVOID) lpCallback;
173     epd.lpContext = lpContext;
174
175     return DirectDrawEnumerateExA(DirectDrawEnumerateProcA, (LPVOID) &epd, 0);
176 }
177
178 /***********************************************************************
179  *              DirectDrawEnumerateW (DDRAW.*)
180  */
181
182 static BOOL WINAPI DirectDrawEnumerateProcW(
183   GUID *lpGUID, LPWSTR lpDriverDescription, LPWSTR lpDriverName, 
184   LPVOID lpContext, HMONITOR hm)
185 {
186     DirectDrawEnumerateProcData *pEPD = (DirectDrawEnumerateProcData*)lpContext;
187   
188     return ((LPDDENUMCALLBACKW) pEPD->lpCallback)(
189         lpGUID, lpDriverDescription, lpDriverName, pEPD->lpContext);
190 }
191
192 /**********************************************************************/
193
194 HRESULT WINAPI DirectDrawEnumerateW(
195   LPDDENUMCALLBACKW lpCallback, LPVOID lpContext) 
196 {
197     DirectDrawEnumerateProcData epd;
198     epd.lpCallback = (LPVOID) lpCallback;
199     epd.lpContext = lpContext;
200
201     return DirectDrawEnumerateExW(DirectDrawEnumerateProcW, (LPVOID) &epd, 0);
202 }
203
204 /******************************************************************************
205  *                              DirectDraw Window Procedure
206  */
207 static LRESULT WINAPI DDWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
208 {
209     LRESULT ret;
210     IDirectDrawImpl* ddraw = NULL;
211     DWORD lastError;
212
213     /* FIXME(ddraw,"(0x%04x,%s,0x%08lx,0x%08lx),stub!\n",(int)hwnd,SPY_GetMsgName(msg),(long)wParam,(long)lParam); */
214
215     SetLastError( ERROR_SUCCESS );
216     ddraw  = (IDirectDrawImpl*)GetPropA( hwnd, ddProp );
217     if( (!ddraw)  && ( ( lastError = GetLastError() ) != ERROR_SUCCESS )) 
218         ERR("Unable to retrieve this ptr from window. Error %08lx\n",lastError);
219
220     if( ddraw ) {
221     /* Perform any special direct draw functions */
222         if (msg==WM_PAINT)
223             ddraw->d->paintable = 1;
224
225         /* Now let the application deal with the rest of this */
226         if( ddraw->d->mainWindow ) {
227
228             /* Don't think that we actually need to call this but... 
229              * might as well be on the safe side of things...
230              */
231
232             /* I changed hwnd to ddraw->d->mainWindow as I did not see why
233              * it should be the procedures of our fake window that gets called
234              * instead of those of the window provided by the application.
235              * And with this patch, mouse clicks work with Monkey Island III
236              * - Lionel
237              */
238             ret = DefWindowProcA( ddraw->d->mainWindow, msg, wParam, lParam );
239
240             if( !ret ) {
241                 /* We didn't handle the message - give it to the application */
242                 if (ddraw && ddraw->d->mainWindow)
243                 {
244                     WNDPROC winproc = (WNDPROC)GetWindowLongA( ddraw->d->mainWindow, GWL_WNDPROC );
245                     ret = CallWindowProcA(winproc, ddraw->d->mainWindow, msg, wParam, lParam );
246                 }
247             }
248             return ret;
249         } /* else FALLTHROUGH */
250     } /* else FALLTHROUGH */
251     return DefWindowProcA(hwnd,msg,wParam,lParam);
252 }
253
254 /***********************************************************************
255  *              DirectDrawCreate
256  */
257 HRESULT WINAPI DirectDrawCreate(
258         LPGUID lpGUID, LPDIRECTDRAW *lplpDD, LPUNKNOWN pUnkOuter
259 ) {
260     IDirectDrawImpl** ilplpDD=(IDirectDrawImpl**)lplpDD;
261     WNDCLASSA   wc;
262     HRESULT     ret = 0;
263     int         i,drvindex=0;
264     GUID        zeroGUID;
265
266     struct ddraw_driver *ddd = NULL;
267
268     if (!HIWORD(lpGUID)) lpGUID = NULL;
269
270     TRACE("(%s,%p,%p)\n",debugstr_guid(lpGUID),ilplpDD,pUnkOuter);
271
272     memset(&zeroGUID,0,sizeof(zeroGUID));
273     while (1) {
274         ddd = NULL;
275         if ( ( !lpGUID ) ||
276              ( IsEqualGUID( &zeroGUID,  lpGUID ) ) ||
277              ( IsEqualGUID( &IID_IDirectDraw,  lpGUID ) ) ||
278              ( IsEqualGUID( &IID_IDirectDraw2, lpGUID ) ) ||
279              ( IsEqualGUID( &IID_IDirectDraw4, lpGUID ) ) ||
280              ( IsEqualGUID( &IID_IDirectDraw7, lpGUID ) )
281         ) {
282             /* choose an interface out of the list */
283             for (i=0;i<nrof_ddraw_drivers;i++) {
284                 ddraw_driver *xddd = ddraw_drivers[i];
285                 if (!xddd)
286                     continue;
287                 if (!ddd || (ddd->preference<xddd->preference)) {
288                     drvindex = i;
289                     ddd = xddd;
290                 }
291             }
292         } else {
293             for (i=0;i<nrof_ddraw_drivers;i++) {
294                 if (!ddraw_drivers[i])
295                     continue;
296                 if (IsEqualGUID(ddraw_drivers[i]->guid,lpGUID)) {
297                     drvindex = i;
298                     ddd = ddraw_drivers[i];
299                     break;
300                 }
301             }
302         }
303         if (!ddd) {
304             if (!nrof_ddraw_drivers) {
305                 ERR("DirectDrawCreate(%s,%p,%p): no DirectDraw drivers compiled in.\n",debugstr_guid(lpGUID),lplpDD,pUnkOuter);
306                 return DDERR_NODIRECTDRAWHW;
307             }
308             ERR("DirectDrawCreate(%s,%p,%p): did not recognize requested GUID.\n",debugstr_guid(lpGUID),lplpDD,pUnkOuter);
309             return DDERR_INVALIDDIRECTDRAWGUID;
310         }
311         TRACE("using \"%s\" driver, calling %p\n",ddd->name,ddd->createDDRAW);
312
313         ret = ddd->createDDRAW(lplpDD);
314         if (!ret)
315             break;
316         ddraw_drivers[drvindex] = NULL; /* mark this one as unusable */
317     }
318
319     if (lpGUID &&
320         (IsEqualGUID( &IID_IDirectDraw2, lpGUID ) ||
321          IsEqualGUID( &IID_IDirectDraw4, lpGUID ) ||
322          IsEqualGUID( &IID_IDirectDraw7, lpGUID )
323         )
324     ) {
325         LPVOID x;
326         ret = IDirectDraw_QueryInterface(*lplpDD,lpGUID,&x);
327         IDirectDraw_Release(*lplpDD); /* either drop 1 refcount, or release */
328         if (!ret)
329             *lplpDD = x;
330         else
331             return ret;
332     }
333     wc.style            = CS_GLOBALCLASS;
334     wc.lpfnWndProc      = DDWndProc;
335     wc.cbClsExtra       = 0;
336     wc.cbWndExtra       = 0;
337
338     /* We can be a child of the desktop since we're really important */
339     wc.hInstance= 0; 
340     wc.hIcon    = 0;
341     wc.hCursor  = (HCURSOR)IDC_ARROWA;
342     wc.hbrBackground    = NULL_BRUSH;
343     wc.lpszMenuName     = 0;
344     wc.lpszClassName    = "WINE_DirectDraw";
345     (*ilplpDD)->d->winclass = RegisterClassA(&wc);
346     return ret;
347 }
348
349 /***********************************************************************
350  *              DirectDrawCreateEx
351  */
352 HRESULT WINAPI DirectDrawCreateEx(
353         LPGUID lpGUID, LPVOID* lplpDD, REFIID iid, LPUNKNOWN pUnkOuter
354 ) {
355   LPDIRECTDRAW  ddraw;
356   HRESULT       hres;
357
358   FIXME("(%p,%p,%s,%p), might be wrong.\n",lpGUID,lplpDD,debugstr_guid(iid),pUnkOuter);
359
360   hres=DirectDrawCreate(lpGUID,(LPDIRECTDRAW*)&ddraw,pUnkOuter);
361   if (!hres) {
362       hres=IDirectDraw_QueryInterface(ddraw,iid,lplpDD);
363       IDirectDraw_Release(ddraw);
364   }
365   return hres;
366 }
367
368 /*******************************************************************************
369  * DirectDraw ClassFactory
370  *
371  *  Heavily inspired (well, can you say completely copied :-) ) from DirectSound
372  *
373  */
374 typedef struct {
375     /* IUnknown fields */
376     ICOM_VFIELD(IClassFactory);
377     DWORD               ref;
378 } IClassFactoryImpl;
379
380 static HRESULT WINAPI 
381 DDCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
382     ICOM_THIS(IClassFactoryImpl,iface);
383
384     FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
385     return E_NOINTERFACE;
386 }
387
388 static ULONG WINAPI
389 DDCF_AddRef(LPCLASSFACTORY iface) {
390     ICOM_THIS(IClassFactoryImpl,iface);
391     return ++(This->ref);
392 }
393
394 static ULONG WINAPI DDCF_Release(LPCLASSFACTORY iface) {
395     ICOM_THIS(IClassFactoryImpl,iface);
396     /* static class, won't be  freed */
397     return --(This->ref);
398 }
399
400 static HRESULT WINAPI DDCF_CreateInstance(
401         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
402 ) {
403     ICOM_THIS(IClassFactoryImpl,iface);
404
405     TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
406     if ( ( IsEqualGUID( &IID_IDirectDraw,  riid ) ) ||
407          ( IsEqualGUID( &IID_IDirectDraw2, riid ) ) ||
408          ( IsEqualGUID( &IID_IDirectDraw4, riid ) ) ) {
409             /* FIXME: reuse already created DirectDraw if present? */
410             return DirectDrawCreate((LPGUID) riid,(LPDIRECTDRAW*)ppobj,pOuter);
411     }
412     return CLASS_E_CLASSNOTAVAILABLE;
413 }
414
415 static HRESULT WINAPI DDCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
416     ICOM_THIS(IClassFactoryImpl,iface);
417     FIXME("(%p)->(%d),stub!\n",This,dolock);
418     return S_OK;
419 }
420
421 static ICOM_VTABLE(IClassFactory) DDCF_Vtbl = 
422 {
423     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
424     DDCF_QueryInterface,
425     DDCF_AddRef,
426     DDCF_Release,
427     DDCF_CreateInstance,
428     DDCF_LockServer
429 };
430 static IClassFactoryImpl DDRAW_CF = {&DDCF_Vtbl, 1 };
431
432 /*******************************************************************************
433  * DllGetClassObject [DDRAW.13]
434  * Retrieves class object from a DLL object
435  *
436  * NOTES
437  *    Docs say returns STDAPI
438  *
439  * PARAMS
440  *    rclsid [I] CLSID for the class object
441  *    riid   [I] Reference to identifier of interface for class object
442  *    ppv    [O] Address of variable to receive interface pointer for riid
443  *
444  * RETURNS
445  *    Success: S_OK
446  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
447  *             E_UNEXPECTED
448  */
449 DWORD WINAPI DDRAW_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
450 {
451     TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
452     if ( IsEqualGUID( &IID_IClassFactory, riid ) ) {
453         *ppv = (LPVOID)&DDRAW_CF;
454         IClassFactory_AddRef((IClassFactory*)*ppv);
455         return S_OK;
456     }
457     FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
458     return CLASS_E_CLASSNOTAVAILABLE;
459 }
460
461
462 /*******************************************************************************
463  * DllCanUnloadNow [DDRAW.12]  Determines whether the DLL is in use.
464  *
465  * RETURNS
466  *    Success: S_OK
467  *    Failure: S_FALSE
468  */
469 DWORD WINAPI DDRAW_DllCanUnloadNow(void) {
470     FIXME("(void): stub\n");
471     return S_FALSE;
472 }