mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-01 22:50:08 +00:00
a47e7badb9
Currently the ddraw capabilities were almost static, except of D3D support. When overlay support is added, the caps depend on certain settings in WineD3D or capabilities available from OpenGL and Xv. So set those caps in wined3d as well.
955 lines
31 KiB
C
955 lines
31 KiB
C
/* DirectDraw Base Functions
|
|
*
|
|
* Copyright 1997-1999 Marcus Meissner
|
|
* Copyright 1998 Lionel Ulmer
|
|
* Copyright 2000-2001 TransGaming Technologies Inc.
|
|
* Copyright 2006 Stefan Dösinger
|
|
* Copyright 2008 Denver Gingerich
|
|
*
|
|
* This file contains the (internal) driver registration functions,
|
|
* driver enumeration APIs and DirectDraw creation functions.
|
|
*
|
|
* 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
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
#include "wine/debug.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#define COBJMACROS
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winerror.h"
|
|
#include "wingdi.h"
|
|
#include "wine/exception.h"
|
|
#include "winreg.h"
|
|
|
|
#include "ddraw.h"
|
|
#include "d3d.h"
|
|
|
|
#include "ddraw_private.h"
|
|
|
|
typedef IWineD3D* (WINAPI *fnWineDirect3DCreate)(UINT, UINT, IUnknown *);
|
|
|
|
static fnWineDirect3DCreate pWineDirect3DCreate;
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
|
|
|
|
/* The configured default surface */
|
|
WINED3DSURFTYPE DefaultSurfaceType = SURFACE_UNKNOWN;
|
|
|
|
/* DDraw list and critical section */
|
|
static struct list global_ddraw_list = LIST_INIT(global_ddraw_list);
|
|
|
|
static CRITICAL_SECTION_DEBUG ddraw_cs_debug =
|
|
{
|
|
0, 0, &ddraw_cs,
|
|
{ &ddraw_cs_debug.ProcessLocksList,
|
|
&ddraw_cs_debug.ProcessLocksList },
|
|
0, 0, { (DWORD_PTR)(__FILE__ ": ddraw_cs") }
|
|
};
|
|
CRITICAL_SECTION ddraw_cs = { &ddraw_cs_debug, -1, 0, 0, 0, 0 };
|
|
|
|
/* value of ForceRefreshRate */
|
|
DWORD force_refresh_rate = 0;
|
|
|
|
/*
|
|
* Helper Function for DDRAW_Create and DirectDrawCreateClipper for
|
|
* lazy loading of the Wine D3D driver.
|
|
*
|
|
* Returns
|
|
* TRUE on success
|
|
* FALSE on failure.
|
|
*/
|
|
|
|
BOOL LoadWineD3D(void)
|
|
{
|
|
static HMODULE hWineD3D = (HMODULE) -1;
|
|
if (hWineD3D == (HMODULE) -1)
|
|
{
|
|
hWineD3D = LoadLibraryA("wined3d");
|
|
if (hWineD3D)
|
|
{
|
|
pWineDirect3DCreate = (fnWineDirect3DCreate) GetProcAddress(hWineD3D, "WineDirect3DCreate");
|
|
pWineDirect3DCreateClipper = (fnWineDirect3DCreateClipper) GetProcAddress(hWineD3D, "WineDirect3DCreateClipper");
|
|
return TRUE;
|
|
}
|
|
}
|
|
return hWineD3D != NULL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* 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:
|
|
* DD_OK if the Interface was created successfully
|
|
* CLASS_E_NOAGGREGATION if UnkOuter is not NULL
|
|
* E_OUTOFMEMORY if some allocation failed
|
|
*
|
|
***********************************************************************/
|
|
static HRESULT
|
|
DDRAW_Create(const GUID *guid,
|
|
void **DD,
|
|
IUnknown *UnkOuter,
|
|
REFIID iid)
|
|
{
|
|
IDirectDrawImpl *This = NULL;
|
|
HRESULT hr;
|
|
IWineD3D *wineD3D = NULL;
|
|
IWineD3DDevice *wineD3DDevice = NULL;
|
|
HDC hDC;
|
|
WINED3DDEVTYPE devicetype;
|
|
|
|
TRACE("(%s,%p,%p)\n", debugstr_guid(guid), DD, UnkOuter);
|
|
|
|
*DD = NULL;
|
|
|
|
/* 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;
|
|
}
|
|
else if(guid == (GUID *) DDCREATE_HARDWAREONLY)
|
|
{
|
|
devicetype = WINED3DDEVTYPE_HAL;
|
|
}
|
|
else
|
|
{
|
|
devicetype = 0;
|
|
}
|
|
|
|
/* DDraw doesn't support aggregation, according to msdn */
|
|
if (UnkOuter != NULL)
|
|
return CLASS_E_NOAGGREGATION;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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);
|
|
ICOM_INIT_INTERFACE(This, IDirectDraw3, IDirectDraw3_Vtbl);
|
|
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 */
|
|
hDC = GetDC(0);
|
|
This->orig_bpp = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
|
|
ReleaseDC(0, hDC);
|
|
This->orig_width = GetSystemMetrics(SM_CXSCREEN);
|
|
This->orig_height = GetSystemMetrics(SM_CYSCREEN);
|
|
|
|
if (!LoadWineD3D())
|
|
{
|
|
ERR("Couldn't load WineD3D - OpenGL libs not present?\n");
|
|
hr = DDERR_NODIRECTDRAWSUPPORT;
|
|
goto err_out;
|
|
}
|
|
|
|
/* 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.
|
|
*/
|
|
wineD3D = pWineDirect3DCreate(0 /* SDKVersion */, 7 /* DXVersion */, (IUnknown *) This /* Parent */);
|
|
if(!wineD3D)
|
|
{
|
|
ERR("Failed to initialise WineD3D\n");
|
|
hr = E_OUTOFMEMORY;
|
|
goto err_out;
|
|
}
|
|
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
|
|
* IDirectDraw7::Initialize. Its sole purpose is to return DD_OK on
|
|
* initialize only once
|
|
*/
|
|
This->initialized = FALSE;
|
|
|
|
/* Initialize WineD3DDevice
|
|
*
|
|
* It is used for screen setup, surface and palette creation
|
|
* When a Direct3DDevice7 is created, the D3D capabilities of WineD3D are
|
|
* 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))
|
|
{
|
|
ERR("Failed to create a wineD3DDevice, result = %x\n", hr);
|
|
goto err_out;
|
|
}
|
|
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;
|
|
}
|
|
|
|
/* Get the amount of video memory */
|
|
This->total_vidmem = IWineD3DDevice_GetAvailableTextureMem(This->wineD3DDevice);
|
|
|
|
list_init(&This->surface_list);
|
|
list_add_head(&global_ddraw_list, &This->ddraw_list_entry);
|
|
|
|
/* Call QueryInterface to get the pointer to the requested interface. This also initializes
|
|
* The required refcount
|
|
*/
|
|
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);
|
|
if(This) HeapFree(GetProcessHeap(), 0, This->decls);
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* DirectDrawCreate (DDRAW.@)
|
|
*
|
|
* Creates legacy DirectDraw Interfaces. Can't create IDirectDraw7
|
|
* interfaces in theory
|
|
*
|
|
* Arguments, return values: See DDRAW_Create
|
|
*
|
|
***********************************************************************/
|
|
HRESULT WINAPI
|
|
DirectDrawCreate(GUID *GUID,
|
|
LPDIRECTDRAW *DD,
|
|
IUnknown *UnkOuter)
|
|
{
|
|
HRESULT hr;
|
|
TRACE("(%s,%p,%p)\n", debugstr_guid(GUID), DD, UnkOuter);
|
|
|
|
EnterCriticalSection(&ddraw_cs);
|
|
hr = DDRAW_Create(GUID, (void **) DD, UnkOuter, &IID_IDirectDraw);
|
|
LeaveCriticalSection(&ddraw_cs);
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* 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,
|
|
LPVOID *DD,
|
|
REFIID iid,
|
|
IUnknown *UnkOuter)
|
|
{
|
|
HRESULT hr;
|
|
TRACE("(%s,%p,%s,%p)\n", debugstr_guid(GUID), DD, debugstr_guid(iid), UnkOuter);
|
|
|
|
if (!IsEqualGUID(iid, &IID_IDirectDraw7))
|
|
return DDERR_INVALIDPARAMS;
|
|
|
|
EnterCriticalSection(&ddraw_cs);
|
|
hr = DDRAW_Create(GUID, DD, UnkOuter, iid);
|
|
LeaveCriticalSection(&ddraw_cs);
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* 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,
|
|
LPVOID Context)
|
|
{
|
|
BOOL stop = FALSE;
|
|
|
|
TRACE(" Enumerating default DirectDraw HAL interface\n");
|
|
/* We only have one driver */
|
|
__TRY
|
|
{
|
|
static CHAR driver_desc[] = "DirectDraw HAL",
|
|
driver_name[] = "display";
|
|
|
|
stop = !Callback(NULL, driver_desc, driver_name, Context);
|
|
}
|
|
__EXCEPT_PAGE_FAULT
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
__ENDTRY
|
|
|
|
TRACE(" End of enumeration\n");
|
|
return DD_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* 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,
|
|
LPVOID Context,
|
|
DWORD Flags)
|
|
{
|
|
BOOL stop = FALSE;
|
|
TRACE("Enumerating default DirectDraw HAL interface\n");
|
|
|
|
/* We only have one driver by now */
|
|
__TRY
|
|
{
|
|
static CHAR driver_desc[] = "DirectDraw HAL",
|
|
driver_name[] = "display";
|
|
|
|
/* QuickTime expects the description "DirectDraw HAL" */
|
|
stop = !Callback(NULL, driver_desc, driver_name, Context, 0);
|
|
}
|
|
__EXCEPT_PAGE_FAULT
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
__ENDTRY;
|
|
|
|
TRACE("End of enumeration\n");
|
|
return DD_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* DirectDrawEnumerateW (DDRAW.@)
|
|
*
|
|
* Enumerates legacy drivers, unicode version. See
|
|
* the comments above DirectDrawEnumerateA for more details.
|
|
*
|
|
* The Flag member is not supported right now.
|
|
*
|
|
***********************************************************************/
|
|
|
|
/***********************************************************************
|
|
* DirectDrawEnumerateExW (DDRAW.@)
|
|
*
|
|
* Enumerates DirectDraw7 drivers, unicode version. See
|
|
* the comments above DirectDrawEnumerateA for more details.
|
|
*
|
|
* The Flag member is not supported right now.
|
|
*
|
|
***********************************************************************/
|
|
|
|
/***********************************************************************
|
|
* Classfactory implementation.
|
|
***********************************************************************/
|
|
|
|
/***********************************************************************
|
|
* 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)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p,%s,%p)\n", UnkOuter, debugstr_guid(iid), obj);
|
|
|
|
EnterCriticalSection(&ddraw_cs);
|
|
hr = DDRAW_Create(NULL, obj, UnkOuter, iid);
|
|
LeaveCriticalSection(&ddraw_cs);
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* 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)
|
|
{
|
|
HRESULT hr;
|
|
IDirectDrawClipper *Clip;
|
|
|
|
EnterCriticalSection(&ddraw_cs);
|
|
hr = DirectDrawCreateClipper(0, &Clip, UnkOuter);
|
|
if (hr != DD_OK)
|
|
{
|
|
LeaveCriticalSection(&ddraw_cs);
|
|
return hr;
|
|
}
|
|
|
|
hr = IDirectDrawClipper_QueryInterface(Clip, riid, obj);
|
|
IDirectDrawClipper_Release(Clip);
|
|
|
|
LeaveCriticalSection(&ddraw_cs);
|
|
return hr;
|
|
}
|
|
|
|
static const struct object_creation_info object_creation[] =
|
|
{
|
|
{ &CLSID_DirectDraw, CF_CreateDirectDraw },
|
|
{ &CLSID_DirectDraw7, CF_CreateDirectDraw },
|
|
{ &CLSID_DirectDrawClipper, CF_CreateDirectDrawClipper }
|
|
};
|
|
|
|
/*******************************************************************************
|
|
* 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
|
|
*
|
|
*******************************************************************************/
|
|
static HRESULT WINAPI
|
|
IDirectDrawClassFactoryImpl_QueryInterface(IClassFactory *iface,
|
|
REFIID riid,
|
|
void **obj)
|
|
{
|
|
ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
|
|
|
|
TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), obj);
|
|
|
|
if (IsEqualGUID(riid, &IID_IUnknown)
|
|
|| IsEqualGUID(riid, &IID_IClassFactory))
|
|
{
|
|
IClassFactory_AddRef(iface);
|
|
*obj = This;
|
|
return S_OK;
|
|
}
|
|
|
|
WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),obj);
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* IDirectDrawClassFactory::AddRef
|
|
*
|
|
* AddRef for the class factory
|
|
*
|
|
* RETURNS
|
|
* The new refcount
|
|
*
|
|
*******************************************************************************/
|
|
static ULONG WINAPI
|
|
IDirectDrawClassFactoryImpl_AddRef(IClassFactory *iface)
|
|
{
|
|
ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
|
|
ULONG ref = InterlockedIncrement(&This->ref);
|
|
|
|
TRACE("(%p)->() incrementing from %d.\n", This, ref - 1);
|
|
|
|
return ref;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* 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)
|
|
{
|
|
ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
|
TRACE("(%p)->() decrementing from %d.\n", This, ref+1);
|
|
|
|
if (ref == 0)
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
|
|
return ref;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* IDirectDrawClassFactory::CreateInstance
|
|
*
|
|
* What is this? Seems to create DirectDraw objects...
|
|
*
|
|
* Params
|
|
* The usual things???
|
|
*
|
|
* RETURNS
|
|
* ???
|
|
*
|
|
*******************************************************************************/
|
|
static HRESULT WINAPI
|
|
IDirectDrawClassFactoryImpl_CreateInstance(IClassFactory *iface,
|
|
IUnknown *UnkOuter,
|
|
REFIID riid,
|
|
void **obj)
|
|
{
|
|
ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
|
|
|
|
TRACE("(%p)->(%p,%s,%p)\n",This,UnkOuter,debugstr_guid(riid),obj);
|
|
|
|
return This->pfnCreateInstance(UnkOuter, riid, obj);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* IDirectDrawClassFactory::LockServer
|
|
*
|
|
* What is this?
|
|
*
|
|
* Params
|
|
* ???
|
|
*
|
|
* RETURNS
|
|
* S_OK, because it's a stub
|
|
*
|
|
*******************************************************************************/
|
|
static HRESULT WINAPI
|
|
IDirectDrawClassFactoryImpl_LockServer(IClassFactory *iface,BOOL dolock)
|
|
{
|
|
ICOM_THIS_FROM(IClassFactoryImpl, IClassFactory, iface);
|
|
FIXME("(%p)->(%d),stub!\n",This,dolock);
|
|
return S_OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* The class factory VTable
|
|
*******************************************************************************/
|
|
static const IClassFactoryVtbl IClassFactory_Vtbl =
|
|
{
|
|
IDirectDrawClassFactoryImpl_QueryInterface,
|
|
IDirectDrawClassFactoryImpl_AddRef,
|
|
IDirectDrawClassFactoryImpl_Release,
|
|
IDirectDrawClassFactoryImpl_CreateInstance,
|
|
IDirectDrawClassFactoryImpl_LockServer
|
|
};
|
|
|
|
/*******************************************************************************
|
|
* DllGetClassObject [DDRAW.@]
|
|
* 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
|
|
*/
|
|
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
|
|
{
|
|
unsigned int i;
|
|
IClassFactoryImpl *factory;
|
|
|
|
TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
|
|
|
|
if ( !IsEqualGUID( &IID_IClassFactory, riid )
|
|
&& ! IsEqualGUID( &IID_IUnknown, riid) )
|
|
return E_NOINTERFACE;
|
|
|
|
for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
|
|
{
|
|
if (IsEqualGUID(object_creation[i].clsid, rclsid))
|
|
break;
|
|
}
|
|
|
|
if (i == sizeof(object_creation)/sizeof(object_creation[0]))
|
|
{
|
|
FIXME("%s: no class found.\n", debugstr_guid(rclsid));
|
|
return CLASS_E_CLASSNOTAVAILABLE;
|
|
}
|
|
|
|
factory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*factory));
|
|
if (factory == NULL) return E_OUTOFMEMORY;
|
|
|
|
ICOM_INIT_INTERFACE(factory, IClassFactory, IClassFactory_Vtbl);
|
|
factory->ref = 1;
|
|
|
|
factory->pfnCreateInstance = object_creation[i].pfnCreateInstance;
|
|
|
|
*ppv = ICOM_INTERFACE(factory, IClassFactory);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* DllCanUnloadNow [DDRAW.@] Determines whether the DLL is in use.
|
|
*
|
|
* RETURNS
|
|
* Success: S_OK
|
|
* Failure: S_FALSE
|
|
*/
|
|
HRESULT WINAPI DllCanUnloadNow(void)
|
|
{
|
|
HRESULT hr;
|
|
FIXME("(void): stub\n");
|
|
|
|
EnterCriticalSection(&ddraw_cs);
|
|
hr = S_FALSE;
|
|
LeaveCriticalSection(&ddraw_cs);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* 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;
|
|
*******************************************************************************/
|
|
static HRESULT WINAPI
|
|
DestroyCallback(IDirectDrawSurface7 *surf,
|
|
DDSURFACEDESC2 *desc,
|
|
void *context)
|
|
{
|
|
IDirectDrawSurfaceImpl *Impl = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7, surf);
|
|
IDirectDrawImpl *ddraw = (IDirectDrawImpl *) context;
|
|
ULONG ref;
|
|
|
|
ref = IDirectDrawSurface7_Release(surf); /* For the EnumSurfaces */
|
|
WARN("Surface %p has an reference count of %d\n", Impl, ref);
|
|
|
|
/* Skip surfaces which are attached somewhere or which are
|
|
* part of a complex compound. They will get released when destroying
|
|
* the root
|
|
*/
|
|
if( (!Impl->is_complex_root) || (Impl->first_attached != Impl) )
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* get_config_key
|
|
*
|
|
* Reads a config key from the registry. Taken from WineD3D
|
|
*
|
|
***********************************************************************/
|
|
static inline DWORD get_config_key(HKEY defkey, HKEY appkey, const char* name, char* buffer, DWORD size)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* 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,
|
|
LPVOID lpv)
|
|
{
|
|
TRACE("(%p,%x,%p)\n", hInstDLL, Reason, lpv);
|
|
if (Reason == DLL_PROCESS_ATTACH)
|
|
{
|
|
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
|
|
{
|
|
ERR("Unknown default surface type. Supported are:\n gdi, opengl\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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 );
|
|
}
|
|
|
|
DisableThreadLibraryCalls(hInstDLL);
|
|
}
|
|
else if (Reason == DLL_PROCESS_DETACH)
|
|
{
|
|
if(!list_empty(&global_ddraw_list))
|
|
{
|
|
struct list *entry, *entry2;
|
|
WARN("There are still existing DirectDraw interfaces. Wine bug or buggy application?\n");
|
|
|
|
/* We remove elements from this loop */
|
|
LIST_FOR_EACH_SAFE(entry, entry2, &global_ddraw_list)
|
|
{
|
|
HRESULT hr;
|
|
DDSURFACEDESC2 desc;
|
|
int i;
|
|
IDirectDrawImpl *ddraw = LIST_ENTRY(entry, IDirectDrawImpl, ddraw_list_entry);
|
|
|
|
WARN("DDraw %p has a refcount of %d\n", ddraw, ddraw->ref7 + ddraw->ref4 + ddraw->ref3 + ddraw->ref2 + ddraw->ref1);
|
|
|
|
/* Add references to each interface to avoid freeing them unexpectedly */
|
|
IDirectDraw_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw));
|
|
IDirectDraw2_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw2));
|
|
IDirectDraw3_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw3));
|
|
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
|
|
* and execute buffers too
|
|
*/
|
|
if(ddraw->d3ddevice)
|
|
{
|
|
WARN("DDraw %p has d3ddevice %p attached\n", ddraw, ddraw->d3ddevice);
|
|
while(IDirect3DDevice7_Release(ICOM_INTERFACE(ddraw->d3ddevice, IDirect3DDevice7)));
|
|
}
|
|
|
|
/* 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++)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/* Check the surface count */
|
|
if(ddraw->surfaces > 0)
|
|
ERR("DDraw %p still has %d surfaces attached\n", ddraw, ddraw->surfaces);
|
|
|
|
/* 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)));
|
|
while(IDirectDraw3_Release(ICOM_INTERFACE(ddraw, IDirectDraw3)));
|
|
while(IDirectDraw4_Release(ICOM_INTERFACE(ddraw, IDirectDraw4)));
|
|
while(IDirectDraw7_Release(ICOM_INTERFACE(ddraw, IDirectDraw7)));
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|