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 Dösinger
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

30 31
#define DDRAW_INIT_GUID
#include "ddraw_private.h"
32
#include "rpcproxy.h"
33

34
#include "wine/exception.h"
35
#include "winreg.h"
36

37
WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
38

39
/* The configured default surface */
40
enum wined3d_surface_type DefaultSurfaceType = WINED3D_SURFACE_TYPE_OPENGL;
41

42 43
static struct list global_ddraw_list = LIST_INIT(global_ddraw_list);

44 45
static HINSTANCE instance;

46 47 48
/* value of ForceRefreshRate */
DWORD force_refresh_rate = 0;

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
/* Structure for converting DirectDrawEnumerateA to DirectDrawEnumerateExA */
struct callback_info
{
    LPDDENUMCALLBACKA callback;
    void *context;
};

/* Enumeration callback for converting DirectDrawEnumerateA to DirectDrawEnumerateExA */
static HRESULT CALLBACK enum_callback(GUID *guid, char *description, char *driver_name,
                                      void *context, HMONITOR monitor)
{
    const struct callback_info *info = context;

    return info->callback(guid, description, driver_name, info->context);
}

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
/* Handle table functions */
BOOL ddraw_handle_table_init(struct ddraw_handle_table *t, UINT initial_size)
{
    t->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, initial_size * sizeof(*t->entries));
    if (!t->entries)
    {
        ERR("Failed to allocate handle table memory.\n");
        return FALSE;
    }
    t->free_entries = NULL;
    t->table_size = initial_size;
    t->entry_count = 0;

    return TRUE;
}

void ddraw_handle_table_destroy(struct ddraw_handle_table *t)
{
    HeapFree(GetProcessHeap(), 0, t->entries);
    memset(t, 0, sizeof(*t));
}

DWORD ddraw_allocate_handle(struct ddraw_handle_table *t, void *object, enum ddraw_handle_type type)
{
    struct ddraw_handle_entry *entry;

    if (t->free_entries)
    {
        DWORD idx = t->free_entries - t->entries;
        /* Use a free handle */
        entry = t->free_entries;
        if (entry->type != DDRAW_HANDLE_FREE)
        {
            ERR("Handle %#x (%p) is in the free list, but has type %#x.\n", idx, entry->object, entry->type);
            return DDRAW_INVALID_HANDLE;
        }
        t->free_entries = entry->object;
        entry->object = object;
        entry->type = type;

        return idx;
    }

    if (!(t->entry_count < t->table_size))
    {
        /* Grow the table */
        UINT new_size = t->table_size + (t->table_size >> 1);
        struct ddraw_handle_entry *new_entries = HeapReAlloc(GetProcessHeap(),
                0, t->entries, new_size * sizeof(*t->entries));
        if (!new_entries)
        {
            ERR("Failed to grow the handle table.\n");
            return DDRAW_INVALID_HANDLE;
        }
        t->entries = new_entries;
        t->table_size = new_size;
    }

    entry = &t->entries[t->entry_count];
    entry->object = object;
    entry->type = type;

    return t->entry_count++;
}

void *ddraw_free_handle(struct ddraw_handle_table *t, DWORD handle, enum ddraw_handle_type type)
{
    struct ddraw_handle_entry *entry;
    void *object;

    if (handle == DDRAW_INVALID_HANDLE || handle >= t->entry_count)
    {
        WARN("Invalid handle %#x passed.\n", handle);
        return NULL;
    }

    entry = &t->entries[handle];
    if (entry->type != type)
    {
        WARN("Handle %#x (%p) is not of type %#x.\n", handle, entry->object, type);
        return NULL;
    }

    object = entry->object;
    entry->object = t->free_entries;
    entry->type = DDRAW_HANDLE_FREE;
    t->free_entries = entry;

    return object;
}

void *ddraw_get_object(struct ddraw_handle_table *t, DWORD handle, enum ddraw_handle_type type)
{
    struct ddraw_handle_entry *entry;

    if (handle == DDRAW_INVALID_HANDLE || handle >= t->entry_count)
    {
        WARN("Invalid handle %#x passed.\n", handle);
        return NULL;
    }

    entry = &t->entries[handle];
    if (entry->type != type)
    {
        WARN("Handle %#x (%p) is not of type %#x.\n", handle, entry->object, type);
        return NULL;
    }

    return entry->object;
}

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
/***********************************************************************
 *
 * 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:
194
 *  DD_OK if the Interface was created successfully
195 196 197 198 199
 *  CLASS_E_NOAGGREGATION if UnkOuter is not NULL
 *  E_OUTOFMEMORY if some allocation failed
 *
 ***********************************************************************/
static HRESULT
200
DDRAW_Create(const GUID *guid,
201 202 203
             void **DD,
             IUnknown *UnkOuter,
             REFIID iid)
204
{
205
    enum wined3d_device_type device_type;
206
    struct ddraw *ddraw;
207
    HRESULT hr;
208

209 210
    TRACE("driver_guid %s, ddraw %p, outer_unknown %p, interface_iid %s.\n",
            debugstr_guid(guid), DD, UnkOuter, debugstr_guid(iid));
211

212 213
    *DD = NULL;

214 215 216 217 218 219 220 221 222
    /* 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
         */
223
        device_type = WINED3D_DEVICE_TYPE_REF;
224
    }
225 226
    else if(guid == (GUID *) DDCREATE_HARDWAREONLY)
    {
227
        device_type = WINED3D_DEVICE_TYPE_HAL;
228
    }
229 230
    else
    {
231
        device_type = 0;
232 233
    }

234
    /* DDraw doesn't support aggregation, according to msdn */
235 236
    if (UnkOuter != NULL)
        return CLASS_E_NOAGGREGATION;
237

238
    /* DirectDraw creation comes here */
239 240
    ddraw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ddraw));
    if (!ddraw)
241 242 243
    {
        ERR("Out of memory when creating DirectDraw\n");
        return E_OUTOFMEMORY;
244
    }
245

246
    hr = ddraw_init(ddraw, device_type);
247
    if (FAILED(hr))
248
    {
249
        WARN("Failed to initialize ddraw object, hr %#x.\n", hr);
250
        HeapFree(GetProcessHeap(), 0, ddraw);
251
        return hr;
252 253
    }

254 255 256 257 258 259
    hr = IDirectDraw7_QueryInterface(&ddraw->IDirectDraw7_iface, iid, DD);
    IDirectDraw7_Release(&ddraw->IDirectDraw7_iface);
    if (SUCCEEDED(hr))
        list_add_head(&global_ddraw_list, &ddraw->ddraw_list_entry);
    else
        WARN("Failed to query interface %s from ddraw object %p.\n", debugstr_guid(iid), ddraw);
260

261
    return hr;
262 263 264
}

/***********************************************************************
265 266 267 268 269 270 271 272
 * DirectDrawCreate (DDRAW.@)
 *
 * Creates legacy DirectDraw Interfaces. Can't create IDirectDraw7
 * interfaces in theory
 *
 * Arguments, return values: See DDRAW_Create
 *
 ***********************************************************************/
273
HRESULT WINAPI DECLSPEC_HOTPATCH
274
DirectDrawCreate(GUID *GUID,
275
                 LPDIRECTDRAW *DD,
276
                 IUnknown *UnkOuter)
277
{
278
    HRESULT hr;
279 280 281

    TRACE("driver_guid %s, ddraw %p, outer_unknown %p.\n",
            debugstr_guid(GUID), DD, UnkOuter);
282

283
    wined3d_mutex_lock();
284
    hr = DDRAW_Create(GUID, (void **) DD, UnkOuter, &IID_IDirectDraw);
285
    wined3d_mutex_unlock();
286 287 288 289 290 291 292 293

    if (SUCCEEDED(hr))
    {
        hr = IDirectDraw_Initialize(*DD, GUID);
        if (FAILED(hr))
            IDirectDraw_Release(*DD);
    }

294
    return hr;
295 296 297
}

/***********************************************************************
298 299 300 301 302 303 304 305
 * DirectDrawCreateEx (DDRAW.@)
 *
 * Only creates new IDirectDraw7 interfaces, supposed to fail if legacy
 * interfaces are requested.
 *
 * Arguments, return values: See DDRAW_Create
 *
 ***********************************************************************/
306
HRESULT WINAPI DECLSPEC_HOTPATCH
307 308
DirectDrawCreateEx(GUID *guid,
                   LPVOID *dd,
309 310
                   REFIID iid,
                   IUnknown *UnkOuter)
311
{
312
    HRESULT hr;
313 314

    TRACE("driver_guid %s, ddraw %p, interface_iid %s, outer_unknown %p.\n",
315
            debugstr_guid(guid), dd, debugstr_guid(iid), UnkOuter);
316

317 318
    if (!IsEqualGUID(iid, &IID_IDirectDraw7))
        return DDERR_INVALIDPARAMS;
319

320
    wined3d_mutex_lock();
321
    hr = DDRAW_Create(guid, dd, UnkOuter, iid);
322
    wined3d_mutex_unlock();
323 324 325

    if (SUCCEEDED(hr))
    {
326 327
        IDirectDraw7 *ddraw7 = *(IDirectDraw7 **)dd;
        hr = IDirectDraw7_Initialize(ddraw7, guid);
328 329 330 331
        if (FAILED(hr))
            IDirectDraw7_Release(ddraw7);
    }

332
    return hr;
333 334
}

335
/***********************************************************************
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
 * 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
 *
 *
 ***********************************************************************/
355
HRESULT WINAPI DirectDrawEnumerateA(LPDDENUMCALLBACKA callback, void *context)
356
{
357
    struct callback_info info;
358

359
    TRACE("callback %p, context %p.\n", callback, context);
360

361 362 363
    info.callback = callback;
    info.context = context;
    return DirectDrawEnumerateExA(enum_callback, &info, 0x0);
364
}
365

366 367 368 369 370 371 372 373 374
/***********************************************************************
 * DirectDrawEnumerateExA (DDRAW.@)
 *
 * Enumerates DirectDraw7 drivers, ascii version. See
 * the comments above DirectDrawEnumerateA for more details.
 *
 * The Flag member is not supported right now.
 *
 ***********************************************************************/
375
HRESULT WINAPI DirectDrawEnumerateExA(LPDDENUMCALLBACKEXA callback, void *context, DWORD flags)
376
{
377 378
    struct wined3d *wined3d;

379
    TRACE("callback %p, context %p, flags %#x.\n", callback, context, flags);
380

381
    if (flags & ~(DDENUM_ATTACHEDSECONDARYDEVICES |
382 383 384 385
                  DDENUM_DETACHEDSECONDARYDEVICES |
                  DDENUM_NONDISPLAYDEVICES))
        return DDERR_INVALIDPARAMS;

386 387
    if (flags)
        FIXME("flags 0x%08x not handled\n", flags);
388

389 390
    TRACE("Enumerating ddraw interfaces\n");
    wined3d = wined3d_create(7, WINED3D_LEGACY_DEPTH_BIAS);
391

392
    __TRY
393
    {
394
        /* QuickTime expects the description "DirectDraw HAL" */
395 396
        static CHAR driver_desc[] = "DirectDraw HAL",
        driver_name[] = "display";
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
        struct wined3d_adapter_identifier adapter_id;
        HRESULT hr = S_OK;
        UINT adapter = 0;
        BOOL cont_enum;

        /* The Battle.net System Checker expects both a NULL device and a GUID-based device */
        TRACE("Default interface: DirectDraw HAL\n");
        cont_enum = callback(NULL, driver_desc, driver_name, context, 0);
        for (adapter = 0; SUCCEEDED(hr) && cont_enum; adapter++)
        {
            char DriverName[512] = "";

            /* The Battle.net System Checker expects the GetAdapterIdentifier DeviceName to match the
             * Driver Name, so obtain the DeviceName and GUID from D3D. */
            memset(&adapter_id, 0x0, sizeof(adapter_id));
            adapter_id.device_name = DriverName;
            adapter_id.device_name_size = sizeof(DriverName);
            wined3d_mutex_lock();
            hr = wined3d_get_adapter_identifier(wined3d, adapter, 0x0, &adapter_id);
            wined3d_mutex_unlock();
            if (SUCCEEDED(hr))
            {
                TRACE("Interface %d: %s\n", adapter, wine_dbgstr_guid(&adapter_id.device_identifier));
                cont_enum = callback(&adapter_id.device_identifier, driver_desc,
                                     adapter_id.device_name, context, 0);
            }
        }
424
    }
425 426
    __EXCEPT_PAGE_FAULT
    {
427
        wined3d_decref(wined3d);
428
        return DDERR_INVALIDPARAMS;
429 430
    }
    __ENDTRY;
431

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

437
/***********************************************************************
438
 * DirectDrawEnumerateW (DDRAW.@)
439
 *
440 441
 * Enumerates legacy drivers, unicode version.
 * This function is not implemented on Windows.
442 443
 *
 ***********************************************************************/
444
HRESULT WINAPI DirectDrawEnumerateW(LPDDENUMCALLBACKW callback, void *context)
445
{
446
    TRACE("callback %p, context %p.\n", callback, context);
447

448
    if (!callback)
449 450 451 452
        return DDERR_INVALIDPARAMS;
    else
        return DDERR_UNSUPPORTED;
}
453

454
/***********************************************************************
455
 * DirectDrawEnumerateExW (DDRAW.@)
456
 *
457 458
 * Enumerates DirectDraw7 drivers, unicode version.
 * This function is not implemented on Windows.
459 460
 *
 ***********************************************************************/
461
HRESULT WINAPI DirectDrawEnumerateExW(LPDDENUMCALLBACKEXW callback, void *context, DWORD flags)
462
{
463
    TRACE("callback %p, context %p, flags %#x.\n", callback, context, flags);
464 465 466

    return DDERR_UNSUPPORTED;
}
467

468 469 470
/***********************************************************************
 * Classfactory implementation.
 ***********************************************************************/
471

472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
/***********************************************************************
 * 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)
489 490 491
{
    HRESULT hr;

492
    TRACE("outer_unknown %p, riid %s, object %p.\n", UnkOuter, debugstr_guid(iid), obj);
493

494
    wined3d_mutex_lock();
495
    hr = DDRAW_Create(NULL, obj, UnkOuter, iid);
496 497
    wined3d_mutex_unlock();

498
    return hr;
499 500
}

501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
/***********************************************************************
 * 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)
518
{
519 520 521
    HRESULT hr;
    IDirectDrawClipper *Clip;

522 523
    TRACE("outer_unknown %p, riid %s, object %p.\n", UnkOuter, debugstr_guid(riid), obj);

524
    wined3d_mutex_lock();
525
    hr = DirectDrawCreateClipper(0, &Clip, UnkOuter);
526 527
    if (hr != DD_OK)
    {
528
        wined3d_mutex_unlock();
529 530
        return hr;
    }
531

532 533
    hr = IDirectDrawClipper_QueryInterface(Clip, riid, obj);
    IDirectDrawClipper_Release(Clip);
534

535 536
    wined3d_mutex_unlock();

537 538
    return hr;
}
539 540 541

static const struct object_creation_info object_creation[] =
{
542 543 544
    { &CLSID_DirectDraw,        CF_CreateDirectDraw },
    { &CLSID_DirectDraw7,       CF_CreateDirectDraw },
    { &CLSID_DirectDrawClipper, CF_CreateDirectDrawClipper }
545 546
};

547
struct ddraw_class_factory
548 549 550 551 552
{
    IClassFactory IClassFactory_iface;

    LONG ref;
    HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, REFIID iid, LPVOID *ppObj);
553
};
554

555
static inline struct ddraw_class_factory *impl_from_IClassFactory(IClassFactory *iface)
556
{
557
    return CONTAINING_RECORD(iface, struct ddraw_class_factory, IClassFactory_iface);
558 559
}

560 561 562 563 564 565 566 567 568 569 570 571 572 573
/*******************************************************************************
 * 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
 *
 *******************************************************************************/
574
static HRESULT WINAPI ddraw_class_factory_QueryInterface(IClassFactory *iface, REFIID riid, void **out)
575
{
576
    TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
577

578
    if (IsEqualGUID(riid, &IID_IUnknown)
579
        || IsEqualGUID(riid, &IID_IClassFactory))
580
    {
581
        IClassFactory_AddRef(iface);
582
        *out = iface;
583
        return S_OK;
584 585
    }

586 587
    WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));

588 589 590
    return E_NOINTERFACE;
}

591 592 593 594 595 596 597 598 599
/*******************************************************************************
 * IDirectDrawClassFactory::AddRef
 *
 * AddRef for the class factory
 *
 * RETURNS
 *  The new refcount
 *
 *******************************************************************************/
600
static ULONG WINAPI ddraw_class_factory_AddRef(IClassFactory *iface)
601
{
602 603
    struct ddraw_class_factory *factory = impl_from_IClassFactory(iface);
    ULONG ref = InterlockedIncrement(&factory->ref);
604

605
    TRACE("%p increasing refcount to %u.\n", factory, ref);
606

607
    return ref;
608 609
}

610 611 612 613 614 615 616 617 618 619
/*******************************************************************************
 * IDirectDrawClassFactory::Release
 *
 * Release for the class factory. If the refcount falls to 0, the object
 * is destroyed
 *
 * RETURNS
 *  The new refcount
 *
 *******************************************************************************/
620
static ULONG WINAPI ddraw_class_factory_Release(IClassFactory *iface)
621
{
622 623
    struct ddraw_class_factory *factory = impl_from_IClassFactory(iface);
    ULONG ref = InterlockedDecrement(&factory->ref);
624

625
    TRACE("%p decreasing refcount to %u.\n", factory, ref);
626

627 628
    if (!ref)
        HeapFree(GetProcessHeap(), 0, factory);
629

630
    return ref;
631 632
}

633

634 635 636 637 638 639
/*******************************************************************************
 * IDirectDrawClassFactory::CreateInstance
 *
 * What is this? Seems to create DirectDraw objects...
 *
 * Params
Austin English's avatar
Austin English committed
640
 *  The usual things???
641 642 643 644 645
 *
 * RETURNS
 *  ???
 *
 *******************************************************************************/
646 647
static HRESULT WINAPI ddraw_class_factory_CreateInstance(IClassFactory *iface,
        IUnknown *outer_unknown, REFIID riid, void **out)
648
{
649
    struct ddraw_class_factory *factory = impl_from_IClassFactory(iface);
650

651 652
    TRACE("iface %p, outer_unknown %p, riid %s, out %p.\n",
            iface, outer_unknown, debugstr_guid(riid), out);
653

654
    return factory->pfnCreateInstance(outer_unknown, riid, out);
655 656
}

657 658 659 660 661 662 663 664 665 666 667 668
/*******************************************************************************
 * IDirectDrawClassFactory::LockServer
 *
 * What is this?
 *
 * Params
 *  ???
 *
 * RETURNS
 *  S_OK, because it's a stub
 *
 *******************************************************************************/
669
static HRESULT WINAPI ddraw_class_factory_LockServer(IClassFactory *iface, BOOL dolock)
670
{
671 672
    FIXME("iface %p, dolock %#x stub!\n", iface, dolock);

673 674 675
    return S_OK;
}

676 677 678 679
/*******************************************************************************
 * The class factory VTable
 *******************************************************************************/
static const IClassFactoryVtbl IClassFactory_Vtbl =
680
{
681 682 683 684 685
    ddraw_class_factory_QueryInterface,
    ddraw_class_factory_AddRef,
    ddraw_class_factory_Release,
    ddraw_class_factory_CreateInstance,
    ddraw_class_factory_LockServer
686 687 688
};

/*******************************************************************************
689
 * DllGetClassObject [DDRAW.@]
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
 * 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
 */
705
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
706
{
707
    struct ddraw_class_factory *factory;
708
    unsigned int i;
709

710 711
    TRACE("rclsid %s, riid %s, object %p.\n",
            debugstr_guid(rclsid), debugstr_guid(riid), ppv);
712

713 714 715
    if (!IsEqualGUID(&IID_IClassFactory, riid)
            && !IsEqualGUID(&IID_IUnknown, riid))
        return E_NOINTERFACE;
716 717 718

    for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
    {
719 720
        if (IsEqualGUID(object_creation[i].clsid, rclsid))
            break;
721 722 723 724
    }

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

729
    factory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*factory));
730 731
    if (factory == NULL) return E_OUTOFMEMORY;

732
    factory->IClassFactory_iface.lpVtbl = &IClassFactory_Vtbl;
733 734 735 736
    factory->ref = 1;

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

737
    *ppv = factory;
738
    return S_OK;
739 740 741 742
}


/*******************************************************************************
743
 * DllCanUnloadNow [DDRAW.@]  Determines whether the DLL is in use.
744 745 746 747 748
 *
 * RETURNS
 *    Success: S_OK
 *    Failure: S_FALSE
 */
749 750
HRESULT WINAPI DllCanUnloadNow(void)
{
751 752
    TRACE("\n");

753
    return S_FALSE;
754
}
755

756 757 758 759 760 761

/***********************************************************************
 *		DllRegisterServer (DDRAW.@)
 */
HRESULT WINAPI DllRegisterServer(void)
{
762
    return __wine_register_resources( instance );
763 764 765 766 767 768 769
}

/***********************************************************************
 *		DllUnregisterServer (DDRAW.@)
 */
HRESULT WINAPI DllUnregisterServer(void)
{
770
    return __wine_unregister_resources( instance );
771 772
}

773 774 775 776 777 778 779 780 781 782 783 784 785 786
/*******************************************************************************
 * 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;
 *******************************************************************************/
787
static HRESULT WINAPI
788 789 790
DestroyCallback(IDirectDrawSurface7 *surf,
                DDSURFACEDESC2 *desc,
                void *context)
791
{
792
    struct ddraw_surface *Impl = impl_from_IDirectDrawSurface7(surf);
793
    ULONG ref7, ref4, ref3, ref2, ref1, gamma_count, iface_count;
794

795
    ref7 = IDirectDrawSurface7_Release(surf);  /* For the EnumSurfaces */
796 797 798 799 800 801
    ref4 = Impl->ref4;
    ref3 = Impl->ref3;
    ref2 = Impl->ref2;
    ref1 = Impl->ref1;
    gamma_count = Impl->gamma_count;

802 803
    WARN("Surface %p has an reference counts of 7: %u 4: %u 3: %u 2: %u 1: %u gamma: %u\n",
            Impl, ref7, ref4, ref3, ref2, ref1, gamma_count);
804 805 806 807 808

    /* Skip surfaces which are attached somewhere or which are
     * part of a complex compound. They will get released when destroying
     * the root
     */
809
    if( (!Impl->is_complex_root) || (Impl->first_attached != Impl) )
810 811 812
        return DDENUMRET_OK;

    /* Destroy the surface */
813 814
    iface_count = ddraw_surface_release_iface(Impl);
    while (iface_count) iface_count = ddraw_surface_release_iface(Impl);
815 816

    return DDENUMRET_OK;
817 818
}

819
/***********************************************************************
820 821 822 823 824
 * get_config_key
 *
 * Reads a config key from the registry. Taken from WineD3D
 *
 ***********************************************************************/
825
static inline DWORD get_config_key(HKEY defkey, HKEY appkey, const char* name, char* buffer, DWORD size)
826
{
827 828 829 830
    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;
}
831

832 833 834 835 836 837 838 839 840 841 842
/***********************************************************************
 * 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,
843
        LPVOID lpv)
844
{
845
    TRACE("(%p,%x,%p)\n", hInstDLL, Reason, lpv);
846
    if (Reason == DLL_PROCESS_ATTACH)
847
    {
848
        static HMODULE ddraw_self;
849 850 851 852
        char buffer[MAX_PATH+10];
        DWORD size = sizeof(buffer);
        HKEY hkey = 0;
        HKEY appkey = 0;
853
        WNDCLASSA wc;
854 855
        DWORD len;

856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
        /* Register the window class. This 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(). */
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = DefWindowProcA;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInstDLL;
        wc.hIcon = 0;
        wc.hCursor = 0;
        wc.hbrBackground = GetStockObject(BLACK_BRUSH);
        wc.lpszMenuName = NULL;
        wc.lpszClassName = DDRAW_WINDOW_CLASS_NAME;
        if (!RegisterClassA(&wc))
        {
            ERR("Failed to register ddraw window class, last error %#x.\n", GetLastError());
            return FALSE;
        }

875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
       /* @@ 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");
902
                    DefaultSurfaceType = WINED3D_SURFACE_TYPE_GDI;
903 904 905 906
                }
                else if (!strcmp(buffer,"opengl"))
                {
                    TRACE("Defaulting to opengl surfaces\n");
907
                    DefaultSurfaceType = WINED3D_SURFACE_TYPE_OPENGL;
908 909 910
                }
                else
                {
911
                    ERR("Unknown default surface type. Supported are:\n gdi, opengl\n");
912 913 914
                }
            }
        }
915

916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
        /* 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 );
        }

948 949 950 951 952 953 954 955 956 957
        /* Prevent the ddraw module from being unloaded. When switching to
         * exclusive mode, we replace the window proc of the ddraw window. If
         * an application would unload ddraw from the WM_DESTROY handler for
         * that window, it would return to unmapped memory and die. Apparently
         * this is supposed to work on Windows. We should probably use
         * GET_MODULE_HANDLE_EX_FLAG_PIN for this, but that's not currently
         * implemented. */
        if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR *)&ddraw_self, &ddraw_self))
            ERR("Failed to get own module handle.\n");

958
        instance = hInstDLL;
959
        DisableThreadLibraryCalls(hInstDLL);
960 961 962
    }
    else if (Reason == DLL_PROCESS_DETACH)
    {
963
        if(!list_empty(&global_ddraw_list))
964
        {
965 966 967
            struct list *entry, *entry2;
            WARN("There are still existing DirectDraw interfaces. Wine bug or buggy application?\n");

Austin English's avatar
Austin English committed
968
            /* We remove elements from this loop */
969
            LIST_FOR_EACH_SAFE(entry, entry2, &global_ddraw_list)
970
            {
971
                struct ddraw *ddraw = LIST_ENTRY(entry, struct ddraw, ddraw_list_entry);
972 973 974 975
                HRESULT hr;
                DDSURFACEDESC2 desc;
                int i;

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

Austin English's avatar
Austin English committed
978
                /* Add references to each interface to avoid freeing them unexpectedly */
979
                IDirectDraw_AddRef(&ddraw->IDirectDraw_iface);
980
                IDirectDraw2_AddRef(&ddraw->IDirectDraw2_iface);
981
                IDirectDraw4_AddRef(&ddraw->IDirectDraw4_iface);
982
                IDirectDraw7_AddRef(&ddraw->IDirectDraw7_iface);
983 984 985

                /* Does a D3D device exist? Destroy it
                    * TODO: Destroy all Vertex buffers, Lights, Materials
Austin English's avatar
Austin English committed
986
                    * and execute buffers too
987 988 989 990
                    */
                if(ddraw->d3ddevice)
                {
                    WARN("DDraw %p has d3ddevice %p attached\n", ddraw, ddraw->d3ddevice);
991
                    while(IDirect3DDevice7_Release(&ddraw->d3ddevice->IDirect3DDevice7_iface));
992
                }
993

994 995 996 997 998 999
                /* Destroy the swapchain after any 3D device. The 3D device
                 * cleanup code needs a swapchain. Specifically, it tries to
                 * set the current render target to the front buffer. */
                if (ddraw->wined3d_swapchain)
                    ddraw_destroy_swapchain(ddraw);

1000 1001 1002 1003 1004 1005
                /* 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++)
1006
                {
1007 1008
                    hr = IDirectDraw7_EnumSurfaces(&ddraw->IDirectDraw7_iface, DDENUMSURFACES_ALL,
                            &desc, ddraw, DestroyCallback);
1009 1010
                    if(hr != D3D_OK)
                        ERR("(%p) EnumSurfaces failed, prepare for trouble\n", ddraw);
1011
                }
1012

1013 1014
                if (!list_empty(&ddraw->surface_list))
                    ERR("DDraw %p still has surfaces attached.\n", ddraw);
1015 1016 1017 1018

                /* Release all hanging references to destroy the objects. This
                    * restores the screen mode too
                    */
1019
                while(IDirectDraw_Release(&ddraw->IDirectDraw_iface));
1020
                while(IDirectDraw2_Release(&ddraw->IDirectDraw2_iface));
1021
                while(IDirectDraw4_Release(&ddraw->IDirectDraw4_iface));
1022
                while(IDirectDraw7_Release(&ddraw->IDirectDraw7_iface));
1023
            }
1024
        }
1025 1026 1027

        /* Unregister the window class. */
        UnregisterClassA(DDRAW_WINDOW_CLASS_NAME, hInstDLL);
1028
    }
1029 1030 1031

    return TRUE;
}