quartz: Make sure video window is actually destroyed.
[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
30 #define DDRAW_INIT_GUID
31 #include "ddraw_private.h"
32
33 #include "wine/exception.h"
34 #include "winreg.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
37
38 /* The configured default surface */
39 WINED3DSURFTYPE DefaultSurfaceType = SURFACE_UNKNOWN;
40
41 typeof(WineDirect3DCreateClipper) *pWineDirect3DCreateClipper DECLSPEC_HIDDEN;
42 typeof(WineDirect3DCreate) *pWineDirect3DCreate DECLSPEC_HIDDEN;
43
44 /* DDraw list and critical section */
45 static struct list global_ddraw_list = LIST_INIT(global_ddraw_list);
46
47 static CRITICAL_SECTION_DEBUG ddraw_cs_debug =
48 {
49     0, 0, &ddraw_cs,
50     { &ddraw_cs_debug.ProcessLocksList,
51     &ddraw_cs_debug.ProcessLocksList },
52     0, 0, { (DWORD_PTR)(__FILE__ ": ddraw_cs") }
53 };
54 CRITICAL_SECTION ddraw_cs = { &ddraw_cs_debug, -1, 0, 0, 0, 0 };
55
56 /* value of ForceRefreshRate */
57 DWORD force_refresh_rate = 0;
58
59 /* Handle table functions */
60 BOOL ddraw_handle_table_init(struct ddraw_handle_table *t, UINT initial_size)
61 {
62     t->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, initial_size * sizeof(*t->entries));
63     if (!t->entries)
64     {
65         ERR("Failed to allocate handle table memory.\n");
66         return FALSE;
67     }
68     t->free_entries = NULL;
69     t->table_size = initial_size;
70     t->entry_count = 0;
71
72     return TRUE;
73 }
74
75 void ddraw_handle_table_destroy(struct ddraw_handle_table *t)
76 {
77     HeapFree(GetProcessHeap(), 0, t->entries);
78     memset(t, 0, sizeof(*t));
79 }
80
81 DWORD ddraw_allocate_handle(struct ddraw_handle_table *t, void *object, enum ddraw_handle_type type)
82 {
83     struct ddraw_handle_entry *entry;
84
85     if (t->free_entries)
86     {
87         DWORD idx = t->free_entries - t->entries;
88         /* Use a free handle */
89         entry = t->free_entries;
90         if (entry->type != DDRAW_HANDLE_FREE)
91         {
92             ERR("Handle %#x (%p) is in the free list, but has type %#x.\n", idx, entry->object, entry->type);
93             return DDRAW_INVALID_HANDLE;
94         }
95         t->free_entries = entry->object;
96         entry->object = object;
97         entry->type = type;
98
99         return idx;
100     }
101
102     if (!(t->entry_count < t->table_size))
103     {
104         /* Grow the table */
105         UINT new_size = t->table_size + (t->table_size >> 1);
106         struct ddraw_handle_entry *new_entries = HeapReAlloc(GetProcessHeap(),
107                 0, t->entries, new_size * sizeof(*t->entries));
108         if (!new_entries)
109         {
110             ERR("Failed to grow the handle table.\n");
111             return DDRAW_INVALID_HANDLE;
112         }
113         t->entries = new_entries;
114         t->table_size = new_size;
115     }
116
117     entry = &t->entries[t->entry_count];
118     entry->object = object;
119     entry->type = type;
120
121     return t->entry_count++;
122 }
123
124 void *ddraw_free_handle(struct ddraw_handle_table *t, DWORD handle, enum ddraw_handle_type type)
125 {
126     struct ddraw_handle_entry *entry;
127     void *object;
128
129     if (handle == DDRAW_INVALID_HANDLE || handle >= t->entry_count)
130     {
131         WARN("Invalid handle %#x passed.\n", handle);
132         return NULL;
133     }
134
135     entry = &t->entries[handle];
136     if (entry->type != type)
137     {
138         WARN("Handle %#x (%p) is not of type %#x.\n", handle, entry->object, type);
139         return NULL;
140     }
141
142     object = entry->object;
143     entry->object = t->free_entries;
144     entry->type = DDRAW_HANDLE_FREE;
145     t->free_entries = entry;
146
147     return object;
148 }
149
150 void *ddraw_get_object(struct ddraw_handle_table *t, DWORD handle, enum ddraw_handle_type type)
151 {
152     struct ddraw_handle_entry *entry;
153
154     if (handle == DDRAW_INVALID_HANDLE || handle >= t->entry_count)
155     {
156         WARN("Invalid handle %#x passed.\n", handle);
157         return NULL;
158     }
159
160     entry = &t->entries[handle];
161     if (entry->type != type)
162     {
163         WARN("Handle %#x (%p) is not of type %#x.\n", handle, entry->object, type);
164         return NULL;
165     }
166
167     return entry->object;
168 }
169
170 /*
171  * Helper Function for DDRAW_Create and DirectDrawCreateClipper for
172  * lazy loading of the Wine D3D driver.
173  *
174  * Returns
175  *  TRUE on success
176  *  FALSE on failure.
177  */
178
179 BOOL LoadWineD3D(void)
180 {
181     static HMODULE hWineD3D = (HMODULE) -1;
182     if (hWineD3D == (HMODULE) -1)
183     {
184         hWineD3D = LoadLibraryA("wined3d");
185         if (hWineD3D)
186         {
187             pWineDirect3DCreate = (typeof(WineDirect3DCreate) *)GetProcAddress(hWineD3D, "WineDirect3DCreate");
188             pWineDirect3DCreateClipper = (typeof(WineDirect3DCreateClipper) *) GetProcAddress(hWineD3D, "WineDirect3DCreateClipper");
189             return TRUE;
190         }
191     }
192     return hWineD3D != NULL;
193 }
194
195 /***********************************************************************
196  *
197  * Helper function for DirectDrawCreate and friends
198  * Creates a new DDraw interface with the given REFIID
199  *
200  * Interfaces that can be created:
201  *  IDirectDraw, IDirectDraw2, IDirectDraw4, IDirectDraw7
202  *  IDirect3D, IDirect3D2, IDirect3D3, IDirect3D7. (Does Windows return
203  *  IDirect3D interfaces?)
204  *
205  * Arguments:
206  *  guid: ID of the requested driver, NULL for the default driver.
207  *        The GUID can be queried with DirectDrawEnumerate(Ex)A/W
208  *  DD: Used to return the pointer to the created object
209  *  UnkOuter: For aggregation, which is unsupported. Must be NULL
210  *  iid: requested version ID.
211  *
212  * Returns:
213  *  DD_OK if the Interface was created successfully
214  *  CLASS_E_NOAGGREGATION if UnkOuter is not NULL
215  *  E_OUTOFMEMORY if some allocation failed
216  *
217  ***********************************************************************/
218 static HRESULT
219 DDRAW_Create(const GUID *guid,
220              void **DD,
221              IUnknown *UnkOuter,
222              REFIID iid)
223 {
224     WINED3DDEVTYPE devicetype;
225     IDirectDrawImpl *This;
226     HRESULT hr;
227
228     TRACE("driver_guid %s, ddraw %p, outer_unknown %p, interface_iid %s.\n",
229             debugstr_guid(guid), DD, UnkOuter, debugstr_guid(iid));
230
231     *DD = NULL;
232
233     /* We don't care about this guids. Well, there's no special guid anyway
234      * OK, we could
235      */
236     if (guid == (GUID *) DDCREATE_EMULATIONONLY)
237     {
238         /* Use the reference device id. This doesn't actually change anything,
239          * WineD3D always uses OpenGL for D3D rendering. One could make it request
240          * indirect rendering
241          */
242         devicetype = WINED3DDEVTYPE_REF;
243     }
244     else if(guid == (GUID *) DDCREATE_HARDWAREONLY)
245     {
246         devicetype = WINED3DDEVTYPE_HAL;
247     }
248     else
249     {
250         devicetype = 0;
251     }
252
253     /* DDraw doesn't support aggregation, according to msdn */
254     if (UnkOuter != NULL)
255         return CLASS_E_NOAGGREGATION;
256
257     /* DirectDraw creation comes here */
258     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectDrawImpl));
259     if(!This)
260     {
261         ERR("Out of memory when creating DirectDraw\n");
262         return E_OUTOFMEMORY;
263     }
264
265     hr = ddraw_init(This, devicetype);
266     if (FAILED(hr))
267     {
268         WARN("Failed to initialize ddraw object, hr %#x.\n", hr);
269         HeapFree(GetProcessHeap(), 0, This);
270         return hr;
271     }
272
273     hr = IDirectDraw7_QueryInterface((IDirectDraw7 *)This, iid, DD);
274     IDirectDraw7_Release((IDirectDraw7 *)This);
275     if (SUCCEEDED(hr)) list_add_head(&global_ddraw_list, &This->ddraw_list_entry);
276     else WARN("Failed to query interface %s from ddraw object %p.\n", debugstr_guid(iid), This);
277
278     return hr;
279 }
280
281 /***********************************************************************
282  * DirectDrawCreate (DDRAW.@)
283  *
284  * Creates legacy DirectDraw Interfaces. Can't create IDirectDraw7
285  * interfaces in theory
286  *
287  * Arguments, return values: See DDRAW_Create
288  *
289  ***********************************************************************/
290 HRESULT WINAPI DECLSPEC_HOTPATCH
291 DirectDrawCreate(GUID *GUID,
292                  LPDIRECTDRAW *DD,
293                  IUnknown *UnkOuter)
294 {
295     HRESULT hr;
296
297     TRACE("driver_guid %s, ddraw %p, outer_unknown %p.\n",
298             debugstr_guid(GUID), DD, UnkOuter);
299
300     EnterCriticalSection(&ddraw_cs);
301     hr = DDRAW_Create(GUID, (void **) DD, UnkOuter, &IID_IDirectDraw);
302     LeaveCriticalSection(&ddraw_cs);
303     return hr;
304 }
305
306 /***********************************************************************
307  * DirectDrawCreateEx (DDRAW.@)
308  *
309  * Only creates new IDirectDraw7 interfaces, supposed to fail if legacy
310  * interfaces are requested.
311  *
312  * Arguments, return values: See DDRAW_Create
313  *
314  ***********************************************************************/
315 HRESULT WINAPI DECLSPEC_HOTPATCH
316 DirectDrawCreateEx(GUID *GUID,
317                    LPVOID *DD,
318                    REFIID iid,
319                    IUnknown *UnkOuter)
320 {
321     HRESULT hr;
322
323     TRACE("driver_guid %s, ddraw %p, interface_iid %s, outer_unknown %p.\n",
324             debugstr_guid(GUID), DD, debugstr_guid(iid), UnkOuter);
325
326     if (!IsEqualGUID(iid, &IID_IDirectDraw7))
327         return DDERR_INVALIDPARAMS;
328
329     EnterCriticalSection(&ddraw_cs);
330     hr = DDRAW_Create(GUID, DD, UnkOuter, iid);
331     LeaveCriticalSection(&ddraw_cs);
332     return hr;
333 }
334
335 /***********************************************************************
336  * DirectDrawEnumerateA (DDRAW.@)
337  *
338  * Enumerates legacy ddraw drivers, ascii version. We only have one
339  * driver, which relays to WineD3D. If we were sufficiently cool,
340  * we could offer various interfaces, which use a different default surface
341  * implementation, but I think it's better to offer this choice in
342  * winecfg, because some apps use the default driver, so we would need
343  * a winecfg option anyway, and there shouldn't be 2 ways to set one setting
344  *
345  * Arguments:
346  *  Callback: Callback function from the app
347  *  Context: Argument to the call back.
348  *
349  * Returns:
350  *  DD_OK on success
351  *  E_INVALIDARG if the Callback caused a page fault
352  *
353  *
354  ***********************************************************************/
355 HRESULT WINAPI DirectDrawEnumerateA(LPDDENUMCALLBACKA Callback, void *Context)
356 {
357     TRACE("callback %p, context %p.\n", Callback, Context);
358
359     TRACE(" Enumerating default DirectDraw HAL interface\n");
360     /* We only have one driver */
361     __TRY
362     {
363         static CHAR driver_desc[] = "DirectDraw HAL",
364         driver_name[] = "display";
365
366         Callback(NULL, driver_desc, driver_name, Context);
367     }
368     __EXCEPT_PAGE_FAULT
369     {
370         return DDERR_INVALIDPARAMS;
371     }
372     __ENDTRY
373
374     TRACE(" End of enumeration\n");
375     return DD_OK;
376 }
377
378 /***********************************************************************
379  * DirectDrawEnumerateExA (DDRAW.@)
380  *
381  * Enumerates DirectDraw7 drivers, ascii version. See
382  * the comments above DirectDrawEnumerateA for more details.
383  *
384  * The Flag member is not supported right now.
385  *
386  ***********************************************************************/
387 HRESULT WINAPI DirectDrawEnumerateExA(LPDDENUMCALLBACKEXA Callback, void *Context, DWORD Flags)
388 {
389     TRACE("callback %p, context %p, flags %#x.\n", Callback, Context, Flags);
390
391     if (Flags & ~(DDENUM_ATTACHEDSECONDARYDEVICES |
392                   DDENUM_DETACHEDSECONDARYDEVICES |
393                   DDENUM_NONDISPLAYDEVICES))
394         return DDERR_INVALIDPARAMS;
395
396     if (Flags)
397         FIXME("flags 0x%08x not handled\n", Flags);
398
399     TRACE("Enumerating default DirectDraw HAL interface\n");
400
401     /* We only have one driver by now */
402     __TRY
403     {
404         static CHAR driver_desc[] = "DirectDraw HAL",
405         driver_name[] = "display";
406
407         /* QuickTime expects the description "DirectDraw HAL" */
408         Callback(NULL, driver_desc, driver_name, Context, 0);
409     }
410     __EXCEPT_PAGE_FAULT
411     {
412         return DDERR_INVALIDPARAMS;
413     }
414     __ENDTRY;
415
416     TRACE("End of enumeration\n");
417     return DD_OK;
418 }
419
420 /***********************************************************************
421  * DirectDrawEnumerateW (DDRAW.@)
422  *
423  * Enumerates legacy drivers, unicode version.
424  * This function is not implemented on Windows.
425  *
426  ***********************************************************************/
427 HRESULT WINAPI DirectDrawEnumerateW(LPDDENUMCALLBACKW callback, void *context)
428 {
429     TRACE("callback %p, context %p.\n", callback, context);
430
431     if (!callback)
432         return DDERR_INVALIDPARAMS;
433     else
434         return DDERR_UNSUPPORTED;
435 }
436
437 /***********************************************************************
438  * DirectDrawEnumerateExW (DDRAW.@)
439  *
440  * Enumerates DirectDraw7 drivers, unicode version.
441  * This function is not implemented on Windows.
442  *
443  ***********************************************************************/
444 HRESULT WINAPI DirectDrawEnumerateExW(LPDDENUMCALLBACKEXW callback, void *context, DWORD flags)
445 {
446     TRACE("callback %p, context %p, flags %#x.\n", callback, context, flags);
447
448     return DDERR_UNSUPPORTED;
449 }
450
451 /***********************************************************************
452  * Classfactory implementation.
453  ***********************************************************************/
454
455 /***********************************************************************
456  * CF_CreateDirectDraw
457  *
458  * DDraw creation function for the class factory
459  *
460  * Params:
461  *  UnkOuter: Set to NULL
462  *  iid: ID of the wanted interface
463  *  obj: Address to pass the interface pointer back
464  *
465  * Returns
466  *  DD_OK / DDERR*, see DDRAW_Create
467  *
468  ***********************************************************************/
469 static HRESULT
470 CF_CreateDirectDraw(IUnknown* UnkOuter, REFIID iid,
471                     void **obj)
472 {
473     HRESULT hr;
474
475     TRACE("outer_unknown %p, riid %s, object %p.\n", UnkOuter, debugstr_guid(iid), obj);
476
477     EnterCriticalSection(&ddraw_cs);
478     hr = DDRAW_Create(NULL, obj, UnkOuter, iid);
479     LeaveCriticalSection(&ddraw_cs);
480     return hr;
481 }
482
483 /***********************************************************************
484  * CF_CreateDirectDraw
485  *
486  * Clipper creation function for the class factory
487  *
488  * Params:
489  *  UnkOuter: Set to NULL
490  *  iid: ID of the wanted interface
491  *  obj: Address to pass the interface pointer back
492  *
493  * Returns
494  *  DD_OK / DDERR*, see DDRAW_Create
495  *
496  ***********************************************************************/
497 static HRESULT
498 CF_CreateDirectDrawClipper(IUnknown* UnkOuter, REFIID riid,
499                               void **obj)
500 {
501     HRESULT hr;
502     IDirectDrawClipper *Clip;
503
504     TRACE("outer_unknown %p, riid %s, object %p.\n", UnkOuter, debugstr_guid(riid), obj);
505
506     EnterCriticalSection(&ddraw_cs);
507     hr = DirectDrawCreateClipper(0, &Clip, UnkOuter);
508     if (hr != DD_OK)
509     {
510         LeaveCriticalSection(&ddraw_cs);
511         return hr;
512     }
513
514     hr = IDirectDrawClipper_QueryInterface(Clip, riid, obj);
515     IDirectDrawClipper_Release(Clip);
516
517     LeaveCriticalSection(&ddraw_cs);
518     return hr;
519 }
520
521 static const struct object_creation_info object_creation[] =
522 {
523     { &CLSID_DirectDraw,        CF_CreateDirectDraw },
524     { &CLSID_DirectDraw7,       CF_CreateDirectDraw },
525     { &CLSID_DirectDrawClipper, CF_CreateDirectDrawClipper }
526 };
527
528 /*******************************************************************************
529  * IDirectDrawClassFactory::QueryInterface
530  *
531  * QueryInterface for the class factory
532  *
533  * PARAMS
534  *    riid   Reference to identifier of queried interface
535  *    ppv    Address to return the interface pointer at
536  *
537  * RETURNS
538  *    Success: S_OK
539  *    Failure: E_NOINTERFACE
540  *
541  *******************************************************************************/
542 static HRESULT WINAPI
543 IDirectDrawClassFactoryImpl_QueryInterface(IClassFactory *iface,
544                     REFIID riid,
545                     void **obj)
546 {
547     IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
548
549     TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), obj);
550
551     if (IsEqualGUID(riid, &IID_IUnknown)
552         || IsEqualGUID(riid, &IID_IClassFactory))
553     {
554         IClassFactory_AddRef(iface);
555         *obj = This;
556         return S_OK;
557     }
558
559     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),obj);
560     return E_NOINTERFACE;
561 }
562
563 /*******************************************************************************
564  * IDirectDrawClassFactory::AddRef
565  *
566  * AddRef for the class factory
567  *
568  * RETURNS
569  *  The new refcount
570  *
571  *******************************************************************************/
572 static ULONG WINAPI
573 IDirectDrawClassFactoryImpl_AddRef(IClassFactory *iface)
574 {
575     IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
576     ULONG ref = InterlockedIncrement(&This->ref);
577
578     TRACE("%p increasing refcount to %u.\n", This, ref);
579
580     return ref;
581 }
582
583 /*******************************************************************************
584  * IDirectDrawClassFactory::Release
585  *
586  * Release for the class factory. If the refcount falls to 0, the object
587  * is destroyed
588  *
589  * RETURNS
590  *  The new refcount
591  *
592  *******************************************************************************/
593 static ULONG WINAPI
594 IDirectDrawClassFactoryImpl_Release(IClassFactory *iface)
595 {
596     IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
597     ULONG ref = InterlockedDecrement(&This->ref);
598
599     TRACE("%p decreasing refcount to %u.\n", This, ref);
600
601     if (ref == 0)
602         HeapFree(GetProcessHeap(), 0, This);
603
604     return ref;
605 }
606
607
608 /*******************************************************************************
609  * IDirectDrawClassFactory::CreateInstance
610  *
611  * What is this? Seems to create DirectDraw objects...
612  *
613  * Params
614  *  The usual things???
615  *
616  * RETURNS
617  *  ???
618  *
619  *******************************************************************************/
620 static HRESULT WINAPI
621 IDirectDrawClassFactoryImpl_CreateInstance(IClassFactory *iface,
622                                            IUnknown *UnkOuter,
623                                            REFIID riid,
624                                            void **obj)
625 {
626     IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
627
628     TRACE("iface %p, outer_unknown %p, riid %s, object %p.\n",
629             iface, UnkOuter, debugstr_guid(riid), obj);
630
631     return This->pfnCreateInstance(UnkOuter, riid, obj);
632 }
633
634 /*******************************************************************************
635  * IDirectDrawClassFactory::LockServer
636  *
637  * What is this?
638  *
639  * Params
640  *  ???
641  *
642  * RETURNS
643  *  S_OK, because it's a stub
644  *
645  *******************************************************************************/
646 static HRESULT WINAPI IDirectDrawClassFactoryImpl_LockServer(IClassFactory *iface, BOOL dolock)
647 {
648     FIXME("iface %p, dolock %#x stub!\n", iface, dolock);
649
650     return S_OK;
651 }
652
653 /*******************************************************************************
654  * The class factory VTable
655  *******************************************************************************/
656 static const IClassFactoryVtbl IClassFactory_Vtbl =
657 {
658     IDirectDrawClassFactoryImpl_QueryInterface,
659     IDirectDrawClassFactoryImpl_AddRef,
660     IDirectDrawClassFactoryImpl_Release,
661     IDirectDrawClassFactoryImpl_CreateInstance,
662     IDirectDrawClassFactoryImpl_LockServer
663 };
664
665 /*******************************************************************************
666  * DllGetClassObject [DDRAW.@]
667  * Retrieves class object from a DLL object
668  *
669  * NOTES
670  *    Docs say returns STDAPI
671  *
672  * PARAMS
673  *    rclsid [I] CLSID for the class object
674  *    riid   [I] Reference to identifier of interface for class object
675  *    ppv    [O] Address of variable to receive interface pointer for riid
676  *
677  * RETURNS
678  *    Success: S_OK
679  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
680  *             E_UNEXPECTED
681  */
682 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
683 {
684     unsigned int i;
685     IClassFactoryImpl *factory;
686
687     TRACE("rclsid %s, riid %s, object %p.\n",
688             debugstr_guid(rclsid), debugstr_guid(riid), ppv);
689
690     if (!IsEqualGUID(&IID_IClassFactory, riid)
691             && !IsEqualGUID(&IID_IUnknown, riid))
692         return E_NOINTERFACE;
693
694     for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
695     {
696         if (IsEqualGUID(object_creation[i].clsid, rclsid))
697             break;
698     }
699
700     if (i == sizeof(object_creation)/sizeof(object_creation[0]))
701     {
702         FIXME("%s: no class found.\n", debugstr_guid(rclsid));
703         return CLASS_E_CLASSNOTAVAILABLE;
704     }
705
706     factory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*factory));
707     if (factory == NULL) return E_OUTOFMEMORY;
708
709     factory->lpVtbl = &IClassFactory_Vtbl;
710     factory->ref = 1;
711
712     factory->pfnCreateInstance = object_creation[i].pfnCreateInstance;
713
714     *ppv = factory;
715     return S_OK;
716 }
717
718
719 /*******************************************************************************
720  * DllCanUnloadNow [DDRAW.@]  Determines whether the DLL is in use.
721  *
722  * RETURNS
723  *    Success: S_OK
724  *    Failure: S_FALSE
725  */
726 HRESULT WINAPI DllCanUnloadNow(void)
727 {
728     TRACE("\n");
729
730     return S_FALSE;
731 }
732
733 /*******************************************************************************
734  * DestroyCallback
735  *
736  * Callback function for the EnumSurfaces call in DllMain.
737  * Dumps some surface info and releases the surface
738  *
739  * Params:
740  *  surf: The enumerated surface
741  *  desc: it's description
742  *  context: Pointer to the ddraw impl
743  *
744  * Returns:
745  *  DDENUMRET_OK;
746  *******************************************************************************/
747 static HRESULT WINAPI
748 DestroyCallback(IDirectDrawSurface7 *surf,
749                 DDSURFACEDESC2 *desc,
750                 void *context)
751 {
752     IDirectDrawSurfaceImpl *Impl = (IDirectDrawSurfaceImpl *)surf;
753     ULONG ref;
754
755     ref = IDirectDrawSurface7_Release(surf);  /* For the EnumSurfaces */
756     WARN("Surface %p has an reference count of %d\n", Impl, ref);
757
758     /* Skip surfaces which are attached somewhere or which are
759      * part of a complex compound. They will get released when destroying
760      * the root
761      */
762     if( (!Impl->is_complex_root) || (Impl->first_attached != Impl) )
763         return DDENUMRET_OK;
764
765     /* Destroy the surface */
766     while(ref) ref = IDirectDrawSurface7_Release(surf);
767
768     return DDENUMRET_OK;
769 }
770
771 /***********************************************************************
772  * get_config_key
773  *
774  * Reads a config key from the registry. Taken from WineD3D
775  *
776  ***********************************************************************/
777 static inline DWORD get_config_key(HKEY defkey, HKEY appkey, const char* name, char* buffer, DWORD size)
778 {
779     if (0 != appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE) buffer, &size )) return 0;
780     if (0 != defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE) buffer, &size )) return 0;
781     return ERROR_FILE_NOT_FOUND;
782 }
783
784 /***********************************************************************
785  * DllMain (DDRAW.0)
786  *
787  * Could be used to register DirectDraw drivers, if we have more than
788  * one. Also used to destroy any objects left at unload if the
789  * app didn't release them properly(Gothic 2, Diablo 2, Moto racer, ...)
790  *
791  ***********************************************************************/
792 BOOL WINAPI
793 DllMain(HINSTANCE hInstDLL,
794         DWORD Reason,
795         LPVOID lpv)
796 {
797     TRACE("(%p,%x,%p)\n", hInstDLL, Reason, lpv);
798     if (Reason == DLL_PROCESS_ATTACH)
799     {
800         char buffer[MAX_PATH+10];
801         DWORD size = sizeof(buffer);
802         HKEY hkey = 0;
803         HKEY appkey = 0;
804         WNDCLASSA wc;
805         DWORD len;
806
807         /* Register the window class. This is used to create a hidden window
808          * for D3D rendering, if the application didn't pass one. It can also
809          * be used for creating a device window from SetCooperativeLevel(). */
810         wc.style = CS_HREDRAW | CS_VREDRAW;
811         wc.lpfnWndProc = DefWindowProcA;
812         wc.cbClsExtra = 0;
813         wc.cbWndExtra = 0;
814         wc.hInstance = hInstDLL;
815         wc.hIcon = 0;
816         wc.hCursor = 0;
817         wc.hbrBackground = GetStockObject(BLACK_BRUSH);
818         wc.lpszMenuName = NULL;
819         wc.lpszClassName = DDRAW_WINDOW_CLASS_NAME;
820         if (!RegisterClassA(&wc))
821         {
822             ERR("Failed to register ddraw window class, last error %#x.\n", GetLastError());
823             return FALSE;
824         }
825
826        /* @@ Wine registry key: HKCU\Software\Wine\Direct3D */
827        if ( RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\Direct3D", &hkey ) ) hkey = 0;
828
829        len = GetModuleFileNameA( 0, buffer, MAX_PATH );
830        if (len && len < MAX_PATH)
831        {
832             HKEY tmpkey;
833             /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\Direct3D */
834             if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
835             {
836                 char *p, *appname = buffer;
837                 if ((p = strrchr( appname, '/' ))) appname = p + 1;
838                 if ((p = strrchr( appname, '\\' ))) appname = p + 1;
839                 strcat( appname, "\\Direct3D" );
840                 TRACE("appname = [%s]\n", appname);
841                 if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
842                 RegCloseKey( tmpkey );
843             }
844        }
845
846        if ( 0 != hkey || 0 != appkey )
847        {
848             if ( !get_config_key( hkey, appkey, "DirectDrawRenderer", buffer, size) )
849             {
850                 if (!strcmp(buffer,"gdi"))
851                 {
852                     TRACE("Defaulting to GDI surfaces\n");
853                     DefaultSurfaceType = SURFACE_GDI;
854                 }
855                 else if (!strcmp(buffer,"opengl"))
856                 {
857                     TRACE("Defaulting to opengl surfaces\n");
858                     DefaultSurfaceType = SURFACE_OPENGL;
859                 }
860                 else
861                 {
862                     ERR("Unknown default surface type. Supported are:\n gdi, opengl\n");
863                 }
864             }
865         }
866
867         /* On Windows one can force the refresh rate that DirectDraw uses by
868          * setting an override value in dxdiag.  This is documented in KB315614
869          * (main article), KB230002, and KB217348.  By comparing registry dumps
870          * before and after setting the override, we see that the override value
871          * is stored in HKLM\Software\Microsoft\DirectDraw\ForceRefreshRate as a
872          * DWORD that represents the refresh rate to force.  We use this
873          * registry entry to modify the behavior of SetDisplayMode so that Wine
874          * users can override the refresh rate in a Windows-compatible way.
875          *
876          * dxdiag will not accept a refresh rate lower than 40 or higher than
877          * 120 so this value should be within that range.  It is, of course,
878          * possible for a user to set the registry entry value directly so that
879          * assumption might not hold.
880          *
881          * There is no current mechanism for setting this value through the Wine
882          * GUI.  It would be most appropriate to set this value through a dxdiag
883          * clone, but it may be sufficient to use winecfg.
884          *
885          * TODO: Create a mechanism for setting this value through the Wine GUI.
886          */
887         if ( !RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectDraw", &hkey ) )
888         {
889             DWORD type, data;
890             size = sizeof(data);
891             if (!RegQueryValueExA( hkey, "ForceRefreshRate", NULL, &type, (LPBYTE)&data, &size ) && type == REG_DWORD)
892             {
893                 TRACE("ForceRefreshRate set; overriding refresh rate to %d Hz\n", data);
894                 force_refresh_rate = data;
895             }
896             RegCloseKey( hkey );
897         }
898
899         DisableThreadLibraryCalls(hInstDLL);
900     }
901     else if (Reason == DLL_PROCESS_DETACH)
902     {
903         if(!list_empty(&global_ddraw_list))
904         {
905             struct list *entry, *entry2;
906             WARN("There are still existing DirectDraw interfaces. Wine bug or buggy application?\n");
907
908             /* We remove elements from this loop */
909             LIST_FOR_EACH_SAFE(entry, entry2, &global_ddraw_list)
910             {
911                 HRESULT hr;
912                 DDSURFACEDESC2 desc;
913                 int i;
914                 IDirectDrawImpl *ddraw = LIST_ENTRY(entry, IDirectDrawImpl, ddraw_list_entry);
915
916                 WARN("DDraw %p has a refcount of %d\n", ddraw, ddraw->ref7 + ddraw->ref4 + ddraw->ref3 + ddraw->ref2 + ddraw->ref1);
917
918                 /* Add references to each interface to avoid freeing them unexpectedly */
919                 IDirectDraw_AddRef((IDirectDraw *)&ddraw->IDirectDraw_vtbl);
920                 IDirectDraw2_AddRef((IDirectDraw2 *)&ddraw->IDirectDraw2_vtbl);
921                 IDirectDraw3_AddRef((IDirectDraw3 *)&ddraw->IDirectDraw3_vtbl);
922                 IDirectDraw4_AddRef((IDirectDraw4 *)&ddraw->IDirectDraw4_vtbl);
923                 IDirectDraw7_AddRef((IDirectDraw7 *)ddraw);
924
925                 /* Does a D3D device exist? Destroy it
926                     * TODO: Destroy all Vertex buffers, Lights, Materials
927                     * and execute buffers too
928                     */
929                 if(ddraw->d3ddevice)
930                 {
931                     WARN("DDraw %p has d3ddevice %p attached\n", ddraw, ddraw->d3ddevice);
932                     while(IDirect3DDevice7_Release((IDirect3DDevice7 *)ddraw->d3ddevice));
933                 }
934
935                 /* Try to release the objects
936                     * Do an EnumSurfaces to find any hanging surfaces
937                     */
938                 memset(&desc, 0, sizeof(desc));
939                 desc.dwSize = sizeof(desc);
940                 for(i = 0; i <= 1; i++)
941                 {
942                     hr = IDirectDraw7_EnumSurfaces((IDirectDraw7 *)ddraw,
943                             DDENUMSURFACES_ALL, &desc, ddraw, DestroyCallback);
944                     if(hr != D3D_OK)
945                         ERR("(%p) EnumSurfaces failed, prepare for trouble\n", ddraw);
946                 }
947
948                 /* Check the surface count */
949                 if(ddraw->surfaces > 0)
950                     ERR("DDraw %p still has %d surfaces attached\n", ddraw, ddraw->surfaces);
951
952                 /* Release all hanging references to destroy the objects. This
953                     * restores the screen mode too
954                     */
955                 while(IDirectDraw_Release((IDirectDraw *)&ddraw->IDirectDraw_vtbl));
956                 while(IDirectDraw2_Release((IDirectDraw2 *)&ddraw->IDirectDraw2_vtbl));
957                 while(IDirectDraw3_Release((IDirectDraw3 *)&ddraw->IDirectDraw3_vtbl));
958                 while(IDirectDraw4_Release((IDirectDraw4 *)&ddraw->IDirectDraw4_vtbl));
959                 while(IDirectDraw7_Release((IDirectDraw7 *)ddraw));
960             }
961         }
962
963         /* Unregister the window class. */
964         UnregisterClassA(DDRAW_WINDOW_CLASS_NAME, hInstDLL);
965     }
966
967     return TRUE;
968 }