main.c 33.2 KB
Newer Older
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 Dsinger
7
 * Copyright 2008 Denver Gingerich
8 9 10
 *
 * This file contains the (internal) driver registration functions,
 * driver enumeration APIs and DirectDraw creation functions.
11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 34 35
#include <string.h>
#include <stdlib.h>

36 37
#define COBJMACROS

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 46

#include "ddraw.h"
#include "d3d.h"
47 48

#include "ddraw_private.h"
49

50 51 52 53 54
typedef IWineD3D* (WINAPI *fnWineDirect3DCreate)(UINT, UINT, IUnknown *);

static HMODULE hWineD3D = (HMODULE) -1;
static fnWineDirect3DCreate pWineDirect3DCreate;

55
WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
56

57 58
/* The configured default surface */
WINED3DSURFTYPE DefaultSurfaceType = SURFACE_UNKNOWN;
59

60
/* DDraw list and critical section */
61 62
static struct list global_ddraw_list = LIST_INIT(global_ddraw_list);

63
static CRITICAL_SECTION_DEBUG ddraw_cs_debug =
64
{
65 66 67 68
    0, 0, &ddraw_cs,
    { &ddraw_cs_debug.ProcessLocksList,
    &ddraw_cs_debug.ProcessLocksList },
    0, 0, { (DWORD_PTR)(__FILE__ ": ddraw_cs") }
69
};
70
CRITICAL_SECTION ddraw_cs = { &ddraw_cs_debug, -1, 0, 0, 0, 0 };
71

72 73 74
/* value of ForceRefreshRate */
DWORD force_refresh_rate = 0;

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
/***********************************************************************
 *
 * Helper function for DirectDrawCreate and friends
 * Creates a new DDraw interface with the given REFIID
 *
 * Interfaces that can be created:
 *  IDirectDraw, IDirectDraw2, IDirectDraw4, IDirectDraw7
 *  IDirect3D, IDirect3D2, IDirect3D3, IDirect3D7. (Does Windows return
 *  IDirect3D interfaces?)
 *
 * Arguments:
 *  guid: ID of the requested driver, NULL for the default driver.
 *        The GUID can be queried with DirectDrawEnumerate(Ex)A/W
 *  DD: Used to return the pointer to the created object
 *  UnkOuter: For aggregation, which is unsupported. Must be NULL
 *  iid: requested version ID.
 *
 * Returns:
93
 *  DD_OK if the Interface was created successfully
94 95 96 97 98
 *  CLASS_E_NOAGGREGATION if UnkOuter is not NULL
 *  E_OUTOFMEMORY if some allocation failed
 *
 ***********************************************************************/
static HRESULT
99
DDRAW_Create(const GUID *guid,
100 101 102
             void **DD,
             IUnknown *UnkOuter,
             REFIID iid)
103
{
104 105 106 107 108 109
    IDirectDrawImpl *This = NULL;
    HRESULT hr;
    IWineD3D *wineD3D = NULL;
    IWineD3DDevice *wineD3DDevice = NULL;
    HDC hDC;
    WINED3DDEVTYPE devicetype;
110

111
    TRACE("(%s,%p,%p)\n", debugstr_guid(guid), DD, UnkOuter);
112

113 114
    *DD = NULL;

115 116 117 118 119 120 121 122 123 124
    /* We don't care about this guids. Well, there's no special guid anyway
     * OK, we could
     */
    if (guid == (GUID *) DDCREATE_EMULATIONONLY)
    {
        /* Use the reference device id. This doesn't actually change anything,
         * WineD3D always uses OpenGL for D3D rendering. One could make it request
         * indirect rendering
         */
        devicetype = WINED3DDEVTYPE_REF;
125
    }
126 127 128
    else if(guid == (GUID *) DDCREATE_HARDWAREONLY)
    {
        devicetype = WINED3DDEVTYPE_HAL;
129
    }
130 131 132
    else
    {
        devicetype = 0;
133 134
    }

135
    /* DDraw doesn't support aggregation, according to msdn */
136 137
    if (UnkOuter != NULL)
        return CLASS_E_NOAGGREGATION;
138

139 140 141 142 143 144
    /* DirectDraw creation comes here */
    This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectDrawImpl));
    if(!This)
    {
        ERR("Out of memory when creating DirectDraw\n");
        return E_OUTOFMEMORY;
145
    }
146

147 148 149 150 151 152
    /* The interfaces:
     * IDirectDraw and IDirect3D are the same object,
     * QueryInterface is used to get other interfaces.
     */
    ICOM_INIT_INTERFACE(This, IDirectDraw,  IDirectDraw1_Vtbl);
    ICOM_INIT_INTERFACE(This, IDirectDraw2, IDirectDraw2_Vtbl);
153
    ICOM_INIT_INTERFACE(This, IDirectDraw3, IDirectDraw3_Vtbl);
154 155 156 157 158 159 160 161 162 163 164 165 166 167
    ICOM_INIT_INTERFACE(This, IDirectDraw4, IDirectDraw4_Vtbl);
    ICOM_INIT_INTERFACE(This, IDirectDraw7, IDirectDraw7_Vtbl);
    ICOM_INIT_INTERFACE(This, IDirect3D,  IDirect3D1_Vtbl);
    ICOM_INIT_INTERFACE(This, IDirect3D2, IDirect3D2_Vtbl);
    ICOM_INIT_INTERFACE(This, IDirect3D3, IDirect3D3_Vtbl);
    ICOM_INIT_INTERFACE(This, IDirect3D7, IDirect3D7_Vtbl);

    /* See comments in IDirectDrawImpl_CreateNewSurface for a description
     * of this member.
     * Read from a registry key, should add a winecfg option later
     */
    This->ImplType = DefaultSurfaceType;

    /* Get the current screen settings */
168
    hDC = GetDC(0);
169
    This->orig_bpp = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
170
    ReleaseDC(0, hDC);
171 172 173
    This->orig_width = GetSystemMetrics(SM_CXSCREEN);
    This->orig_height = GetSystemMetrics(SM_CYSCREEN);

174 175 176 177
    if (hWineD3D == (HMODULE) -1)
    {
        hWineD3D = LoadLibraryA("wined3d");
        if (hWineD3D)
178
        {
179
            pWineDirect3DCreate = (fnWineDirect3DCreate) GetProcAddress(hWineD3D, "WineDirect3DCreate");
180 181
            pWineDirect3DCreateClipper = (fnWineDirect3DCreateClipper) GetProcAddress(hWineD3D, "WineDirect3DCreateClipper");
        }
182 183 184 185 186
    }

    if (!hWineD3D)
    {
        ERR("Couldn't load WineD3D - OpenGL libs not present?\n");
187
        hr = DDERR_NODIRECTDRAWSUPPORT;
188 189 190
        goto err_out;
    }

191 192 193 194 195 196
    /* Initialize WineD3D
     *
     * All Rendering (2D and 3D) is relayed to WineD3D,
     * but DirectDraw specific management, like DDSURFACEDESC and DDPIXELFORMAT
     * structure handling is handled in this lib.
     */
197
    wineD3D = pWineDirect3DCreate(0 /* SDKVersion */, 7 /* DXVersion */, (IUnknown *) This /* Parent */);
198
    if(!wineD3D)
199
    {
200 201 202
        ERR("Failed to initialise WineD3D\n");
        hr = E_OUTOFMEMORY;
        goto err_out;
203
    }
204 205 206 207 208 209
    This->wineD3D = wineD3D;
    TRACE("WineD3D created at %p\n", wineD3D);

    /* Initialized member...
     *
     * It is set to false at creation time, and set to true in
210
     * IDirectDraw7::Initialize. Its sole purpose is to return DD_OK on
211 212 213 214 215 216 217
     * initialize only once
     */
    This->initialized = FALSE;

    /* Initialize WineD3DDevice
     *
     * It is used for screen setup, surface and palette creation
218
     * When a Direct3DDevice7 is created, the D3D capabilities of WineD3D are
219 220 221 222 223 224 225 226 227 228 229
     * initialized
     */
    hr = IWineD3D_CreateDevice(wineD3D,
                               0 /*D3D_ADAPTER_DEFAULT*/,
                               devicetype,
                               NULL, /* FocusWindow, don't know yet */
                               0, /* BehaviorFlags */
                               &wineD3DDevice,
                               (IUnknown *) ICOM_INTERFACE(This, IDirectDraw7));
    if(FAILED(hr))
    {
230
        ERR("Failed to create a wineD3DDevice, result = %x\n", hr);
231
        goto err_out;
232
    }
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
    This->wineD3DDevice = wineD3DDevice;
    TRACE("wineD3DDevice created at %p\n", This->wineD3DDevice);

    /* Register the window class
     *
     * It is used to create a hidden window for D3D
     * rendering, if the application didn't pass one.
     * It can also be used for Creating a device window
     * from SetCooperativeLevel
     *
     * The name: DDRAW_<address>. The classname is
     * 32 bit long, so a 64 bit address will fit nicely
     * (Will this be compiled for 64 bit anyway?)
     *
     */
    sprintf(This->classname, "DDRAW_%p", This);

    memset(&This->wnd_class, 0, sizeof(This->wnd_class));
    This->wnd_class.style = CS_HREDRAW | CS_VREDRAW;
    This->wnd_class.lpfnWndProc = DefWindowProcA;
    This->wnd_class.cbClsExtra = 0;
    This->wnd_class.cbWndExtra = 0;
    This->wnd_class.hInstance = GetModuleHandleA(0);
    This->wnd_class.hIcon = 0;
    This->wnd_class.hCursor = 0;
    This->wnd_class.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
    This->wnd_class.lpszMenuName = NULL;
    This->wnd_class.lpszClassName = This->classname;
    if(!RegisterClassA(&This->wnd_class))
    {
        ERR("RegisterClassA failed!\n");
        goto err_out;
265 266
    }

267 268 269 270 271
    /* Get the amount of video memory */
    This->total_vidmem = IWineD3DDevice_GetAvailableTextureMem(This->wineD3DDevice);

    /* Initialize the caps */
    This->caps.dwSize = sizeof(This->caps);
272
/* do not report DDCAPS_OVERLAY and friends since we don't support overlays */
273 274 275
#define BLIT_CAPS (DDCAPS_BLT | DDCAPS_BLTCOLORFILL | DDCAPS_BLTDEPTHFILL \
          | DDCAPS_BLTSTRETCH | DDCAPS_CANBLTSYSMEM | DDCAPS_CANCLIP	  \
          | DDCAPS_CANCLIPSTRETCHED | DDCAPS_COLORKEY			  \
276
          | DDCAPS_COLORKEYHWASSIST | DDCAPS_ALIGNBOUNDARYSRC )
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
#define CKEY_CAPS (DDCKEYCAPS_DESTBLT | DDCKEYCAPS_SRCBLT)
#define FX_CAPS (DDFXCAPS_BLTALPHA | DDFXCAPS_BLTMIRRORLEFTRIGHT	\
                | DDFXCAPS_BLTMIRRORUPDOWN | DDFXCAPS_BLTROTATION90	\
                | DDFXCAPS_BLTSHRINKX | DDFXCAPS_BLTSHRINKXN		\
                | DDFXCAPS_BLTSHRINKY | DDFXCAPS_BLTSHRINKXN		\
                | DDFXCAPS_BLTSTRETCHX | DDFXCAPS_BLTSTRETCHXN		\
                | DDFXCAPS_BLTSTRETCHY | DDFXCAPS_BLTSTRETCHYN)
    This->caps.dwCaps |= DDCAPS_GDI | DDCAPS_PALETTE | BLIT_CAPS;

    This->caps.dwCaps2 |= DDCAPS2_CERTIFIED | DDCAPS2_NOPAGELOCKREQUIRED |
                          DDCAPS2_PRIMARYGAMMA | DDCAPS2_WIDESURFACES |
                          DDCAPS2_CANRENDERWINDOWED;
    This->caps.dwCKeyCaps |= CKEY_CAPS;
    This->caps.dwFXCaps |= FX_CAPS;
    This->caps.dwPalCaps |= DDPCAPS_8BIT | DDPCAPS_PRIMARYSURFACE;
    This->caps.dwVidMemTotal = This->total_vidmem;
    This->caps.dwVidMemFree = This->total_vidmem;
    This->caps.dwSVBCaps |= BLIT_CAPS;
    This->caps.dwSVBCKeyCaps |= CKEY_CAPS;
    This->caps.dwSVBFXCaps |= FX_CAPS;
    This->caps.dwVSBCaps |= BLIT_CAPS;
    This->caps.dwVSBCKeyCaps |= CKEY_CAPS;
    This->caps.dwVSBFXCaps |= FX_CAPS;
    This->caps.dwSSBCaps |= BLIT_CAPS;
    This->caps.dwSSBCKeyCaps |= CKEY_CAPS;
    This->caps.dwSSBFXCaps |= FX_CAPS;
    This->caps.ddsCaps.dwCaps |= DDSCAPS_ALPHA | DDSCAPS_BACKBUFFER |
                                 DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER |
                                 DDSCAPS_OFFSCREENPLAIN | DDSCAPS_PALETTE |
                                 DDSCAPS_PRIMARYSURFACE | DDSCAPS_SYSTEMMEMORY |
                                 DDSCAPS_VIDEOMEMORY | DDSCAPS_VISIBLE;
    /* Hacks for D3D code */
    /* TODO: Check if WineD3D has 3D enabled
       Need opengl surfaces or auto for 3D
     */
    if(This->ImplType == 0 || This->ImplType == SURFACE_OPENGL)
    {
        This->caps.dwCaps |= DDCAPS_3D;
        This->caps.ddsCaps.dwCaps |= DDSCAPS_3DDEVICE | DDSCAPS_MIPMAP | DDSCAPS_TEXTURE | DDSCAPS_ZBUFFER;
    }
    This->caps.ddsOldCaps.dwCaps = This->caps.ddsCaps.dwCaps;

#undef BLIT_CAPS
#undef CKEY_CAPS
#undef FX_CAPS

323
    list_init(&This->surface_list);
324
    list_add_head(&global_ddraw_list, &This->ddraw_list_entry);
325

326 327 328
    /* Call QueryInterface to get the pointer to the requested interface. This also initializes
     * The required refcount
     */
329 330 331 332 333 334 335
    hr = IDirectDraw7_QueryInterface( ICOM_INTERFACE(This, IDirectDraw7), iid, DD);
    if(SUCCEEDED(hr)) return DD_OK;

err_out:
    /* Let's hope we never need this ;) */
    if(wineD3DDevice) IWineD3DDevice_Release(wineD3DDevice);
    if(wineD3D) IWineD3D_Release(wineD3D);
336
    if(This) HeapFree(GetProcessHeap(), 0, This->decls);
337
    HeapFree(GetProcessHeap(), 0, This);
338
    return hr;
339 340 341
}

/***********************************************************************
342 343 344 345 346 347 348 349 350 351
 * DirectDrawCreate (DDRAW.@)
 *
 * Creates legacy DirectDraw Interfaces. Can't create IDirectDraw7
 * interfaces in theory
 *
 * Arguments, return values: See DDRAW_Create
 *
 ***********************************************************************/
HRESULT WINAPI
DirectDrawCreate(GUID *GUID,
352
                 LPDIRECTDRAW *DD,
353
                 IUnknown *UnkOuter)
354
{
355
    HRESULT hr;
356
    TRACE("(%s,%p,%p)\n", debugstr_guid(GUID), DD, UnkOuter);
357

358 359 360 361
    EnterCriticalSection(&ddraw_cs);
    hr = DDRAW_Create(GUID, (void **) DD, UnkOuter, &IID_IDirectDraw);
    LeaveCriticalSection(&ddraw_cs);
    return hr;
362 363 364
}

/***********************************************************************
365 366 367 368 369 370 371 372 373 374
 * DirectDrawCreateEx (DDRAW.@)
 *
 * Only creates new IDirectDraw7 interfaces, supposed to fail if legacy
 * interfaces are requested.
 *
 * Arguments, return values: See DDRAW_Create
 *
 ***********************************************************************/
HRESULT WINAPI
DirectDrawCreateEx(GUID *GUID,
375
                   LPVOID *DD,
376 377
                   REFIID iid,
                   IUnknown *UnkOuter)
378
{
379
    HRESULT hr;
380
    TRACE("(%s,%p,%s,%p)\n", debugstr_guid(GUID), DD, debugstr_guid(iid), UnkOuter);
381

382 383
    if (!IsEqualGUID(iid, &IID_IDirectDraw7))
        return DDERR_INVALIDPARAMS;
384

385 386 387 388
    EnterCriticalSection(&ddraw_cs);
    hr = DDRAW_Create(GUID, DD, UnkOuter, iid);
    LeaveCriticalSection(&ddraw_cs);
    return hr;
389 390
}

391
/***********************************************************************
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
 * DirectDrawEnumerateA (DDRAW.@)
 *
 * Enumerates legacy ddraw drivers, ascii version. We only have one
 * driver, which relays to WineD3D. If we were sufficiently cool,
 * we could offer various interfaces, which use a different default surface
 * implementation, but I think it's better to offer this choice in
 * winecfg, because some apps use the default driver, so we would need
 * a winecfg option anyway, and there shouldn't be 2 ways to set one setting
 *
 * Arguments:
 *  Callback: Callback function from the app
 *  Context: Argument to the call back.
 *
 * Returns:
 *  DD_OK on success
 *  E_INVALIDARG if the Callback caused a page fault
 *
 *
 ***********************************************************************/
HRESULT WINAPI
DirectDrawEnumerateA(LPDDENUMCALLBACKA Callback,
413
                     LPVOID Context)
414
{
415
    BOOL stop = FALSE;
416

417 418 419
    TRACE(" Enumerating default DirectDraw HAL interface\n");
    /* We only have one driver */
    __TRY
420
    {
421 422 423 424
        static CHAR driver_desc[] = "DirectDraw HAL",
        driver_name[] = "display";

        stop = !Callback(NULL, driver_desc, driver_name, Context);
425
    }
426
    __EXCEPT_PAGE_FAULT
427
    {
428
        return E_INVALIDARG;
429
    }
430
    __ENDTRY
431

432 433 434
    TRACE(" End of enumeration\n");
    return DD_OK;
}
435

436 437 438 439 440 441 442 443 444 445 446
/***********************************************************************
 * DirectDrawEnumerateExA (DDRAW.@)
 *
 * Enumerates DirectDraw7 drivers, ascii version. See
 * the comments above DirectDrawEnumerateA for more details.
 *
 * The Flag member is not supported right now.
 *
 ***********************************************************************/
HRESULT WINAPI
DirectDrawEnumerateExA(LPDDENUMCALLBACKEXA Callback,
447
                       LPVOID Context,
448 449 450 451
                       DWORD Flags)
{
    BOOL stop = FALSE;
    TRACE("Enumerating default DirectDraw HAL interface\n");
452

453 454
    /* We only have one driver by now */
    __TRY
455
    {
456 457 458
        static CHAR driver_desc[] = "DirectDraw HAL",
        driver_name[] = "display";

459
        /* QuickTime expects the description "DirectDraw HAL" */
460
        stop = !Callback(NULL, driver_desc, driver_name, Context, 0);
461
    }
462 463 464 465 466
    __EXCEPT_PAGE_FAULT
    {
        return E_INVALIDARG;
    }
    __ENDTRY;
467

468 469
    TRACE("End of enumeration\n");
    return DD_OK;
470 471
}

472
/***********************************************************************
473
 * DirectDrawEnumerateW (DDRAW.@)
474
 *
475 476 477 478 479 480
 * Enumerates legacy drivers, unicode version. See
 * the comments above DirectDrawEnumerateA for more details.
 *
 * The Flag member is not supported right now.
 *
 ***********************************************************************/
481

482
/***********************************************************************
483
 * DirectDrawEnumerateExW (DDRAW.@)
484
 *
485 486 487 488 489 490
 * Enumerates DirectDraw7 drivers, unicode version. See
 * the comments above DirectDrawEnumerateA for more details.
 *
 * The Flag member is not supported right now.
 *
 ***********************************************************************/
491

492 493 494
/***********************************************************************
 * Classfactory implementation.
 ***********************************************************************/
495

496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
/***********************************************************************
 * CF_CreateDirectDraw
 *
 * DDraw creation function for the class factory
 *
 * Params:
 *  UnkOuter: Set to NULL
 *  iid: ID of the wanted interface
 *  obj: Address to pass the interface pointer back
 *
 * Returns
 *  DD_OK / DDERR*, see DDRAW_Create
 *
 ***********************************************************************/
static HRESULT
CF_CreateDirectDraw(IUnknown* UnkOuter, REFIID iid,
                    void **obj)
513 514 515
{
    HRESULT hr;

516
    TRACE("(%p,%s,%p)\n", UnkOuter, debugstr_guid(iid), obj);
517

518
    EnterCriticalSection(&ddraw_cs);
519
    hr = DDRAW_Create(NULL, obj, UnkOuter, iid);
520
    LeaveCriticalSection(&ddraw_cs);
521
    return hr;
522 523
}

524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
/***********************************************************************
 * CF_CreateDirectDraw
 *
 * Clipper creation function for the class factory
 *
 * Params:
 *  UnkOuter: Set to NULL
 *  iid: ID of the wanted interface
 *  obj: Address to pass the interface pointer back
 *
 * Returns
 *  DD_OK / DDERR*, see DDRAW_Create
 *
 ***********************************************************************/
static HRESULT
CF_CreateDirectDrawClipper(IUnknown* UnkOuter, REFIID riid,
                              void **obj)
541
{
542 543 544
    HRESULT hr;
    IDirectDrawClipper *Clip;

545
    EnterCriticalSection(&ddraw_cs);
546
    hr = DirectDrawCreateClipper(0, &Clip, UnkOuter);
547 548 549 550 551
    if (hr != DD_OK)
    {
        LeaveCriticalSection(&ddraw_cs);
        return hr;
    }
552

553 554
    hr = IDirectDrawClipper_QueryInterface(Clip, riid, obj);
    IDirectDrawClipper_Release(Clip);
555 556

    LeaveCriticalSection(&ddraw_cs);
557 558
    return hr;
}
559 560 561

static const struct object_creation_info object_creation[] =
{
562 563 564
    { &CLSID_DirectDraw,        CF_CreateDirectDraw },
    { &CLSID_DirectDraw7,       CF_CreateDirectDraw },
    { &CLSID_DirectDrawClipper, CF_CreateDirectDrawClipper }
565 566
};

567 568 569 570 571 572 573 574 575 576 577 578 579 580
/*******************************************************************************
 * IDirectDrawClassFactory::QueryInterface
 *
 * QueryInterface for the class factory
 *
 * PARAMS
 *    riid   Reference to identifier of queried interface
 *    ppv    Address to return the interface pointer at
 *
 * RETURNS
 *    Success: S_OK
 *    Failure: E_NOINTERFACE
 *
 *******************************************************************************/
581
static HRESULT WINAPI
582 583 584
IDirectDrawClassFactoryImpl_QueryInterface(IClassFactory *iface,
                    REFIID riid,
                    void **obj)
585
{
586 587 588
    ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);

    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), obj);
589

590
    if (IsEqualGUID(riid, &IID_IUnknown)
591
        || IsEqualGUID(riid, &IID_IClassFactory))
592
    {
593 594 595
        IClassFactory_AddRef(iface);
        *obj = This;
        return S_OK;
596 597
    }

598
    WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),obj);
599 600 601
    return E_NOINTERFACE;
}

602 603 604 605 606 607 608 609 610 611 612
/*******************************************************************************
 * IDirectDrawClassFactory::AddRef
 *
 * AddRef for the class factory
 *
 * RETURNS
 *  The new refcount
 *
 *******************************************************************************/
static ULONG WINAPI
IDirectDrawClassFactoryImpl_AddRef(IClassFactory *iface)
613
{
614
    ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
615
    ULONG ref = InterlockedIncrement(&This->ref);
616

617
    TRACE("(%p)->() incrementing from %d.\n", This, ref - 1);
618

619
    return ref;
620 621
}

622 623 624 625 626 627 628 629 630 631 632 633
/*******************************************************************************
 * IDirectDrawClassFactory::Release
 *
 * Release for the class factory. If the refcount falls to 0, the object
 * is destroyed
 *
 * RETURNS
 *  The new refcount
 *
 *******************************************************************************/
static ULONG WINAPI
IDirectDrawClassFactoryImpl_Release(IClassFactory *iface)
634
{
635
    ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
636
    ULONG ref = InterlockedDecrement(&This->ref);
637
    TRACE("(%p)->() decrementing from %d.\n", This, ref+1);
638

639
    if (ref == 0)
640
        HeapFree(GetProcessHeap(), 0, This);
641

642
    return ref;
643 644
}

645

646 647 648 649 650 651
/*******************************************************************************
 * IDirectDrawClassFactory::CreateInstance
 *
 * What is this? Seems to create DirectDraw objects...
 *
 * Params
Austin English's avatar
Austin English committed
652
 *  The usual things???
653 654 655 656 657 658 659 660 661 662
 *
 * RETURNS
 *  ???
 *
 *******************************************************************************/
static HRESULT WINAPI
IDirectDrawClassFactoryImpl_CreateInstance(IClassFactory *iface,
                                           IUnknown *UnkOuter,
                                           REFIID riid,
                                           void **obj)
663
{
664
    ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
665

666
    TRACE("(%p)->(%p,%s,%p)\n",This,UnkOuter,debugstr_guid(riid),obj);
667

668
    return This->pfnCreateInstance(UnkOuter, riid, obj);
669 670
}

671 672 673 674 675 676 677 678 679 680 681 682 683 684
/*******************************************************************************
 * IDirectDrawClassFactory::LockServer
 *
 * What is this?
 *
 * Params
 *  ???
 *
 * RETURNS
 *  S_OK, because it's a stub
 *
 *******************************************************************************/
static HRESULT WINAPI
IDirectDrawClassFactoryImpl_LockServer(IClassFactory *iface,BOOL dolock)
685
{
686
    ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
687 688 689 690
    FIXME("(%p)->(%d),stub!\n",This,dolock);
    return S_OK;
}

691 692 693 694
/*******************************************************************************
 * The class factory VTable
 *******************************************************************************/
static const IClassFactoryVtbl IClassFactory_Vtbl =
695
{
696 697 698 699 700
    IDirectDrawClassFactoryImpl_QueryInterface,
    IDirectDrawClassFactoryImpl_AddRef,
    IDirectDrawClassFactoryImpl_Release,
    IDirectDrawClassFactoryImpl_CreateInstance,
    IDirectDrawClassFactoryImpl_LockServer
701 702 703
};

/*******************************************************************************
704
 * DllGetClassObject [DDRAW.@]
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
 * Retrieves class object from a DLL object
 *
 * NOTES
 *    Docs say returns STDAPI
 *
 * PARAMS
 *    rclsid [I] CLSID for the class object
 *    riid   [I] Reference to identifier of interface for class object
 *    ppv    [O] Address of variable to receive interface pointer for riid
 *
 * RETURNS
 *    Success: S_OK
 *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
 *             E_UNEXPECTED
 */
720
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
721
{
722
    unsigned int i;
723 724
    IClassFactoryImpl *factory;

725
    TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
726 727 728 729 730 731 732

    if ( !IsEqualGUID( &IID_IClassFactory, riid )
	 && ! IsEqualGUID( &IID_IUnknown, riid) )
	return E_NOINTERFACE;

    for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
    {
733
	if (IsEqualGUID(object_creation[i].clsid, rclsid))
734 735 736 737 738 739 740
	    break;
    }

    if (i == sizeof(object_creation)/sizeof(object_creation[0]))
    {
	FIXME("%s: no class found.\n", debugstr_guid(rclsid));
	return CLASS_E_CLASSNOTAVAILABLE;
741
    }
742

743
    factory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*factory));
744 745
    if (factory == NULL) return E_OUTOFMEMORY;

746
    ICOM_INIT_INTERFACE(factory, IClassFactory, IClassFactory_Vtbl);
747 748 749 750 751 752
    factory->ref = 1;

    factory->pfnCreateInstance = object_creation[i].pfnCreateInstance;

    *ppv = ICOM_INTERFACE(factory, IClassFactory);
    return S_OK;
753 754 755 756
}


/*******************************************************************************
757
 * DllCanUnloadNow [DDRAW.@]  Determines whether the DLL is in use.
758 759 760 761 762
 *
 * RETURNS
 *    Success: S_OK
 *    Failure: S_FALSE
 */
763 764
HRESULT WINAPI DllCanUnloadNow(void)
{
765
    HRESULT hr;
766
    FIXME("(void): stub\n");
767 768 769 770 771 772

    EnterCriticalSection(&ddraw_cs);
    hr = S_FALSE;
    LeaveCriticalSection(&ddraw_cs);

    return hr;
773
}
774

775 776 777 778 779 780 781 782 783 784 785 786 787 788
/*******************************************************************************
 * DestroyCallback
 *
 * Callback function for the EnumSurfaces call in DllMain.
 * Dumps some surface info and releases the surface
 *
 * Params:
 *  surf: The enumerated surface
 *  desc: it's description
 *  context: Pointer to the ddraw impl
 *
 * Returns:
 *  DDENUMRET_OK;
 *******************************************************************************/
789
static HRESULT WINAPI
790 791 792
DestroyCallback(IDirectDrawSurface7 *surf,
                DDSURFACEDESC2 *desc,
                void *context)
793
{
794 795 796 797 798
    IDirectDrawSurfaceImpl *Impl = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7, surf);
    IDirectDrawImpl *ddraw = (IDirectDrawImpl *) context;
    ULONG ref;

    ref = IDirectDrawSurface7_Release(surf);  /* For the EnumSurfaces */
799
    WARN("Surface %p has an reference count of %d\n", Impl, ref);
800 801 802 803 804

    /* Skip surfaces which are attached somewhere or which are
     * part of a complex compound. They will get released when destroying
     * the root
     */
805
    if( (!Impl->is_complex_root) || (Impl->first_attached != Impl) )
806 807 808 809 810 811 812 813 814
        return DDENUMRET_OK;
    /* Skip our depth stencil surface, it will be released with the render target */
    if( Impl == ddraw->DepthStencilBuffer)
        return DDENUMRET_OK;

    /* Destroy the surface */
    while(ref) ref = IDirectDrawSurface7_Release(surf);

    return DDENUMRET_OK;
815 816
}

817
/***********************************************************************
818 819 820 821 822
 * get_config_key
 *
 * Reads a config key from the registry. Taken from WineD3D
 *
 ***********************************************************************/
823
static inline DWORD get_config_key(HKEY defkey, HKEY appkey, const char* name, char* buffer, DWORD size)
824
{
825 826 827 828
    if (0 != appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE) buffer, &size )) return 0;
    if (0 != defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE) buffer, &size )) return 0;
    return ERROR_FILE_NOT_FOUND;
}
829

830 831 832 833 834 835 836 837 838 839 840
/***********************************************************************
 * DllMain (DDRAW.0)
 *
 * Could be used to register DirectDraw drivers, if we have more than
 * one. Also used to destroy any objects left at unload if the
 * app didn't release them properly(Gothic 2, Diablo 2, Moto racer, ...)
 *
 ***********************************************************************/
BOOL WINAPI
DllMain(HINSTANCE hInstDLL,
        DWORD Reason,
841
        LPVOID lpv)
842
{
843
    TRACE("(%p,%x,%p)\n", hInstDLL, Reason, lpv);
844
    if (Reason == DLL_PROCESS_ATTACH)
845
    {
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
        char buffer[MAX_PATH+10];
        DWORD size = sizeof(buffer);
        HKEY hkey = 0;
        HKEY appkey = 0;
        DWORD len;

       /* @@ Wine registry key: HKCU\Software\Wine\Direct3D */
       if ( RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\Direct3D", &hkey ) ) hkey = 0;

       len = GetModuleFileNameA( 0, buffer, MAX_PATH );
       if (len && len < MAX_PATH)
       {
            HKEY tmpkey;
            /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\Direct3D */
            if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
            {
                char *p, *appname = buffer;
                if ((p = strrchr( appname, '/' ))) appname = p + 1;
                if ((p = strrchr( appname, '\\' ))) appname = p + 1;
                strcat( appname, "\\Direct3D" );
                TRACE("appname = [%s]\n", appname);
                if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
                RegCloseKey( tmpkey );
            }
       }

       if ( 0 != hkey || 0 != appkey )
       {
            if ( !get_config_key( hkey, appkey, "DirectDrawRenderer", buffer, size) )
            {
                if (!strcmp(buffer,"gdi"))
                {
                    TRACE("Defaulting to GDI surfaces\n");
                    DefaultSurfaceType = SURFACE_GDI;
                }
                else if (!strcmp(buffer,"opengl"))
                {
                    TRACE("Defaulting to opengl surfaces\n");
                    DefaultSurfaceType = SURFACE_OPENGL;
                }
                else
                {
888
                    ERR("Unknown default surface type. Supported are:\n gdi, opengl\n");
889 890 891
                }
            }
        }
892

893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
        /* On Windows one can force the refresh rate that DirectDraw uses by
         * setting an override value in dxdiag.  This is documented in KB315614
         * (main article), KB230002, and KB217348.  By comparing registry dumps
         * before and after setting the override, we see that the override value
         * is stored in HKLM\Software\Microsoft\DirectDraw\ForceRefreshRate as a
         * DWORD that represents the refresh rate to force.  We use this
         * registry entry to modify the behavior of SetDisplayMode so that Wine
         * users can override the refresh rate in a Windows-compatible way.
         *
         * dxdiag will not accept a refresh rate lower than 40 or higher than
         * 120 so this value should be within that range.  It is, of course,
         * possible for a user to set the registry entry value directly so that
         * assumption might not hold.
         *
         * There is no current mechanism for setting this value through the Wine
         * GUI.  It would be most appropriate to set this value through a dxdiag
         * clone, but it may be sufficient to use winecfg.
         *
         * TODO: Create a mechanism for setting this value through the Wine GUI.
         */
        if ( !RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectDraw", &hkey ) )
        {
            DWORD type, data;
            size = sizeof(data);
            if (!RegQueryValueExA( hkey, "ForceRefreshRate", NULL, &type, (LPBYTE)&data, &size ) && type == REG_DWORD)
            {
                TRACE("ForceRefreshRate set; overriding refresh rate to %d Hz\n", data);
                force_refresh_rate = data;
            }
            RegCloseKey( hkey );
        }

925
        DisableThreadLibraryCalls(hInstDLL);
926 927 928
    }
    else if (Reason == DLL_PROCESS_DETACH)
    {
929
        if(!list_empty(&global_ddraw_list))
930
        {
931 932 933
            struct list *entry, *entry2;
            WARN("There are still existing DirectDraw interfaces. Wine bug or buggy application?\n");

Austin English's avatar
Austin English committed
934
            /* We remove elements from this loop */
935
            LIST_FOR_EACH_SAFE(entry, entry2, &global_ddraw_list)
936
            {
937 938 939 940 941
                HRESULT hr;
                DDSURFACEDESC2 desc;
                int i;
                IDirectDrawImpl *ddraw = LIST_ENTRY(entry, IDirectDrawImpl, ddraw_list_entry);

942
                WARN("DDraw %p has a refcount of %d\n", ddraw, ddraw->ref7 + ddraw->ref4 + ddraw->ref3 + ddraw->ref2 + ddraw->ref1);
943

Austin English's avatar
Austin English committed
944
                /* Add references to each interface to avoid freeing them unexpectedly */
945 946
                IDirectDraw_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw));
                IDirectDraw2_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw2));
947
                IDirectDraw3_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw3));
948 949 950 951 952
                IDirectDraw4_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw4));
                IDirectDraw7_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw7));

                /* Does a D3D device exist? Destroy it
                    * TODO: Destroy all Vertex buffers, Lights, Materials
Austin English's avatar
Austin English committed
953
                    * and execute buffers too
954 955 956 957 958 959
                    */
                if(ddraw->d3ddevice)
                {
                    WARN("DDraw %p has d3ddevice %p attached\n", ddraw, ddraw->d3ddevice);
                    while(IDirect3DDevice7_Release(ICOM_INTERFACE(ddraw->d3ddevice, IDirect3DDevice7)));
                }
960

961 962 963 964 965 966
                /* Try to release the objects
                    * Do an EnumSurfaces to find any hanging surfaces
                    */
                memset(&desc, 0, sizeof(desc));
                desc.dwSize = sizeof(desc);
                for(i = 0; i <= 1; i++)
967
                {
968 969 970 971 972 973 974
                    hr = IDirectDraw7_EnumSurfaces(ICOM_INTERFACE(ddraw, IDirectDraw7),
                                                    DDENUMSURFACES_ALL,
                                                    &desc,
                                                    (void *) ddraw,
                                                    DestroyCallback);
                    if(hr != D3D_OK)
                        ERR("(%p) EnumSurfaces failed, prepare for trouble\n", ddraw);
975
                }
976 977 978

                /* Check the surface count */
                if(ddraw->surfaces > 0)
979
                    ERR("DDraw %p still has %d surfaces attached\n", ddraw, ddraw->surfaces);
980 981 982 983 984 985

                /* Release all hanging references to destroy the objects. This
                    * restores the screen mode too
                    */
                while(IDirectDraw_Release(ICOM_INTERFACE(ddraw, IDirectDraw)));
                while(IDirectDraw2_Release(ICOM_INTERFACE(ddraw, IDirectDraw2)));
986
                while(IDirectDraw3_Release(ICOM_INTERFACE(ddraw, IDirectDraw3)));
987 988
                while(IDirectDraw4_Release(ICOM_INTERFACE(ddraw, IDirectDraw4)));
                while(IDirectDraw7_Release(ICOM_INTERFACE(ddraw, IDirectDraw7)));
989
            }
990
        }
991
    }
992 993 994

    return TRUE;
}