user32: Don't read past the end of a global memory block.
[wine] / dlls / ddraw / main.c
1 /*        DirectDraw Base Functions
2  *
3  * Copyright 1997-1999 Marcus Meissner
4  * Copyright 1998 Lionel Ulmer
5  * Copyright 2000-2001 TransGaming Technologies Inc.
6  * Copyright 2006 Stefan Dösinger
7  * Copyright 2008 Denver Gingerich
8  *
9  * This file contains the (internal) driver registration functions,
10  * driver enumeration APIs and DirectDraw creation functions.
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26
27 #include "config.h"
28 #include "wine/port.h"
29 #include "wine/debug.h"
30
31 #include <assert.h>
32 #include <stdarg.h>
33 #include <string.h>
34 #include <stdlib.h>
35
36 #define COBJMACROS
37
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winerror.h"
41 #include "wingdi.h"
42 #include "wine/exception.h"
43 #include "winreg.h"
44
45 #include "ddraw.h"
46 #include "d3d.h"
47
48 #include "ddraw_private.h"
49
50 typedef IWineD3D* (WINAPI *fnWineDirect3DCreate)(UINT, UINT, IUnknown *);
51
52 static fnWineDirect3DCreate pWineDirect3DCreate;
53
54 WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
55
56 /* The configured default surface */
57 WINED3DSURFTYPE DefaultSurfaceType = SURFACE_UNKNOWN;
58
59 /* DDraw list and critical section */
60 static struct list global_ddraw_list = LIST_INIT(global_ddraw_list);
61
62 static CRITICAL_SECTION_DEBUG ddraw_cs_debug =
63 {
64     0, 0, &ddraw_cs,
65     { &ddraw_cs_debug.ProcessLocksList,
66     &ddraw_cs_debug.ProcessLocksList },
67     0, 0, { (DWORD_PTR)(__FILE__ ": ddraw_cs") }
68 };
69 CRITICAL_SECTION ddraw_cs = { &ddraw_cs_debug, -1, 0, 0, 0, 0 };
70
71 /* value of ForceRefreshRate */
72 DWORD force_refresh_rate = 0;
73
74 /*
75  * Helper Function for DDRAW_Create and DirectDrawCreateClipper for
76  * lazy loading of the Wine D3D driver.
77  *
78  * Returns
79  *  TRUE on success
80  *  FALSE on failure.
81  */
82
83 BOOL LoadWineD3D(void)
84 {
85     static HMODULE hWineD3D = (HMODULE) -1;
86     if (hWineD3D == (HMODULE) -1)
87     {
88         hWineD3D = LoadLibraryA("wined3d");
89         if (hWineD3D)
90         {
91             pWineDirect3DCreate = (fnWineDirect3DCreate) GetProcAddress(hWineD3D, "WineDirect3DCreate");
92             pWineDirect3DCreateClipper = (fnWineDirect3DCreateClipper) GetProcAddress(hWineD3D, "WineDirect3DCreateClipper");
93             return TRUE;
94         }
95     }
96     return hWineD3D != NULL;
97 }
98
99 /***********************************************************************
100  *
101  * Helper function for DirectDrawCreate and friends
102  * Creates a new DDraw interface with the given REFIID
103  *
104  * Interfaces that can be created:
105  *  IDirectDraw, IDirectDraw2, IDirectDraw4, IDirectDraw7
106  *  IDirect3D, IDirect3D2, IDirect3D3, IDirect3D7. (Does Windows return
107  *  IDirect3D interfaces?)
108  *
109  * Arguments:
110  *  guid: ID of the requested driver, NULL for the default driver.
111  *        The GUID can be queried with DirectDrawEnumerate(Ex)A/W
112  *  DD: Used to return the pointer to the created object
113  *  UnkOuter: For aggregation, which is unsupported. Must be NULL
114  *  iid: requested version ID.
115  *
116  * Returns:
117  *  DD_OK if the Interface was created successfully
118  *  CLASS_E_NOAGGREGATION if UnkOuter is not NULL
119  *  E_OUTOFMEMORY if some allocation failed
120  *
121  ***********************************************************************/
122 static HRESULT
123 DDRAW_Create(const GUID *guid,
124              void **DD,
125              IUnknown *UnkOuter,
126              REFIID iid)
127 {
128     IDirectDrawImpl *This = NULL;
129     HRESULT hr;
130     IWineD3D *wineD3D = NULL;
131     IWineD3DDevice *wineD3DDevice = NULL;
132     HDC hDC;
133     WINED3DDEVTYPE devicetype;
134
135     TRACE("(%s,%p,%p)\n", debugstr_guid(guid), DD, UnkOuter);
136
137     *DD = NULL;
138
139     /* We don't care about this guids. Well, there's no special guid anyway
140      * OK, we could
141      */
142     if (guid == (GUID *) DDCREATE_EMULATIONONLY)
143     {
144         /* Use the reference device id. This doesn't actually change anything,
145          * WineD3D always uses OpenGL for D3D rendering. One could make it request
146          * indirect rendering
147          */
148         devicetype = WINED3DDEVTYPE_REF;
149     }
150     else if(guid == (GUID *) DDCREATE_HARDWAREONLY)
151     {
152         devicetype = WINED3DDEVTYPE_HAL;
153     }
154     else
155     {
156         devicetype = 0;
157     }
158
159     /* DDraw doesn't support aggregation, according to msdn */
160     if (UnkOuter != NULL)
161         return CLASS_E_NOAGGREGATION;
162
163     /* DirectDraw creation comes here */
164     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectDrawImpl));
165     if(!This)
166     {
167         ERR("Out of memory when creating DirectDraw\n");
168         return E_OUTOFMEMORY;
169     }
170
171     /* The interfaces:
172      * IDirectDraw and IDirect3D are the same object,
173      * QueryInterface is used to get other interfaces.
174      */
175     ICOM_INIT_INTERFACE(This, IDirectDraw,  IDirectDraw1_Vtbl);
176     ICOM_INIT_INTERFACE(This, IDirectDraw2, IDirectDraw2_Vtbl);
177     ICOM_INIT_INTERFACE(This, IDirectDraw3, IDirectDraw3_Vtbl);
178     ICOM_INIT_INTERFACE(This, IDirectDraw4, IDirectDraw4_Vtbl);
179     ICOM_INIT_INTERFACE(This, IDirectDraw7, IDirectDraw7_Vtbl);
180     ICOM_INIT_INTERFACE(This, IDirect3D,  IDirect3D1_Vtbl);
181     ICOM_INIT_INTERFACE(This, IDirect3D2, IDirect3D2_Vtbl);
182     ICOM_INIT_INTERFACE(This, IDirect3D3, IDirect3D3_Vtbl);
183     ICOM_INIT_INTERFACE(This, IDirect3D7, IDirect3D7_Vtbl);
184
185     /* See comments in IDirectDrawImpl_CreateNewSurface for a description
186      * of this member.
187      * Read from a registry key, should add a winecfg option later
188      */
189     This->ImplType = DefaultSurfaceType;
190
191     /* Get the current screen settings */
192     hDC = GetDC(0);
193     This->orig_bpp = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
194     ReleaseDC(0, hDC);
195     This->orig_width = GetSystemMetrics(SM_CXSCREEN);
196     This->orig_height = GetSystemMetrics(SM_CYSCREEN);
197
198     if (!LoadWineD3D())
199     {
200         ERR("Couldn't load WineD3D - OpenGL libs not present?\n");
201         hr = DDERR_NODIRECTDRAWSUPPORT;
202         goto err_out;
203     }
204
205     /* Initialize WineD3D
206      *
207      * All Rendering (2D and 3D) is relayed to WineD3D,
208      * but DirectDraw specific management, like DDSURFACEDESC and DDPIXELFORMAT
209      * structure handling is handled in this lib.
210      */
211     wineD3D = pWineDirect3DCreate(0 /* SDKVersion */, 7 /* DXVersion */, (IUnknown *) This /* Parent */);
212     if(!wineD3D)
213     {
214         ERR("Failed to initialise WineD3D\n");
215         hr = E_OUTOFMEMORY;
216         goto err_out;
217     }
218     This->wineD3D = wineD3D;
219     TRACE("WineD3D created at %p\n", wineD3D);
220
221     /* Initialized member...
222      *
223      * It is set to false at creation time, and set to true in
224      * IDirectDraw7::Initialize. Its sole purpose is to return DD_OK on
225      * initialize only once
226      */
227     This->initialized = FALSE;
228
229     /* Initialize WineD3DDevice
230      *
231      * It is used for screen setup, surface and palette creation
232      * When a Direct3DDevice7 is created, the D3D capabilities of WineD3D are
233      * initialized
234      */
235     hr = IWineD3D_CreateDevice(wineD3D,
236                                0 /*D3D_ADAPTER_DEFAULT*/,
237                                devicetype,
238                                NULL, /* FocusWindow, don't know yet */
239                                0, /* BehaviorFlags */
240                                &wineD3DDevice,
241                                (IUnknown *) ICOM_INTERFACE(This, IDirectDraw7));
242     if(FAILED(hr))
243     {
244         ERR("Failed to create a wineD3DDevice, result = %x\n", hr);
245         goto err_out;
246     }
247     This->wineD3DDevice = wineD3DDevice;
248     TRACE("wineD3DDevice created at %p\n", This->wineD3DDevice);
249
250     /* Register the window class
251      *
252      * It is used to create a hidden window for D3D
253      * rendering, if the application didn't pass one.
254      * It can also be used for Creating a device window
255      * from SetCooperativeLevel
256      *
257      * The name: DDRAW_<address>. The classname is
258      * 32 bit long, so a 64 bit address will fit nicely
259      * (Will this be compiled for 64 bit anyway?)
260      *
261      */
262     sprintf(This->classname, "DDRAW_%p", This);
263
264     memset(&This->wnd_class, 0, sizeof(This->wnd_class));
265     This->wnd_class.style = CS_HREDRAW | CS_VREDRAW;
266     This->wnd_class.lpfnWndProc = DefWindowProcA;
267     This->wnd_class.cbClsExtra = 0;
268     This->wnd_class.cbWndExtra = 0;
269     This->wnd_class.hInstance = GetModuleHandleA(0);
270     This->wnd_class.hIcon = 0;
271     This->wnd_class.hCursor = 0;
272     This->wnd_class.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
273     This->wnd_class.lpszMenuName = NULL;
274     This->wnd_class.lpszClassName = This->classname;
275     if(!RegisterClassA(&This->wnd_class))
276     {
277         ERR("RegisterClassA failed!\n");
278         goto err_out;
279     }
280
281     /* Get the amount of video memory */
282     This->total_vidmem = IWineD3DDevice_GetAvailableTextureMem(This->wineD3DDevice);
283
284     /* Initialize the caps */
285     This->caps.dwSize = sizeof(This->caps);
286 /* do not report DDCAPS_OVERLAY and friends since we don't support overlays */
287 #define BLIT_CAPS (DDCAPS_BLT | DDCAPS_BLTCOLORFILL | DDCAPS_BLTDEPTHFILL \
288           | DDCAPS_BLTSTRETCH | DDCAPS_CANBLTSYSMEM | DDCAPS_CANCLIP      \
289           | DDCAPS_CANCLIPSTRETCHED | DDCAPS_COLORKEY                     \
290           | DDCAPS_COLORKEYHWASSIST | DDCAPS_ALIGNBOUNDARYSRC )
291 #define CKEY_CAPS (DDCKEYCAPS_DESTBLT | DDCKEYCAPS_SRCBLT)
292 #define FX_CAPS (DDFXCAPS_BLTALPHA | DDFXCAPS_BLTMIRRORLEFTRIGHT        \
293                 | DDFXCAPS_BLTMIRRORUPDOWN | DDFXCAPS_BLTROTATION90     \
294                 | DDFXCAPS_BLTSHRINKX | DDFXCAPS_BLTSHRINKXN            \
295                 | DDFXCAPS_BLTSHRINKY | DDFXCAPS_BLTSHRINKXN            \
296                 | DDFXCAPS_BLTSTRETCHX | DDFXCAPS_BLTSTRETCHXN          \
297                 | DDFXCAPS_BLTSTRETCHY | DDFXCAPS_BLTSTRETCHYN)
298     This->caps.dwCaps |= DDCAPS_GDI | DDCAPS_PALETTE | BLIT_CAPS;
299
300     This->caps.dwCaps2 |= DDCAPS2_CERTIFIED | DDCAPS2_NOPAGELOCKREQUIRED |
301                           DDCAPS2_PRIMARYGAMMA | DDCAPS2_WIDESURFACES |
302                           DDCAPS2_CANRENDERWINDOWED;
303     This->caps.dwCKeyCaps |= CKEY_CAPS;
304     This->caps.dwFXCaps |= FX_CAPS;
305     This->caps.dwPalCaps |= DDPCAPS_8BIT | DDPCAPS_PRIMARYSURFACE;
306     This->caps.dwVidMemTotal = This->total_vidmem;
307     This->caps.dwVidMemFree = This->total_vidmem;
308     This->caps.dwSVBCaps |= BLIT_CAPS;
309     This->caps.dwSVBCKeyCaps |= CKEY_CAPS;
310     This->caps.dwSVBFXCaps |= FX_CAPS;
311     This->caps.dwVSBCaps |= BLIT_CAPS;
312     This->caps.dwVSBCKeyCaps |= CKEY_CAPS;
313     This->caps.dwVSBFXCaps |= FX_CAPS;
314     This->caps.dwSSBCaps |= BLIT_CAPS;
315     This->caps.dwSSBCKeyCaps |= CKEY_CAPS;
316     This->caps.dwSSBFXCaps |= FX_CAPS;
317     This->caps.ddsCaps.dwCaps |= DDSCAPS_ALPHA | DDSCAPS_BACKBUFFER |
318                                  DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER |
319                                  DDSCAPS_OFFSCREENPLAIN | DDSCAPS_PALETTE |
320                                  DDSCAPS_PRIMARYSURFACE | DDSCAPS_SYSTEMMEMORY |
321                                  DDSCAPS_VIDEOMEMORY | DDSCAPS_VISIBLE;
322     /* Hacks for D3D code */
323     /* TODO: Check if WineD3D has 3D enabled
324        Need opengl surfaces or auto for 3D
325      */
326     if(This->ImplType == 0 || This->ImplType == SURFACE_OPENGL)
327     {
328         This->caps.dwCaps |= DDCAPS_3D;
329         This->caps.ddsCaps.dwCaps |= DDSCAPS_3DDEVICE | DDSCAPS_MIPMAP | DDSCAPS_TEXTURE | DDSCAPS_ZBUFFER;
330     }
331     This->caps.ddsOldCaps.dwCaps = This->caps.ddsCaps.dwCaps;
332
333 #undef BLIT_CAPS
334 #undef CKEY_CAPS
335 #undef FX_CAPS
336
337     list_init(&This->surface_list);
338     list_add_head(&global_ddraw_list, &This->ddraw_list_entry);
339
340     /* Call QueryInterface to get the pointer to the requested interface. This also initializes
341      * The required refcount
342      */
343     hr = IDirectDraw7_QueryInterface( ICOM_INTERFACE(This, IDirectDraw7), iid, DD);
344     if(SUCCEEDED(hr)) return DD_OK;
345
346 err_out:
347     /* Let's hope we never need this ;) */
348     if(wineD3DDevice) IWineD3DDevice_Release(wineD3DDevice);
349     if(wineD3D) IWineD3D_Release(wineD3D);
350     if(This) HeapFree(GetProcessHeap(), 0, This->decls);
351     HeapFree(GetProcessHeap(), 0, This);
352     return hr;
353 }
354
355 /***********************************************************************
356  * DirectDrawCreate (DDRAW.@)
357  *
358  * Creates legacy DirectDraw Interfaces. Can't create IDirectDraw7
359  * interfaces in theory
360  *
361  * Arguments, return values: See DDRAW_Create
362  *
363  ***********************************************************************/
364 HRESULT WINAPI
365 DirectDrawCreate(GUID *GUID,
366                  LPDIRECTDRAW *DD,
367                  IUnknown *UnkOuter)
368 {
369     HRESULT hr;
370     TRACE("(%s,%p,%p)\n", debugstr_guid(GUID), DD, UnkOuter);
371
372     EnterCriticalSection(&ddraw_cs);
373     hr = DDRAW_Create(GUID, (void **) DD, UnkOuter, &IID_IDirectDraw);
374     LeaveCriticalSection(&ddraw_cs);
375     return hr;
376 }
377
378 /***********************************************************************
379  * DirectDrawCreateEx (DDRAW.@)
380  *
381  * Only creates new IDirectDraw7 interfaces, supposed to fail if legacy
382  * interfaces are requested.
383  *
384  * Arguments, return values: See DDRAW_Create
385  *
386  ***********************************************************************/
387 HRESULT WINAPI
388 DirectDrawCreateEx(GUID *GUID,
389                    LPVOID *DD,
390                    REFIID iid,
391                    IUnknown *UnkOuter)
392 {
393     HRESULT hr;
394     TRACE("(%s,%p,%s,%p)\n", debugstr_guid(GUID), DD, debugstr_guid(iid), UnkOuter);
395
396     if (!IsEqualGUID(iid, &IID_IDirectDraw7))
397         return DDERR_INVALIDPARAMS;
398
399     EnterCriticalSection(&ddraw_cs);
400     hr = DDRAW_Create(GUID, DD, UnkOuter, iid);
401     LeaveCriticalSection(&ddraw_cs);
402     return hr;
403 }
404
405 /***********************************************************************
406  * DirectDrawEnumerateA (DDRAW.@)
407  *
408  * Enumerates legacy ddraw drivers, ascii version. We only have one
409  * driver, which relays to WineD3D. If we were sufficiently cool,
410  * we could offer various interfaces, which use a different default surface
411  * implementation, but I think it's better to offer this choice in
412  * winecfg, because some apps use the default driver, so we would need
413  * a winecfg option anyway, and there shouldn't be 2 ways to set one setting
414  *
415  * Arguments:
416  *  Callback: Callback function from the app
417  *  Context: Argument to the call back.
418  *
419  * Returns:
420  *  DD_OK on success
421  *  E_INVALIDARG if the Callback caused a page fault
422  *
423  *
424  ***********************************************************************/
425 HRESULT WINAPI
426 DirectDrawEnumerateA(LPDDENUMCALLBACKA Callback,
427                      LPVOID Context)
428 {
429     BOOL stop = FALSE;
430
431     TRACE(" Enumerating default DirectDraw HAL interface\n");
432     /* We only have one driver */
433     __TRY
434     {
435         static CHAR driver_desc[] = "DirectDraw HAL",
436         driver_name[] = "display";
437
438         stop = !Callback(NULL, driver_desc, driver_name, Context);
439     }
440     __EXCEPT_PAGE_FAULT
441     {
442         return E_INVALIDARG;
443     }
444     __ENDTRY
445
446     TRACE(" End of enumeration\n");
447     return DD_OK;
448 }
449
450 /***********************************************************************
451  * DirectDrawEnumerateExA (DDRAW.@)
452  *
453  * Enumerates DirectDraw7 drivers, ascii version. See
454  * the comments above DirectDrawEnumerateA for more details.
455  *
456  * The Flag member is not supported right now.
457  *
458  ***********************************************************************/
459 HRESULT WINAPI
460 DirectDrawEnumerateExA(LPDDENUMCALLBACKEXA Callback,
461                        LPVOID Context,
462                        DWORD Flags)
463 {
464     BOOL stop = FALSE;
465     TRACE("Enumerating default DirectDraw HAL interface\n");
466
467     /* We only have one driver by now */
468     __TRY
469     {
470         static CHAR driver_desc[] = "DirectDraw HAL",
471         driver_name[] = "display";
472
473         /* QuickTime expects the description "DirectDraw HAL" */
474         stop = !Callback(NULL, driver_desc, driver_name, Context, 0);
475     }
476     __EXCEPT_PAGE_FAULT
477     {
478         return E_INVALIDARG;
479     }
480     __ENDTRY;
481
482     TRACE("End of enumeration\n");
483     return DD_OK;
484 }
485
486 /***********************************************************************
487  * DirectDrawEnumerateW (DDRAW.@)
488  *
489  * Enumerates legacy drivers, unicode version. See
490  * the comments above DirectDrawEnumerateA for more details.
491  *
492  * The Flag member is not supported right now.
493  *
494  ***********************************************************************/
495
496 /***********************************************************************
497  * DirectDrawEnumerateExW (DDRAW.@)
498  *
499  * Enumerates DirectDraw7 drivers, unicode version. See
500  * the comments above DirectDrawEnumerateA for more details.
501  *
502  * The Flag member is not supported right now.
503  *
504  ***********************************************************************/
505
506 /***********************************************************************
507  * Classfactory implementation.
508  ***********************************************************************/
509
510 /***********************************************************************
511  * CF_CreateDirectDraw
512  *
513  * DDraw creation function for the class factory
514  *
515  * Params:
516  *  UnkOuter: Set to NULL
517  *  iid: ID of the wanted interface
518  *  obj: Address to pass the interface pointer back
519  *
520  * Returns
521  *  DD_OK / DDERR*, see DDRAW_Create
522  *
523  ***********************************************************************/
524 static HRESULT
525 CF_CreateDirectDraw(IUnknown* UnkOuter, REFIID iid,
526                     void **obj)
527 {
528     HRESULT hr;
529
530     TRACE("(%p,%s,%p)\n", UnkOuter, debugstr_guid(iid), obj);
531
532     EnterCriticalSection(&ddraw_cs);
533     hr = DDRAW_Create(NULL, obj, UnkOuter, iid);
534     LeaveCriticalSection(&ddraw_cs);
535     return hr;
536 }
537
538 /***********************************************************************
539  * CF_CreateDirectDraw
540  *
541  * Clipper creation function for the class factory
542  *
543  * Params:
544  *  UnkOuter: Set to NULL
545  *  iid: ID of the wanted interface
546  *  obj: Address to pass the interface pointer back
547  *
548  * Returns
549  *  DD_OK / DDERR*, see DDRAW_Create
550  *
551  ***********************************************************************/
552 static HRESULT
553 CF_CreateDirectDrawClipper(IUnknown* UnkOuter, REFIID riid,
554                               void **obj)
555 {
556     HRESULT hr;
557     IDirectDrawClipper *Clip;
558
559     EnterCriticalSection(&ddraw_cs);
560     hr = DirectDrawCreateClipper(0, &Clip, UnkOuter);
561     if (hr != DD_OK)
562     {
563         LeaveCriticalSection(&ddraw_cs);
564         return hr;
565     }
566
567     hr = IDirectDrawClipper_QueryInterface(Clip, riid, obj);
568     IDirectDrawClipper_Release(Clip);
569
570     LeaveCriticalSection(&ddraw_cs);
571     return hr;
572 }
573
574 static const struct object_creation_info object_creation[] =
575 {
576     { &CLSID_DirectDraw,        CF_CreateDirectDraw },
577     { &CLSID_DirectDraw7,       CF_CreateDirectDraw },
578     { &CLSID_DirectDrawClipper, CF_CreateDirectDrawClipper }
579 };
580
581 /*******************************************************************************
582  * IDirectDrawClassFactory::QueryInterface
583  *
584  * QueryInterface for the class factory
585  *
586  * PARAMS
587  *    riid   Reference to identifier of queried interface
588  *    ppv    Address to return the interface pointer at
589  *
590  * RETURNS
591  *    Success: S_OK
592  *    Failure: E_NOINTERFACE
593  *
594  *******************************************************************************/
595 static HRESULT WINAPI
596 IDirectDrawClassFactoryImpl_QueryInterface(IClassFactory *iface,
597                     REFIID riid,
598                     void **obj)
599 {
600     ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
601
602     TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), obj);
603
604     if (IsEqualGUID(riid, &IID_IUnknown)
605         || IsEqualGUID(riid, &IID_IClassFactory))
606     {
607         IClassFactory_AddRef(iface);
608         *obj = This;
609         return S_OK;
610     }
611
612     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),obj);
613     return E_NOINTERFACE;
614 }
615
616 /*******************************************************************************
617  * IDirectDrawClassFactory::AddRef
618  *
619  * AddRef for the class factory
620  *
621  * RETURNS
622  *  The new refcount
623  *
624  *******************************************************************************/
625 static ULONG WINAPI
626 IDirectDrawClassFactoryImpl_AddRef(IClassFactory *iface)
627 {
628     ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
629     ULONG ref = InterlockedIncrement(&This->ref);
630
631     TRACE("(%p)->() incrementing from %d.\n", This, ref - 1);
632
633     return ref;
634 }
635
636 /*******************************************************************************
637  * IDirectDrawClassFactory::Release
638  *
639  * Release for the class factory. If the refcount falls to 0, the object
640  * is destroyed
641  *
642  * RETURNS
643  *  The new refcount
644  *
645  *******************************************************************************/
646 static ULONG WINAPI
647 IDirectDrawClassFactoryImpl_Release(IClassFactory *iface)
648 {
649     ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
650     ULONG ref = InterlockedDecrement(&This->ref);
651     TRACE("(%p)->() decrementing from %d.\n", This, ref+1);
652
653     if (ref == 0)
654         HeapFree(GetProcessHeap(), 0, This);
655
656     return ref;
657 }
658
659
660 /*******************************************************************************
661  * IDirectDrawClassFactory::CreateInstance
662  *
663  * What is this? Seems to create DirectDraw objects...
664  *
665  * Params
666  *  The usual things???
667  *
668  * RETURNS
669  *  ???
670  *
671  *******************************************************************************/
672 static HRESULT WINAPI
673 IDirectDrawClassFactoryImpl_CreateInstance(IClassFactory *iface,
674                                            IUnknown *UnkOuter,
675                                            REFIID riid,
676                                            void **obj)
677 {
678     ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
679
680     TRACE("(%p)->(%p,%s,%p)\n",This,UnkOuter,debugstr_guid(riid),obj);
681
682     return This->pfnCreateInstance(UnkOuter, riid, obj);
683 }
684
685 /*******************************************************************************
686  * IDirectDrawClassFactory::LockServer
687  *
688  * What is this?
689  *
690  * Params
691  *  ???
692  *
693  * RETURNS
694  *  S_OK, because it's a stub
695  *
696  *******************************************************************************/
697 static HRESULT WINAPI
698 IDirectDrawClassFactoryImpl_LockServer(IClassFactory *iface,BOOL dolock)
699 {
700     ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
701     FIXME("(%p)->(%d),stub!\n",This,dolock);
702     return S_OK;
703 }
704
705 /*******************************************************************************
706  * The class factory VTable
707  *******************************************************************************/
708 static const IClassFactoryVtbl IClassFactory_Vtbl =
709 {
710     IDirectDrawClassFactoryImpl_QueryInterface,
711     IDirectDrawClassFactoryImpl_AddRef,
712     IDirectDrawClassFactoryImpl_Release,
713     IDirectDrawClassFactoryImpl_CreateInstance,
714     IDirectDrawClassFactoryImpl_LockServer
715 };
716
717 /*******************************************************************************
718  * DllGetClassObject [DDRAW.@]
719  * Retrieves class object from a DLL object
720  *
721  * NOTES
722  *    Docs say returns STDAPI
723  *
724  * PARAMS
725  *    rclsid [I] CLSID for the class object
726  *    riid   [I] Reference to identifier of interface for class object
727  *    ppv    [O] Address of variable to receive interface pointer for riid
728  *
729  * RETURNS
730  *    Success: S_OK
731  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
732  *             E_UNEXPECTED
733  */
734 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
735 {
736     unsigned int i;
737     IClassFactoryImpl *factory;
738
739     TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
740
741     if ( !IsEqualGUID( &IID_IClassFactory, riid )
742          && ! IsEqualGUID( &IID_IUnknown, riid) )
743         return E_NOINTERFACE;
744
745     for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
746     {
747         if (IsEqualGUID(object_creation[i].clsid, rclsid))
748             break;
749     }
750
751     if (i == sizeof(object_creation)/sizeof(object_creation[0]))
752     {
753         FIXME("%s: no class found.\n", debugstr_guid(rclsid));
754         return CLASS_E_CLASSNOTAVAILABLE;
755     }
756
757     factory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*factory));
758     if (factory == NULL) return E_OUTOFMEMORY;
759
760     ICOM_INIT_INTERFACE(factory, IClassFactory, IClassFactory_Vtbl);
761     factory->ref = 1;
762
763     factory->pfnCreateInstance = object_creation[i].pfnCreateInstance;
764
765     *ppv = ICOM_INTERFACE(factory, IClassFactory);
766     return S_OK;
767 }
768
769
770 /*******************************************************************************
771  * DllCanUnloadNow [DDRAW.@]  Determines whether the DLL is in use.
772  *
773  * RETURNS
774  *    Success: S_OK
775  *    Failure: S_FALSE
776  */
777 HRESULT WINAPI DllCanUnloadNow(void)
778 {
779     HRESULT hr;
780     FIXME("(void): stub\n");
781
782     EnterCriticalSection(&ddraw_cs);
783     hr = S_FALSE;
784     LeaveCriticalSection(&ddraw_cs);
785
786     return hr;
787 }
788
789 /*******************************************************************************
790  * DestroyCallback
791  *
792  * Callback function for the EnumSurfaces call in DllMain.
793  * Dumps some surface info and releases the surface
794  *
795  * Params:
796  *  surf: The enumerated surface
797  *  desc: it's description
798  *  context: Pointer to the ddraw impl
799  *
800  * Returns:
801  *  DDENUMRET_OK;
802  *******************************************************************************/
803 static HRESULT WINAPI
804 DestroyCallback(IDirectDrawSurface7 *surf,
805                 DDSURFACEDESC2 *desc,
806                 void *context)
807 {
808     IDirectDrawSurfaceImpl *Impl = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7, surf);
809     IDirectDrawImpl *ddraw = (IDirectDrawImpl *) context;
810     ULONG ref;
811
812     ref = IDirectDrawSurface7_Release(surf);  /* For the EnumSurfaces */
813     WARN("Surface %p has an reference count of %d\n", Impl, ref);
814
815     /* Skip surfaces which are attached somewhere or which are
816      * part of a complex compound. They will get released when destroying
817      * the root
818      */
819     if( (!Impl->is_complex_root) || (Impl->first_attached != Impl) )
820         return DDENUMRET_OK;
821     /* Skip our depth stencil surface, it will be released with the render target */
822     if( Impl == ddraw->DepthStencilBuffer)
823         return DDENUMRET_OK;
824
825     /* Destroy the surface */
826     while(ref) ref = IDirectDrawSurface7_Release(surf);
827
828     return DDENUMRET_OK;
829 }
830
831 /***********************************************************************
832  * get_config_key
833  *
834  * Reads a config key from the registry. Taken from WineD3D
835  *
836  ***********************************************************************/
837 static inline DWORD get_config_key(HKEY defkey, HKEY appkey, const char* name, char* buffer, DWORD size)
838 {
839     if (0 != appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE) buffer, &size )) return 0;
840     if (0 != defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE) buffer, &size )) return 0;
841     return ERROR_FILE_NOT_FOUND;
842 }
843
844 /***********************************************************************
845  * DllMain (DDRAW.0)
846  *
847  * Could be used to register DirectDraw drivers, if we have more than
848  * one. Also used to destroy any objects left at unload if the
849  * app didn't release them properly(Gothic 2, Diablo 2, Moto racer, ...)
850  *
851  ***********************************************************************/
852 BOOL WINAPI
853 DllMain(HINSTANCE hInstDLL,
854         DWORD Reason,
855         LPVOID lpv)
856 {
857     TRACE("(%p,%x,%p)\n", hInstDLL, Reason, lpv);
858     if (Reason == DLL_PROCESS_ATTACH)
859     {
860         char buffer[MAX_PATH+10];
861         DWORD size = sizeof(buffer);
862         HKEY hkey = 0;
863         HKEY appkey = 0;
864         DWORD len;
865
866        /* @@ Wine registry key: HKCU\Software\Wine\Direct3D */
867        if ( RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\Direct3D", &hkey ) ) hkey = 0;
868
869        len = GetModuleFileNameA( 0, buffer, MAX_PATH );
870        if (len && len < MAX_PATH)
871        {
872             HKEY tmpkey;
873             /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\Direct3D */
874             if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
875             {
876                 char *p, *appname = buffer;
877                 if ((p = strrchr( appname, '/' ))) appname = p + 1;
878                 if ((p = strrchr( appname, '\\' ))) appname = p + 1;
879                 strcat( appname, "\\Direct3D" );
880                 TRACE("appname = [%s]\n", appname);
881                 if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
882                 RegCloseKey( tmpkey );
883             }
884        }
885
886        if ( 0 != hkey || 0 != appkey )
887        {
888             if ( !get_config_key( hkey, appkey, "DirectDrawRenderer", buffer, size) )
889             {
890                 if (!strcmp(buffer,"gdi"))
891                 {
892                     TRACE("Defaulting to GDI surfaces\n");
893                     DefaultSurfaceType = SURFACE_GDI;
894                 }
895                 else if (!strcmp(buffer,"opengl"))
896                 {
897                     TRACE("Defaulting to opengl surfaces\n");
898                     DefaultSurfaceType = SURFACE_OPENGL;
899                 }
900                 else
901                 {
902                     ERR("Unknown default surface type. Supported are:\n gdi, opengl\n");
903                 }
904             }
905         }
906
907         /* On Windows one can force the refresh rate that DirectDraw uses by
908          * setting an override value in dxdiag.  This is documented in KB315614
909          * (main article), KB230002, and KB217348.  By comparing registry dumps
910          * before and after setting the override, we see that the override value
911          * is stored in HKLM\Software\Microsoft\DirectDraw\ForceRefreshRate as a
912          * DWORD that represents the refresh rate to force.  We use this
913          * registry entry to modify the behavior of SetDisplayMode so that Wine
914          * users can override the refresh rate in a Windows-compatible way.
915          *
916          * dxdiag will not accept a refresh rate lower than 40 or higher than
917          * 120 so this value should be within that range.  It is, of course,
918          * possible for a user to set the registry entry value directly so that
919          * assumption might not hold.
920          *
921          * There is no current mechanism for setting this value through the Wine
922          * GUI.  It would be most appropriate to set this value through a dxdiag
923          * clone, but it may be sufficient to use winecfg.
924          *
925          * TODO: Create a mechanism for setting this value through the Wine GUI.
926          */
927         if ( !RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectDraw", &hkey ) )
928         {
929             DWORD type, data;
930             size = sizeof(data);
931             if (!RegQueryValueExA( hkey, "ForceRefreshRate", NULL, &type, (LPBYTE)&data, &size ) && type == REG_DWORD)
932             {
933                 TRACE("ForceRefreshRate set; overriding refresh rate to %d Hz\n", data);
934                 force_refresh_rate = data;
935             }
936             RegCloseKey( hkey );
937         }
938
939         DisableThreadLibraryCalls(hInstDLL);
940     }
941     else if (Reason == DLL_PROCESS_DETACH)
942     {
943         if(!list_empty(&global_ddraw_list))
944         {
945             struct list *entry, *entry2;
946             WARN("There are still existing DirectDraw interfaces. Wine bug or buggy application?\n");
947
948             /* We remove elements from this loop */
949             LIST_FOR_EACH_SAFE(entry, entry2, &global_ddraw_list)
950             {
951                 HRESULT hr;
952                 DDSURFACEDESC2 desc;
953                 int i;
954                 IDirectDrawImpl *ddraw = LIST_ENTRY(entry, IDirectDrawImpl, ddraw_list_entry);
955
956                 WARN("DDraw %p has a refcount of %d\n", ddraw, ddraw->ref7 + ddraw->ref4 + ddraw->ref3 + ddraw->ref2 + ddraw->ref1);
957
958                 /* Add references to each interface to avoid freeing them unexpectedly */
959                 IDirectDraw_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw));
960                 IDirectDraw2_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw2));
961                 IDirectDraw3_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw3));
962                 IDirectDraw4_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw4));
963                 IDirectDraw7_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw7));
964
965                 /* Does a D3D device exist? Destroy it
966                     * TODO: Destroy all Vertex buffers, Lights, Materials
967                     * and execute buffers too
968                     */
969                 if(ddraw->d3ddevice)
970                 {
971                     WARN("DDraw %p has d3ddevice %p attached\n", ddraw, ddraw->d3ddevice);
972                     while(IDirect3DDevice7_Release(ICOM_INTERFACE(ddraw->d3ddevice, IDirect3DDevice7)));
973                 }
974
975                 /* Try to release the objects
976                     * Do an EnumSurfaces to find any hanging surfaces
977                     */
978                 memset(&desc, 0, sizeof(desc));
979                 desc.dwSize = sizeof(desc);
980                 for(i = 0; i <= 1; i++)
981                 {
982                     hr = IDirectDraw7_EnumSurfaces(ICOM_INTERFACE(ddraw, IDirectDraw7),
983                                                     DDENUMSURFACES_ALL,
984                                                     &desc,
985                                                     (void *) ddraw,
986                                                     DestroyCallback);
987                     if(hr != D3D_OK)
988                         ERR("(%p) EnumSurfaces failed, prepare for trouble\n", ddraw);
989                 }
990
991                 /* Check the surface count */
992                 if(ddraw->surfaces > 0)
993                     ERR("DDraw %p still has %d surfaces attached\n", ddraw, ddraw->surfaces);
994
995                 /* Release all hanging references to destroy the objects. This
996                     * restores the screen mode too
997                     */
998                 while(IDirectDraw_Release(ICOM_INTERFACE(ddraw, IDirectDraw)));
999                 while(IDirectDraw2_Release(ICOM_INTERFACE(ddraw, IDirectDraw2)));
1000                 while(IDirectDraw3_Release(ICOM_INTERFACE(ddraw, IDirectDraw3)));
1001                 while(IDirectDraw4_Release(ICOM_INTERFACE(ddraw, IDirectDraw4)));
1002                 while(IDirectDraw7_Release(ICOM_INTERFACE(ddraw, IDirectDraw7)));
1003             }
1004         }
1005     }
1006
1007     return TRUE;
1008 }