There is no point in specifying both MEM_RESERVE and MEM_COMMIT for
[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  * Copyright 2000-2001 TransGaming Technologies Inc.
6  *
7  * This file contains the (internal) driver registration functions,
8  * driver enumeration APIs and DirectDraw creation functions.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #define GLPRIVATE_NO_REDEFINE
26
27 #include "config.h"
28
29 #include <assert.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <stdlib.h>
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winnls.h"
37 #include "winerror.h"
38 #include "wingdi.h"
39
40 #include "ddraw.h"
41 #include "d3d.h"
42
43 /* This for all the enumeration and creation of D3D-related objects */
44 #include "ddraw_private.h"
45 #include "wine/debug.h"
46 #include "wine/library.h"
47 #include "wine/port.h"
48
49 #include "gl_private.h"
50
51 #undef GLPRIVATE_NO_REDEFINE
52
53 #define MAX_DDRAW_DRIVERS 3
54 static const ddraw_driver* DDRAW_drivers[MAX_DDRAW_DRIVERS];
55 static int DDRAW_num_drivers; /* = 0 */
56 static int DDRAW_default_driver;
57
58 void (*wine_tsx11_lock_ptr)(void) = NULL;
59 void (*wine_tsx11_unlock_ptr)(void) = NULL;
60
61 WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
62
63 /**********************************************************************/
64
65 typedef struct {
66     LPVOID lpCallback;
67     LPVOID lpContext;
68 } DirectDrawEnumerateProcData;
69
70 BOOL opengl_initialized = 0;
71
72 #ifdef HAVE_OPENGL
73
74 #include "mesa_private.h"
75
76 static void *gl_handle = NULL;
77
78 #define GL_API_FUNCTION(f) typeof(f) * p##f;
79 #include "gl_api.h"
80 #undef GL_API_FUNCTION
81
82 #ifndef SONAME_LIBGL
83 #define SONAME_LIBGL "libGL.so"
84 #endif
85
86 static BOOL DDRAW_bind_to_opengl( void )
87 {
88     const char *glname = SONAME_LIBGL;
89     BOOL ret_value;
90
91     gl_handle = wine_dlopen(glname, RTLD_NOW, NULL, 0);
92     if (!gl_handle) {
93         WARN("Wine cannot find the OpenGL graphics library (%s).\n",glname);
94         return FALSE;
95     }
96
97 #define GL_API_FUNCTION(f)  \
98     if((p##f = wine_dlsym(gl_handle, #f, NULL, 0)) == NULL) \
99     { \
100         WARN("Can't find symbol %s\n", #f); \
101         goto sym_not_found; \
102     }
103 #include "gl_api.h"
104 #undef GL_API_FUNCTION
105
106     /* And now calls the function to initialize the various fields for the rendering devices */
107     ret_value = d3ddevice_init_at_startup(gl_handle);
108
109     wine_dlclose(gl_handle, NULL, 0);
110     gl_handle = NULL;
111     return ret_value;
112     
113 sym_not_found:
114     WARN("Wine cannot find certain functions that it needs inside the OpenGL\n"
115          "graphics library.  To enable Wine to use OpenGL please upgrade\n"
116          "your OpenGL libraries\n");
117     wine_dlclose(gl_handle, NULL, 0);
118     gl_handle = NULL;
119     return FALSE;
120 }
121
122 #endif /* HAVE_OPENGL */
123
124 /***********************************************************************
125  *              DirectDrawEnumerateExA (DDRAW.@)
126  */
127 HRESULT WINAPI DirectDrawEnumerateExA(
128     LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags)
129 {
130     int i;
131     TRACE("(%p,%p, %08lx)\n", lpCallback, lpContext, dwFlags);
132
133     if (TRACE_ON(ddraw)) {
134         TRACE("  Flags : ");
135         if (dwFlags & DDENUM_ATTACHEDSECONDARYDEVICES)
136             TRACE("DDENUM_ATTACHEDSECONDARYDEVICES ");
137         if (dwFlags & DDENUM_DETACHEDSECONDARYDEVICES)
138             TRACE("DDENUM_DETACHEDSECONDARYDEVICES ");
139         if (dwFlags & DDENUM_NONDISPLAYDEVICES)
140             TRACE("DDENUM_NONDISPLAYDEVICES ");
141         TRACE("\n");
142     }
143
144     for (i=0; i<DDRAW_num_drivers; i++)
145     {
146         TRACE("Enumerating %s/%s interface\n",
147               DDRAW_drivers[i]->info->szDriver,
148               DDRAW_drivers[i]->info->szDescription);
149
150         /* We have to pass NULL from the primary display device.
151          * RoadRage chapter 6's enumeration routine expects it. */
152         if (!lpCallback((DDRAW_default_driver == i) ? NULL
153                         :(LPGUID)&DDRAW_drivers[i]->info->guidDeviceIdentifier,
154                         (LPSTR)DDRAW_drivers[i]->info->szDescription,
155                         (LPSTR)DDRAW_drivers[i]->info->szDriver,
156                         lpContext, 0))
157             return DD_OK;
158     }
159
160     /* Unsupported flags */
161     if (dwFlags & DDENUM_NONDISPLAYDEVICES) {
162         FIXME("no non-display devices supported.\n");
163     }
164     if (dwFlags & DDENUM_DETACHEDSECONDARYDEVICES) {
165         FIXME("no detached secondary devices supported.\n");
166     }
167
168     return DD_OK;
169 }
170
171 /***********************************************************************
172  *              DirectDrawEnumerateExW (DDRAW.@)
173  */
174
175 static BOOL CALLBACK DirectDrawEnumerateExProcW(
176     GUID *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName,
177     LPVOID lpContext, HMONITOR hm)
178 {
179     INT len;
180     BOOL bResult;
181     LPWSTR lpDriverDescriptionW, lpDriverNameW;
182     DirectDrawEnumerateProcData *pEPD = (DirectDrawEnumerateProcData*)lpContext;
183
184     len = MultiByteToWideChar( CP_ACP, 0, lpDriverDescription, -1, NULL, 0 );
185     lpDriverDescriptionW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
186     MultiByteToWideChar( CP_ACP, 0, lpDriverDescription, -1, lpDriverDescriptionW, len );
187
188     len = MultiByteToWideChar( CP_ACP, 0, lpDriverName, -1, NULL, 0 );
189     lpDriverNameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
190     MultiByteToWideChar( CP_ACP, 0, lpDriverName, -1, lpDriverNameW, len );
191
192     bResult = (*(LPDDENUMCALLBACKEXW *) pEPD->lpCallback)(lpGUID, lpDriverDescriptionW,
193                                                           lpDriverNameW, pEPD->lpContext, hm);
194
195     HeapFree(GetProcessHeap(), 0, lpDriverDescriptionW);
196     HeapFree(GetProcessHeap(), 0, lpDriverNameW);
197     return bResult;
198 }
199
200 HRESULT WINAPI DirectDrawEnumerateExW(
201   LPDDENUMCALLBACKEXW lpCallback, LPVOID lpContext, DWORD dwFlags)
202 {
203     DirectDrawEnumerateProcData epd;
204     epd.lpCallback = (LPVOID) lpCallback;
205     epd.lpContext = lpContext;
206
207     return DirectDrawEnumerateExA(DirectDrawEnumerateExProcW, (LPVOID) &epd, 0);
208 }
209
210 /***********************************************************************
211  *              DirectDrawEnumerateA (DDRAW.@)
212  */
213
214 static BOOL CALLBACK DirectDrawEnumerateProcA(
215         GUID *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName,
216         LPVOID lpContext, HMONITOR hm)
217 {
218     DirectDrawEnumerateProcData *pEPD = (DirectDrawEnumerateProcData*)lpContext;
219
220     return ((LPDDENUMCALLBACKA) pEPD->lpCallback)(
221         lpGUID, lpDriverDescription, lpDriverName, pEPD->lpContext);
222 }
223
224 HRESULT WINAPI DirectDrawEnumerateA(
225   LPDDENUMCALLBACKA lpCallback, LPVOID lpContext)
226 {
227     DirectDrawEnumerateProcData epd;
228     epd.lpCallback = (LPVOID) lpCallback;
229     epd.lpContext = lpContext;
230
231     return DirectDrawEnumerateExA(DirectDrawEnumerateProcA, (LPVOID) &epd, 0);
232 }
233
234 /***********************************************************************
235  *              DirectDrawEnumerateW (DDRAW.@)
236  */
237
238 static BOOL WINAPI DirectDrawEnumerateProcW(
239   GUID *lpGUID, LPWSTR lpDriverDescription, LPWSTR lpDriverName,
240   LPVOID lpContext, HMONITOR hm)
241 {
242     DirectDrawEnumerateProcData *pEPD = (DirectDrawEnumerateProcData*)lpContext;
243
244     return ((LPDDENUMCALLBACKW) pEPD->lpCallback)(
245         lpGUID, lpDriverDescription, lpDriverName, pEPD->lpContext);
246 }
247
248 HRESULT WINAPI DirectDrawEnumerateW(
249   LPDDENUMCALLBACKW lpCallback, LPVOID lpContext)
250 {
251     DirectDrawEnumerateProcData epd;
252     epd.lpCallback = (LPVOID) lpCallback;
253     epd.lpContext = lpContext;
254
255     return DirectDrawEnumerateExW(DirectDrawEnumerateProcW, (LPVOID) &epd, 0);
256 }
257
258 /***********************************************************************
259  *              DirectDrawCreate (DDRAW.@)
260  */
261
262 const ddraw_driver* DDRAW_FindDriver(const GUID* pGUID)
263 {
264     static const GUID zeroGUID; /* gets zero-inited */
265
266     TRACE("(%s)\n", pGUID ? debugstr_guid(pGUID) : "(null)");
267
268     if (DDRAW_num_drivers == 0) return NULL;
269
270     if (pGUID == (LPGUID)DDCREATE_EMULATIONONLY
271         || pGUID == (LPGUID)DDCREATE_HARDWAREONLY)
272         pGUID = NULL;
273
274     if (pGUID == NULL || memcmp(pGUID, &zeroGUID, sizeof(GUID)) == 0)
275     {
276         /* Use the default driver. */
277         return DDRAW_drivers[DDRAW_default_driver];
278     }
279     else
280     {
281         /* Look for a matching GUID. */
282
283         int i;
284         for (i=0; i < DDRAW_num_drivers; i++)
285         {
286             if (IsEqualGUID(pGUID,
287                             &DDRAW_drivers[i]->info->guidDeviceIdentifier))
288                 break;
289         }
290
291         if (i < DDRAW_num_drivers)
292         {
293             return DDRAW_drivers[i];
294         }
295         else
296         {
297             ERR("(%s): did not recognize requested GUID.\n",debugstr_guid(pGUID));
298             return NULL;
299         }
300     }
301 }
302
303 static HRESULT DDRAW_Create(
304         LPGUID lpGUID, LPVOID *lplpDD, LPUNKNOWN pUnkOuter, REFIID iid, BOOL ex
305 ) {
306     const ddraw_driver* driver;
307     LPDIRECTDRAW7 pDD;
308     HRESULT hr;
309
310     TRACE("(%s,%p,%p,%d)\n", debugstr_guid(lpGUID), lplpDD, pUnkOuter, ex);
311
312     if (DDRAW_num_drivers == 0)
313     {
314         WARN("no DirectDraw drivers registered\n");
315         return DDERR_INVALIDDIRECTDRAWGUID;
316     }
317
318     if (lpGUID == (LPGUID)DDCREATE_EMULATIONONLY
319         || lpGUID == (LPGUID)DDCREATE_HARDWAREONLY)
320         lpGUID = NULL;
321
322     if (pUnkOuter != NULL)
323         return DDERR_INVALIDPARAMS; /* CLASS_E_NOAGGREGATION? */
324
325     driver = DDRAW_FindDriver(lpGUID);
326     if (driver == NULL) return DDERR_INVALIDDIRECTDRAWGUID;
327
328     hr = driver->create(lpGUID, &pDD, pUnkOuter, ex);
329     if (FAILED(hr)) return hr;
330
331     hr = IDirectDraw7_QueryInterface(pDD, iid, lplpDD);
332     IDirectDraw7_Release(pDD);
333     return hr;
334 }
335
336 /***********************************************************************
337  *              DirectDrawCreate (DDRAW.@)
338  *
339  * Only creates legacy IDirectDraw interfaces.
340  * Cannot create IDirectDraw7 interfaces.
341  * In theory.
342  */
343 HRESULT WINAPI DirectDrawCreate(
344         LPGUID lpGUID, LPDIRECTDRAW* lplpDD, LPUNKNOWN pUnkOuter
345 ) {
346     TRACE("(%s,%p,%p)\n", debugstr_guid(lpGUID), lplpDD, pUnkOuter);
347     return DDRAW_Create(lpGUID, (LPVOID*) lplpDD, pUnkOuter, &IID_IDirectDraw, FALSE);
348 }
349
350 /***********************************************************************
351  *              DirectDrawCreateEx (DDRAW.@)
352  *
353  * Only creates new IDirectDraw7 interfaces.
354  * Supposed to fail if legacy interfaces are requested.
355  * In theory.
356  */
357 HRESULT WINAPI DirectDrawCreateEx(
358         LPGUID lpGUID, LPVOID* lplpDD, REFIID iid, LPUNKNOWN pUnkOuter
359 ) {
360     TRACE("(%s,%p,%s,%p)\n", debugstr_guid(lpGUID), lplpDD, debugstr_guid(iid), pUnkOuter);
361
362     if (!IsEqualGUID(iid, &IID_IDirectDraw7))
363         return DDERR_INVALIDPARAMS;
364
365     return DDRAW_Create(lpGUID, lplpDD, pUnkOuter, iid, TRUE);
366 }
367
368 extern HRESULT Uninit_DirectDraw_Create(const GUID*, LPDIRECTDRAW7*,
369                                         LPUNKNOWN, BOOL);
370
371 /* This is for the class factory. */
372 static HRESULT DDRAW_CreateDirectDraw(IUnknown* pUnkOuter, REFIID iid,
373                                       LPVOID* ppObj)
374 {
375     LPDIRECTDRAW7 pDD;
376     HRESULT hr;
377     BOOL ex;
378
379     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppObj);
380     
381     /* This is a mighty hack :-) */
382     if (IsEqualGUID(iid, &IID_IDirectDraw7))
383         ex = TRUE;
384     else
385         ex = FALSE;
386     
387     hr = Uninit_DirectDraw_Create(NULL, &pDD, pUnkOuter, ex);
388     if (FAILED(hr)) return hr;
389
390     hr = IDirectDraw7_QueryInterface(pDD, iid, ppObj);
391     IDirectDraw_Release(pDD);
392     return hr;
393 }
394
395 /******************************************************************************
396  * DirectDraw ClassFactory
397  */
398 typedef struct {
399     ICOM_VFIELD_MULTI(IClassFactory);
400
401     DWORD ref;
402     HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, REFIID iid,
403                                  LPVOID *ppObj);
404 } IClassFactoryImpl;
405
406 struct object_creation_info
407 {
408     const CLSID *clsid;
409     HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, REFIID riid,
410                                  LPVOID *ppObj);
411 };
412
413 /* There should be more, but these are the only ones listed in the header
414  * file. */
415 extern HRESULT DDRAW_CreateDirectDrawClipper(IUnknown *pUnkOuter, REFIID riid,
416                                              LPVOID *ppObj);
417
418 static const struct object_creation_info object_creation[] =
419 {
420     { &CLSID_DirectDraw,        DDRAW_CreateDirectDraw },
421     { &CLSID_DirectDraw7,       DDRAW_CreateDirectDraw },
422     { &CLSID_DirectDrawClipper, DDRAW_CreateDirectDrawClipper }
423 };
424
425 static HRESULT WINAPI
426 DDCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj)
427 {
428     ICOM_THIS(IClassFactoryImpl,iface);
429
430     TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppobj);
431     
432     if (IsEqualGUID(riid, &IID_IUnknown)
433         || IsEqualGUID(riid, &IID_IClassFactory))
434     {
435         IClassFactory_AddRef(iface);
436         *ppobj = This;
437         return S_OK;
438     }
439
440     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppobj);
441     return E_NOINTERFACE;
442 }
443
444 static ULONG WINAPI DDCF_AddRef(LPCLASSFACTORY iface) {
445     ICOM_THIS(IClassFactoryImpl,iface);
446
447     TRACE("(%p)->() incrementing from %ld.\n", This, This->ref);
448     
449     return ++(This->ref);
450 }
451
452 static ULONG WINAPI DDCF_Release(LPCLASSFACTORY iface) {
453     ICOM_THIS(IClassFactoryImpl,iface);
454
455     TRACE("(%p)->() decrementing from %ld.\n", This, This->ref);
456
457     if (--This->ref == 0)
458         HeapFree(GetProcessHeap(), 0, This);
459
460     return This->ref;
461 }
462
463
464 static HRESULT WINAPI DDCF_CreateInstance(
465         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
466 ) {
467     ICOM_THIS(IClassFactoryImpl,iface);
468
469     TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
470
471     return This->pfnCreateInstance(pOuter, riid, ppobj);
472 }
473
474 static HRESULT WINAPI DDCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
475     ICOM_THIS(IClassFactoryImpl,iface);
476     FIXME("(%p)->(%d),stub!\n",This,dolock);
477     return S_OK;
478 }
479
480 static ICOM_VTABLE(IClassFactory) DDCF_Vtbl =
481 {
482     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
483     DDCF_QueryInterface,
484     DDCF_AddRef,
485     DDCF_Release,
486     DDCF_CreateInstance,
487     DDCF_LockServer
488 };
489
490 /*******************************************************************************
491  * DllGetClassObject [DDRAW.@]
492  * Retrieves class object from a DLL object
493  *
494  * NOTES
495  *    Docs say returns STDAPI
496  *
497  * PARAMS
498  *    rclsid [I] CLSID for the class object
499  *    riid   [I] Reference to identifier of interface for class object
500  *    ppv    [O] Address of variable to receive interface pointer for riid
501  *
502  * RETURNS
503  *    Success: S_OK
504  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
505  *             E_UNEXPECTED
506  */
507 DWORD WINAPI DDRAW_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
508 {
509     unsigned int i;
510     IClassFactoryImpl *factory;
511
512     TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
513
514     if ( !IsEqualGUID( &IID_IClassFactory, riid )
515          && ! IsEqualGUID( &IID_IUnknown, riid) )
516         return E_NOINTERFACE;
517
518     for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
519     {
520         if (IsEqualGUID(object_creation[i].clsid, rclsid))
521             break;
522     }
523
524     if (i == sizeof(object_creation)/sizeof(object_creation[0]))
525     {
526         FIXME("%s: no class found.\n", debugstr_guid(rclsid));
527         return CLASS_E_CLASSNOTAVAILABLE;
528     }
529
530     factory = HeapAlloc(GetProcessHeap(), 0, sizeof(*factory));
531     if (factory == NULL) return E_OUTOFMEMORY;
532
533     ICOM_INIT_INTERFACE(factory, IClassFactory, DDCF_Vtbl);
534     factory->ref = 1;
535
536     factory->pfnCreateInstance = object_creation[i].pfnCreateInstance;
537
538     *ppv = ICOM_INTERFACE(factory, IClassFactory);
539     return S_OK;
540 }
541
542
543 /*******************************************************************************
544  * DllCanUnloadNow [DDRAW.@]  Determines whether the DLL is in use.
545  *
546  * RETURNS
547  *    Success: S_OK
548  *    Failure: S_FALSE
549  */
550 DWORD WINAPI DDRAW_DllCanUnloadNow(void) {
551     FIXME("(void): stub\n");
552     return S_FALSE;
553 }
554
555 /******************************************************************************
556  * Initialisation
557  */
558
559 /* Choose which driver is considered the primary display driver. It will
560  * be created when we get a NULL guid for the DirectDrawCreate(Ex). */
561 static int DDRAW_ChooseDefaultDriver(void)
562 {
563     int i;
564     int best = 0;
565     int best_score = 0;
566
567     assert(DDRAW_num_drivers > 0);
568
569     /* This algorithm is really stupid. */
570     for (i=0; i < DDRAW_num_drivers; i++)
571     {
572         if (DDRAW_drivers[i]->preference > best_score)
573         {
574             best_score = DDRAW_drivers[i]->preference;
575             best = i;
576         }
577     }
578
579     assert(best_score > 0);
580
581     return best;
582 }
583
584 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
585 {
586     /* If we were sufficiently cool, DDraw drivers would just be COM
587      * objects, registered with a particular component category. */
588
589     DDRAW_HAL_Init(hInstDLL, fdwReason, lpv);
590     DDRAW_User_Init(hInstDLL, fdwReason, lpv);
591
592     if (fdwReason == DLL_PROCESS_ATTACH)
593     {
594         HMODULE mod;
595
596         DisableThreadLibraryCalls(hInstDLL);
597
598         mod = GetModuleHandleA( "x11drv.dll" );
599         if (mod)
600         {
601             wine_tsx11_lock_ptr   = (void *)GetProcAddress( mod, "wine_tsx11_lock" );
602             wine_tsx11_unlock_ptr = (void *)GetProcAddress( mod, "wine_tsx11_unlock" );
603         }
604 #ifdef HAVE_OPENGL
605         opengl_initialized = DDRAW_bind_to_opengl();
606 #endif /* HAVE_OPENGL */
607     }
608
609     if (DDRAW_num_drivers > 0)
610         DDRAW_default_driver = DDRAW_ChooseDefaultDriver();
611
612     return TRUE;
613 }
614
615 /* Register a direct draw driver. This should be called from your init
616  * function. (That's why there is no locking: your init func is called from
617  * our DllInit, which is serialised.) */
618 void DDRAW_register_driver(const ddraw_driver *driver)
619 {
620     int i;
621
622     for (i = 0; i < DDRAW_num_drivers; i++)
623     {
624         if (DDRAW_drivers[i] == driver)
625         {
626             ERR("Driver reregistering %p\n", driver);
627             return;
628         }
629     }
630
631     if (DDRAW_num_drivers == sizeof(DDRAW_drivers)/sizeof(DDRAW_drivers[0]))
632     {
633         ERR("too many DDRAW drivers\n");
634         return;
635     }
636
637     DDRAW_drivers[DDRAW_num_drivers++] = driver;
638 }
639
640 /* This totally doesn't belong here. */
641 LONG DDRAW_width_bpp_to_pitch(DWORD width, DWORD bpp)
642 {
643     LONG pitch;
644
645     assert(bpp != 0); /* keeps happening... */
646
647     if (bpp == 15) bpp = 16;
648     pitch = width * (bpp / 8);
649     return pitch + (8 - (pitch % 8)) % 8;
650 }