/*		DirectDraw IDirectDraw interface (generic)
 *
 * Copyright 1997-2000 Marcus Meissner
 * Copyright 1998-2000 Lionel Ulmer (most of Direct3D stuff)
 * Copyright 2000-2003 TransGaming Technologies Inc.
 */

/*
 * This file contains all the interface functions that are shared between
 * all interfaces. Or better, it is a "common stub" library for the
 * IDirectDraw* objects
 */

#include "config.h"

#include <assert.h>
#include <string.h>

#include "winerror.h"
#include "ddraw.h"
#include "d3d.h"
#include "wine/debug.h"
#include "bitmap.h"

#include "ddraw_private.h"
#include "ddraw/main.h"
#include "dclipper/main.h"
#include "dpalette/main.h"
#include "dsurface/main.h"
#include "dsurface/dib.h"
#include "dsurface/exebuffer.h"
#include "dsurface/fakezbuffer.h"
#include "d3d_private.h"
#include "d3ddevice/main.h"

WINE_DEFAULT_DEBUG_CHANNEL(ddraw);

extern ICOM_VTABLE(IDirectDraw) DDRAW_IDirectDraw_VTable;
extern ICOM_VTABLE(IDirectDraw2) DDRAW_IDirectDraw2_VTable;
extern ICOM_VTABLE(IDirectDraw4) DDRAW_IDirectDraw4_VTable;

static void DDRAW_UnsubclassWindow(IDirectDrawImpl* This);

static void Main_DirectDraw_DeleteSurfaces(IDirectDrawImpl* This);
static void Main_DirectDraw_DeleteClippers(IDirectDrawImpl* This);
static void Main_DirectDraw_DeletePalettes(IDirectDrawImpl* This);
static void Main_DirectDraw_Delete3DDevices(IDirectDrawImpl* This);
static void LosePrimarySurface(IDirectDrawImpl* This);

static const char ddProp[] = "WINE_DDRAW_Property";

/* Not called from the vtable. */
HRESULT Main_DirectDraw_Construct(IDirectDrawImpl *This, BOOL ex)
{
    /* NOTE: The creator must use HEAP_ZERO_MEMORY or equivalent. */
    This->ref = 1;
    This->ex = ex;

    if (ex) This->local.dwLocalFlags |= DDRAWILCL_DIRECTDRAW7;
    This->local.dwProcessId = GetCurrentProcessId();

    This->final_release = Main_DirectDraw_final_release;

    This->create_palette = Main_DirectDrawPalette_Create;

    This->create_offscreen = Main_create_offscreen;
    This->create_texture   = Main_create_texture;
    This->create_zbuffer   = Main_create_zbuffer;
    This->create_exebuffer = Main_create_exebuffer;
    /* There are no generic versions of create_{primary,backbuffer}. */

    ICOM_INIT_INTERFACE(This, IDirectDraw,  DDRAW_IDirectDraw_VTable);
    ICOM_INIT_INTERFACE(This, IDirectDraw2, DDRAW_IDirectDraw2_VTable);
    ICOM_INIT_INTERFACE(This, IDirectDraw4, DDRAW_IDirectDraw4_VTable);
    /* There is no generic implementation of IDD7 */
    ICOM_INIT_INTERFACE(This, IDirect3D7, DDRAW_IDD3D7_VTable);
    ICOM_INIT_INTERFACE(This, IDirect3D3, DDRAW_IDD3D3_VTable);
    ICOM_INIT_INTERFACE(This, IDirect3D2, DDRAW_IDD3D2_VTable);
    ICOM_INIT_INTERFACE(This, IDirect3D, DDRAW_IDD3D_VTable);

    /* Make sure that linked lists of clippers, surfaces, 3ddevices, etc
     * are kept empty for starters */
    This->surfaces = NULL;
    This->palettes = NULL;
    This->clippers = NULL;
    This->devices = NULL;
    This->primary_surface = NULL;

    This->caps.dwSize = sizeof(This->caps);

    return DD_OK;
}

void Main_DirectDraw_final_release(IDirectDrawImpl* This)
{
    if (IsWindow(This->local.hWnd))
    {
	if (GetPropA(This->local.hWnd, ddProp))
	    DDRAW_UnsubclassWindow(This);
	else
	    FIXME("this shouldn't happen, right?\n");
    }

    Main_DirectDraw_DeleteSurfaces(This);
    Main_DirectDraw_DeleteClippers(This);
    Main_DirectDraw_DeletePalettes(This);
    Main_DirectDraw_Delete3DDevices(This);
    if (This->local.lpGbl && This->local.lpGbl->lpExclusiveOwner == &This->local)
    {
	This->local.lpGbl->lpExclusiveOwner = NULL;
	if (This->set_exclusive_mode)
	    This->set_exclusive_mode(This, FALSE);
    }
}

/* There is no Main_DirectDraw_Create. */

ULONG WINAPI Main_DirectDraw_AddRef(LPDIRECTDRAW7 iface) {
    ICOM_THIS(IDirectDrawImpl,iface);
    TRACE("(%p)->() incrementing from %lu.\n", This, This->ref );

    return ++This->ref;
}

ULONG WINAPI Main_DirectDraw_Release(LPDIRECTDRAW7 iface) {
    ULONG ref;
    ICOM_THIS(IDirectDrawImpl,iface);
    TRACE("(%p)->() decrementing from %lu.\n", This, This->ref );

    ref = --This->ref;

    if (ref == 0)
    {
	if (This->final_release != NULL)
	    This->final_release(This);

	/* We free the private. This is an artifact of the fact that I don't
	 * have the destructors set up correctly. */
	if (This->private != (This+1))
	    HeapFree(GetProcessHeap(), 0, This->private);

	HeapFree(GetProcessHeap(), 0, This);
    }

    return ref;
}

/* TODO: need to support IDirect3D. */
HRESULT WINAPI Main_DirectDraw_QueryInterface(
    LPDIRECTDRAW7 iface,REFIID refiid,LPVOID *obj
) {
    ICOM_THIS(IDirectDrawImpl,iface);
    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(refiid), obj);

    if ( IsEqualGUID( &IID_IUnknown, refiid )
	 || IsEqualGUID( &IID_IDirectDraw7, refiid ) )
    {
	*obj = ICOM_INTERFACE(This, IDirectDraw7);
    }
    else if ( IsEqualGUID( &IID_IDirectDraw, refiid ) )
    {
	*obj = ICOM_INTERFACE(This, IDirectDraw);
    }
    else if ( IsEqualGUID( &IID_IDirectDraw2, refiid ) )
    {
	*obj = ICOM_INTERFACE(This, IDirectDraw2);
    }
    else if ( IsEqualGUID( &IID_IDirectDraw4, refiid ) )
    {
	*obj = ICOM_INTERFACE(This, IDirectDraw4);
    }
    else if ( IsEqualGUID( &IID_IDirect3D7, refiid ) )
    {
	*obj = ICOM_INTERFACE(This, IDirect3D7);
    }
    else if ( IsEqualGUID( &IID_IDirect3D3, refiid ) )
    {
	*obj = ICOM_INTERFACE(This, IDirect3D3);
    }
    else if ( IsEqualGUID( &IID_IDirect3D2, refiid ) )
    {
	*obj = ICOM_INTERFACE(This, IDirect3D2);
    }
    else if ( IsEqualGUID( &IID_IDirect3D, refiid ) )
    {
	/* v1 doesn't have a CreateDevice, so I don't know how it's supposed to work...
	 * is it supposed to use Initialize instead or something? need docs... */
	*obj = ICOM_INTERFACE(This, IDirect3D);
    }
    else
    {
	FIXME("(%p)->(%s,%p): no interface\n",This,debugstr_guid(refiid),obj);
	return E_NOINTERFACE;
    }

    IDirectDraw7_AddRef(iface);
    return S_OK;
}

/* MSDN: "not currently implemented". */
HRESULT WINAPI Main_DirectDraw_Compact(LPDIRECTDRAW7 iface)
{
    TRACE("(%p)\n", iface);

    return DD_OK;
}

HRESULT WINAPI Main_DirectDraw_CreateClipper(LPDIRECTDRAW7 iface,
					     DWORD dwFlags,
					     LPDIRECTDRAWCLIPPER *ppClipper,
					     IUnknown *pUnkOuter)
{
    ICOM_THIS(IDirectDrawImpl, iface);
    HRESULT hr;

    TRACE("(%p)->(0x%lx, %p, %p)\n", iface, dwFlags, ppClipper, pUnkOuter);

    hr = DirectDrawCreateClipper(dwFlags, ppClipper, pUnkOuter);
    if (FAILED(hr)) return hr;

    /* dwFlags is passed twice, apparently an API wart. */
    hr = IDirectDrawClipper_Initialize(*ppClipper,
				       ICOM_INTERFACE(This, IDirectDraw),
				       dwFlags);
    if (FAILED(hr))
    {
	IDirectDrawClipper_Release(*ppClipper);
	return hr;
    }

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_CreatePalette(LPDIRECTDRAW7 iface, DWORD dwFlags,
			      LPPALETTEENTRY palent,
			      LPDIRECTDRAWPALETTE* ppPalette,
			      LPUNKNOWN pUnknown)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    LPDIRECTDRAWPALETTE pPalette;
    HRESULT hr;

    TRACE("(%p)->(%08lx,%p,%p,%p)\n",This,dwFlags,palent,ppPalette,pUnknown);

    if (ppPalette == NULL) return E_POINTER; /* unchecked */
    if (pUnknown != NULL) return CLASS_E_NOAGGREGATION; /* unchecked */

    hr = This->create_palette(This, dwFlags, &pPalette, pUnknown);
    if (FAILED(hr)) return hr;

    hr = IDirectDrawPalette_SetEntries(pPalette, 0, 0,
				       Main_DirectDrawPalette_Size(dwFlags),
				       palent);
    if (FAILED(hr))
    {
	IDirectDrawPalette_Release(pPalette);
	return hr;
    }
    else
    {
	*ppPalette = pPalette;
	return DD_OK;
    }
}

HRESULT
Main_create_offscreen(IDirectDrawImpl* This, const DDSURFACEDESC2* pDDSD,
		      LPDIRECTDRAWSURFACE7* ppSurf, LPUNKNOWN pOuter)
{
    assert(pOuter == NULL);

    return DIB_DirectDrawSurface_Create(This, pDDSD, ppSurf, pOuter);
}

HRESULT
Main_create_texture(IDirectDrawImpl* This, const DDSURFACEDESC2* pDDSD,
		    LPDIRECTDRAWSURFACE7* ppSurf, LPUNKNOWN pOuter,
		    DWORD dwMipMapLevel)
{
    assert(pOuter == NULL);

    if (pDDSD->u4.ddpfPixelFormat.dwFlags & DDPF_FOURCC) {
#if 0
	return Main_DirectDrawSurface_Create(This, pDDSD, ppSurf, pOuter);
#endif
	ERR("can't handle compressed textures\n");
	return DDERR_INVALIDPARAMS;
    }
    else
	return DIB_DirectDrawSurface_Create(This, pDDSD, ppSurf, pOuter);
}

HRESULT
Main_create_zbuffer(IDirectDrawImpl* This, const DDSURFACEDESC2* pDDSD,
		    LPDIRECTDRAWSURFACE7* ppSurf, LPUNKNOWN pOuter)
{
    assert(pOuter == NULL);

    return FakeZBuffer_DirectDrawSurface_Create(This, pDDSD, ppSurf, pOuter);
}

HRESULT
Main_create_exebuffer(IDirectDrawImpl* This, const DDSURFACEDESC2* pDDSD,
		      LPDIRECTDRAWSURFACE7* ppSurf, LPUNKNOWN pOuter)
{
    assert(pOuter == NULL);

    return ExeBuffer_DirectDrawSurface_Create(This, pDDSD, ppSurf, pOuter);
}

/* Does the texture surface described in pDDSD have any smaller mipmaps? */
static BOOL more_mipmaps(const DDSURFACEDESC2 *pDDSD)
{
    return ((pDDSD->dwFlags & DDSD_MIPMAPCOUNT) && pDDSD->u2.dwMipMapCount > 1
	    && (pDDSD->dwWidth > 1 || pDDSD->dwHeight > 1));
}

/* Create a texture surface along with any of its mipmaps. */
static HRESULT
create_mipmaps(IDirectDrawImpl* This, const DDSURFACEDESC2 *pDDSD,
	       LPDIRECTDRAWSURFACE7* ppSurf, LPUNKNOWN pUnkOuter)
{
    DWORD mipmap_level = 0;
    HRESULT hr;
    DWORD max;
    DDSURFACEDESC2 mipmap_surface_desc;

    mipmap_surface_desc = *pDDSD;

    /* A somewhat undocumented feature, if DDSCAPS_COMPLEX is set,
     * "It is permissible to omit the number of mipmaps levels, in which case
     * CreateSurface() will create a chain of surfaces each a power of two
     * smaller than the previous one down to the smallest possible size."
     */
    if(!(mipmap_surface_desc.dwFlags & DDSD_MIPMAPCOUNT) &&
       (mipmap_surface_desc.ddsCaps.dwCaps & DDSCAPS_COMPLEX) &&
       (mipmap_surface_desc.ddsCaps.dwCaps & DDSCAPS_MIPMAP)){
	DDRAW_dump_surface_desc(pDDSD);
	max = (mipmap_surface_desc.dwWidth > mipmap_surface_desc.dwHeight) ? 
	      mipmap_surface_desc.dwWidth : mipmap_surface_desc.dwHeight;
	mipmap_surface_desc.u2.dwMipMapCount = 1;
	while(max > 1){
	    max /= 2;
	    ++mipmap_surface_desc.u2.dwMipMapCount;
	}	
	mipmap_surface_desc.dwFlags |= DDSD_MIPMAPCOUNT;
    }

    hr = This->create_texture(This, &mipmap_surface_desc, ppSurf, pUnkOuter, mipmap_level);
    if (FAILED(hr)) return hr;

    /* Create attached mipmaps if required. */
    if (more_mipmaps(&mipmap_surface_desc))
    {
	LPDIRECTDRAWSURFACE7 mipmap;
	LPDIRECTDRAWSURFACE7 prev_mipmap;

	prev_mipmap = *ppSurf;
	IDirectDrawSurface7_AddRef(prev_mipmap);
	mipmap_surface_desc.ddsCaps.dwCaps2 |= DDSCAPS2_MIPMAPSUBLEVEL;

	while (more_mipmaps(&mipmap_surface_desc))
	{
	    mipmap_level++;

	    mipmap_surface_desc.u2.dwMipMapCount--;

	    if (mipmap_surface_desc.dwWidth > 1)
		mipmap_surface_desc.dwWidth /= 2;

	    if (mipmap_surface_desc.dwHeight > 1)
		mipmap_surface_desc.dwHeight /= 2;

	    DDRAW_calc_image_size(&mipmap_surface_desc);

	    hr = This->create_texture(This, &mipmap_surface_desc, &mipmap,
				      pUnkOuter, mipmap_level);
	    if (FAILED(hr))
	    {
		IDirectDrawSurface7_Release(prev_mipmap);
		IDirectDrawSurface7_Release(*ppSurf);
		return hr;
	    }

	    IDirectDrawSurface7_AddAttachedSurface(prev_mipmap, mipmap);
	    IDirectDrawSurface7_Release(prev_mipmap);
	    prev_mipmap = mipmap;
	}

	IDirectDrawSurface7_Release(prev_mipmap);
    }

    return DD_OK;
}

/* Create a texture. */
static HRESULT
create_texture(IDirectDrawImpl* This, const DDSURFACEDESC2 *pDDSD,
	       LPDIRECTDRAWSURFACE7* ppSurf, LPUNKNOWN pUnkOuter)
{
    DDSURFACEDESC2 ddsd;
    HRESULT hr;

    assert(pUnkOuter == NULL);

    /* is this check right? (pixelformat can be copied from primary) */
    if ((pDDSD->dwFlags&(DDSD_HEIGHT|DDSD_WIDTH)) != (DDSD_HEIGHT|DDSD_WIDTH))
	return DDERR_INVALIDPARAMS;

    DD_STRUCT_INIT((&ddsd));
    DD_STRUCT_COPY_BYSIZE((&ddsd),pDDSD);

    if (!(ddsd.dwFlags & DDSD_PIXELFORMAT))
    {
	ddsd.u4.ddpfPixelFormat = This->pixelformat;
	ddsd.dwFlags |= DDSD_PIXELFORMAT;
    }

    if (ddsd.u4.ddpfPixelFormat.dwFlags & DDPF_FOURCC)
    {
#if 0
        FIXME("Compressed Textures are broken still\n");
	    return DDERR_INVALIDPARAMS;
#endif

	if (ddsd.dwFlags & DDSD_PITCH)
	    return DDERR_INVALIDPARAMS;
	if (!DDRAW_calc_image_size(&ddsd))
	    return DDERR_INVALIDPARAMS;
	TRACE("computed linear size %ld\n", ddsd.u1.dwLinearSize);
    }
    else if (!(ddsd.dwFlags & DDSD_PITCH))
    {
	ddsd.u1.lPitch = DDRAW_width_bpp_to_pitch(ddsd.dwWidth,
						  GET_BPP(ddsd)*8);
	ddsd.dwFlags |= DDSD_PITCH;
	TRACE("computed pitch %ld\n", ddsd.u1.lPitch);
    }

    if (ddsd.ddsCaps.dwCaps2 & DDSCAPS2_CUBEMAP)
    {
	unsigned f, u, c;
	DDSURFACEDESC2 cube_surface_desc;
	LPDIRECTDRAWSURFACE7 face[6];
	IDirectDrawSurfaceImpl *surf;
	IDirectDrawSurfaceImpl *next;
	LPATTACHLIST child;

	/* first, create all requested cubemap faces */
	for (c=0,f=0; f<6; f++) {
	    u = 0x400 << f; /* DDSCAPS2_CUBEMAP_* */
	    if (ddsd.ddsCaps.dwCaps2 & u) {
		cube_surface_desc = ddsd;
		cube_surface_desc.ddsCaps.dwCaps2 &= ~DDSCAPS2_CUBEMAP_ALLFACES;
		cube_surface_desc.ddsCaps.dwCaps2 |= u;
		hr = create_mipmaps(This, &cube_surface_desc, &face[c], pUnkOuter);
		if (FAILED(hr)) {
		    for (f=0; f<c; f++)
			IDirectDrawSurface7_Release(face[f]);
		    return hr;
		}
		c++;
	    }
	}
	if (!c) return DDERR_INVALIDCAPS;

	/* From what I understand, it should be possible to get at any face
	 * of a cube map from any other face via GetAttachedSurface, and the
	 * docs claim that a cubemap cannot be created by manually attaching
	 * surfaces, so it seems to me that a cubemap should be created here
	 * as a circular attachment list, kinda like this
	 * (sibling relations go down, children relations go right):
	 *
	 *  /--> POSITIVEX --\
	 *  |       | /------/
	 *  |       V V
	 *  |    NEGATIVEX --\
	 *  |       | /------/
	 *  |       V V
	 *  |    POSITIVEY --\
	 *  |       | /------/
	 *  |       V V
	 *  |    NEGATIVEY --\
	 *  |       | /------/
	 *  |       V V
	 *  |    POSITIVEZ --\
	 *  |       | /------/
	 *  |       V V
	 *  |    NEGATIVEZ --\
	 *  |       |        |
	 *  \-------+--------/
	 *
	 * Unfortunately, it gets more complicated when the cubemap is
	 * mipmapped, since each mipmap level of a face is a child of it,
	 * independent of the other faces, so to satisfy the requirements,
	 * the relation needs to become:
	 *
	 *  /--> POSITIVEX --> MIPLEVEL --> MIPLEVEL
	 *  |       | /-----------/
	 *  |       V V
	 *  |    NEGATIVEX --> MIPLEVEL --> MIPLEVEL
	 *  |       | /-----------/
	 *  |       V V
	 *  |    POSITIVEY --> MIPLEVEL --> MIPLEVEL
	 *  |       | /-----------/
	 *  |       V V
	 *  |    NEGATIVEY --> MIPLEVEL --> MIPLEVEL
	 *  |       | /-----------/
	 *  |       V V
	 *  |    POSITIVEZ --> MIPLEVEL --> MIPLEVEL
	 *  |       | /-----------/
	 *  |       V V
	 *  |    NEGATIVEZ --> MIPLEVEL --> MIPLEVEL
	 *  |       |             |
	 *  \-------+-------------/
	 */

	surf = ICOM_OBJECT(IDirectDrawSurfaceImpl,
			   IDirectDrawSurface7, face[c-1]);
	for (f=0; f<c; f++) {
	    next = surf;
	    surf = ICOM_OBJECT(IDirectDrawSurfaceImpl,
			       IDirectDrawSurface7, face[f]);
	    child = surf->t.lcl.lpAttachList;
	    if (child) {
		while (child->lpLink)
		    child = child->lpLink;
		child->lpLink = &next->sibling;
	    }
	    else surf->t.lcl.lpAttachList = &next->sibling;
	    surf->sibling.lpLink = &next->sibling;
	    next->t.lcl.lpAttachListFrom = &surf->sibling;
	}

	*ppSurf = face[0];
	return DD_OK;
    }

    hr = create_mipmaps(This, &ddsd, ppSurf, pUnkOuter);
    if (FAILED(hr)) return hr;

    return DD_OK;
}

/* Creates a primary surface and any indicated backbuffers. */
static HRESULT
create_primary(IDirectDrawImpl* This, LPDDSURFACEDESC2 pDDSD,
	       LPDIRECTDRAWSURFACE7* ppSurf, LPUNKNOWN pUnkOuter)
{
    DDSURFACEDESC2 ddsd;
    HRESULT hr;

    assert(pUnkOuter == NULL);

    if (This->primary_surface != NULL)
	return DDERR_PRIMARYSURFACEALREADYEXISTS;

    /* as documented (what about pitch?) */
    if (pDDSD->dwFlags & (DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT))
	return DDERR_INVALIDPARAMS;

    DD_STRUCT_INIT((&ddsd));
    DD_STRUCT_COPY_BYSIZE((&ddsd),pDDSD);
    ddsd.dwFlags |= DDSD_HEIGHT | DDSD_WIDTH | DDSD_PITCH | DDSD_PIXELFORMAT;
    ddsd.dwHeight = This->height;
    ddsd.dwWidth = This->width;
    ddsd.u1.lPitch = This->pitch;
    ddsd.u4.ddpfPixelFormat = This->pixelformat;
    ddsd.ddsCaps.dwCaps |= DDSCAPS_LOCALVIDMEM | DDSCAPS_VIDEOMEMORY
	| DDSCAPS_VISIBLE | DDSCAPS_FRONTBUFFER |
	(pDDSD->ddsCaps.dwCaps & DDSCAPS_3DDEVICE);

    if ((ddsd.dwFlags & DDSD_BACKBUFFERCOUNT) && ddsd.dwBackBufferCount > 0)
	ddsd.ddsCaps.dwCaps |= DDSCAPS_FLIP;

    hr = This->create_primary(This, &ddsd, ppSurf, pUnkOuter);
    if (FAILED(hr)) return hr;

    if (ddsd.dwFlags & DDSD_BACKBUFFERCOUNT)
    {
	IDirectDrawSurfaceImpl* primary;
	LPDIRECTDRAWSURFACE7 pPrev;
	DWORD i;

	ddsd.dwFlags &= ~DDSD_BACKBUFFERCOUNT;
	ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_VISIBLE | DDSCAPS_PRIMARYSURFACE
				 | DDSCAPS_BACKBUFFER | DDSCAPS_FRONTBUFFER);

	primary = ICOM_OBJECT(IDirectDrawSurfaceImpl,IDirectDrawSurface7,
			      *ppSurf);
	pPrev = *ppSurf;
	IDirectDrawSurface7_AddRef(pPrev);

	for (i=0; i < ddsd.dwBackBufferCount; i++)
	{
	    LPDIRECTDRAWSURFACE7 pBack;

	    if (i == 0)
		ddsd.ddsCaps.dwCaps |= DDSCAPS_BACKBUFFER;
	    else
		ddsd.ddsCaps.dwCaps &= ~DDSCAPS_BACKBUFFER;

	    hr = This->create_backbuffer(This, &ddsd, &pBack, pUnkOuter,
					 primary);

	    if (FAILED(hr))
	    {
		IDirectDraw7_Release(pPrev);
		IDirectDraw7_Release(*ppSurf);
		return hr;
	    }
            else
            {
                ICOM_THIS( IDirectDrawSurfaceImpl, pBack );
                This->implicit = TRUE;
            }

	    IDirectDrawSurface7_AddAttachedSurface(pPrev, pBack);
	    IDirectDrawSurface7_Release(pPrev);
	    pPrev = pBack;
	}

	IDirectDrawSurface7_Release(pPrev);
    }

    This->primary_surface = (IDirectDrawSurfaceImpl *)*ppSurf;

    return DD_OK;
}

static HRESULT
create_offscreen(IDirectDrawImpl* This, LPDDSURFACEDESC2 pDDSD,
		 LPDIRECTDRAWSURFACE7* ppSurf, LPUNKNOWN pUnkOuter)
{
    DDSURFACEDESC2 ddsd;
    HRESULT hr;

    /* is this check right? (pixelformat can be copied from primary) */
    if ((pDDSD->dwFlags&(DDSD_HEIGHT|DDSD_WIDTH)) != (DDSD_HEIGHT|DDSD_WIDTH))
	return DDERR_INVALIDPARAMS;

    ddsd.dwSize = sizeof(ddsd);
    DD_STRUCT_COPY_BYSIZE((&ddsd),pDDSD);

    if (!(ddsd.dwFlags & DDSD_PIXELFORMAT))
    {
	/* The DDRAW_WndProc calls RestoreDisplayMode when losing focus.
	 * If the app tries to create surfaces before recovering focus,
	 * they may be created with the wrong pixelformat. */
	if (This->primary_surface)
	    ddsd.u4.ddpfPixelFormat
		= This->primary_surface->surface_desc.u4.ddpfPixelFormat;
	else
	    ddsd.u4.ddpfPixelFormat = This->pixelformat;
    }

    if (!(ddsd.dwFlags & DDSD_PITCH))
    {
	ddsd.u1.lPitch = DDRAW_width_bpp_to_pitch(ddsd.dwWidth,
						  GET_BPP(ddsd)*8);
    }

    ddsd.dwFlags |= DDSD_PITCH | DDSD_PIXELFORMAT;

    hr = This->create_offscreen(This, &ddsd, ppSurf, pUnkOuter);
    if (FAILED(hr)) return hr;

    return hr;
}

HRESULT WINAPI
Main_DirectDraw_CreateSurface(LPDIRECTDRAW7 iface, LPDDSURFACEDESC2 pDDSD,
			      LPDIRECTDRAWSURFACE7 *ppSurf,
			      IUnknown *pUnkOuter)
{
    HRESULT hr;
    ICOM_THIS(IDirectDrawImpl, iface);

    TRACE("(%p)->(%p,%p,%p)\n",This,pDDSD,ppSurf,pUnkOuter);
    TRACE(" flags: %08lx\n", pDDSD->dwFlags);
    TRACE(" caps: %08lx\n", pDDSD->ddsCaps.dwCaps);
    if (pDDSD->dwFlags & (DDSD_HEIGHT | DDSD_WIDTH))
	TRACE(" size: %ldx%ld\n", pDDSD->dwWidth, pDDSD->dwHeight);
    if (pDDSD->dwFlags & DDSD_PITCH)
	TRACE(" pitch: %ld\n", pDDSD->u1.lPitch);
    if (pDDSD->dwFlags & DDSD_LPSURFACE)
	TRACE(" client surface: %p\n", pDDSD->lpSurface);
    if (pDDSD->dwFlags & DDSD_BACKBUFFERCOUNT)
	TRACE(" back buffers: %lu\n", pDDSD->dwBackBufferCount);
    if (pDDSD->dwFlags & DDSD_ZBUFFERBITDEPTH)
	TRACE(" z bit depth: %lu\n", ((LPDDSURFACEDESC)pDDSD)->u2.dwZBufferBitDepth);
    if (pDDSD->dwFlags & DDSD_PIXELFORMAT) {
	LPDDPIXELFORMAT pf = &pDDSD->u4.ddpfPixelFormat;
	TRACE(" pf-flags: %08lx\n", pf->dwFlags);
	if (pf->dwFlags & DDPF_FOURCC)
	    TRACE(" pf-FourCC: %4.4s\n", (const char*)&pf->dwFourCC);
	if (pf->dwFlags & DDPF_RGB) {
	    TRACE(" pf-RGB-BitCount: %ld\n", pf->u1.dwRGBBitCount);
	    TRACE(" pf-RGB-BitMasks: %08lx %08lx %08lx\n", pf->u2.dwRBitMask, pf->u3.dwGBitMask, pf->u4.dwBBitMask);
	}
	if (pf->dwFlags & DDPF_ALPHAPIXELS)
	    TRACE(" pf-ALPHA-BitMask: %08lx\n", pf->u5.dwRGBAlphaBitMask);
    }

    if (pUnkOuter != NULL) {
	FIXME("outer != NULL?\n");
	return CLASS_E_NOAGGREGATION; /* unchecked */
    }

    if (!(pDDSD->dwFlags & DDSD_CAPS)) {
	/* DVIDEO.DLL does forget the DDSD_CAPS flag ... *sigh* */
    	pDDSD->dwFlags |= DDSD_CAPS;
    }

    if (ppSurf == NULL) {
	FIXME("You want to get back a surface? Don't give NULL ptrs!\n");
	return E_POINTER; /* unchecked */
    }

    if (pDDSD->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
    {
	/* create primary surface & backbuffers */
	hr = create_primary(This, pDDSD, ppSurf, pUnkOuter);
    }
    else if (pDDSD->ddsCaps.dwCaps & DDSCAPS_BACKBUFFER)
    {
       /* create backbuffer surface */
       hr = This->create_backbuffer(This, pDDSD, ppSurf, pUnkOuter, NULL);
    }
    else if (pDDSD->ddsCaps.dwCaps & DDSCAPS_OFFSCREENPLAIN)
    {
	/* create offscreenplain surface */
	hr = create_offscreen(This, pDDSD, ppSurf, pUnkOuter);
    }
    else if (pDDSD->ddsCaps.dwCaps & DDSCAPS_ZBUFFER)
    {
        DWORD oldZBitDepth = pDDSD->u4.ddpfPixelFormat.u1.dwZBufferBitDepth;
        DWORD oldPitch =  pDDSD->u1.lPitch;
        if (pDDSD->dwFlags & DDSD_ZBUFFERBITDEPTH) {
           /* old-style DDSURFACEDESC */
           pDDSD->u4.ddpfPixelFormat.u1.dwZBufferBitDepth = ((LPDDSURFACEDESC)pDDSD)->u2.dwZBufferBitDepth;
        }
        if (pDDSD->u4.ddpfPixelFormat.u1.dwZBufferBitDepth == 0)
        {
           FIXME("App has asked us to create a 0-depth ZBuffer - why?  Defaulting to 16-bit ZBuffer\n");
           pDDSD->u4.ddpfPixelFormat.u1.dwZBufferBitDepth = 16;
           pDDSD->u1.lPitch = pDDSD->dwWidth * GET_BPP(*pDDSD);
        }

	/* create z-buffer */
	hr = This->create_zbuffer(This, pDDSD, ppSurf, pUnkOuter);

        pDDSD->u4.ddpfPixelFormat.u1.dwZBufferBitDepth = oldZBitDepth;
        pDDSD->u1.lPitch = oldPitch;
    }
    else if (pDDSD->ddsCaps.dwCaps & DDSCAPS_TEXTURE)
    {
	/* create texture */
	hr = create_texture(This, pDDSD, ppSurf, pUnkOuter);
    }
    else
    {
	/* Otherwise, assume offscreenplain surface */
	FIXME("App didn't request a valid surface type (0x%08lx)- assuming offscreenplain\n", pDDSD->ddsCaps.dwCaps);
	hr = create_offscreen(This, pDDSD, ppSurf, pUnkOuter);
    }

    if (FAILED(hr)) {
	FIXME("failed surface creation with code 0x%08lx\n",hr);
	return hr;
    }

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_DuplicateSurface(LPDIRECTDRAW7 iface, LPDIRECTDRAWSURFACE7 src,
				 LPDIRECTDRAWSURFACE7* dst)
{
    ICOM_THIS(IDirectDrawImpl,iface);

    IDirectDrawSurfaceImpl *pSrc = ICOM_OBJECT(IDirectDrawSurfaceImpl,
					       IDirectDrawSurface7, src);

    TRACE("(%p)->(%p,%p)\n",This,src,dst);

    return pSrc->duplicate_surface(pSrc, dst);
}

/* EnumDisplayModes */

BOOL Main_DirectDraw_DDPIXELFORMAT_Match(const DDPIXELFORMAT *requested,
					 const DDPIXELFORMAT *provided)
{
    /* Some flags must be present in both or neither for a match. */
    static const DWORD must_match = DDPF_PALETTEINDEXED1 | DDPF_PALETTEINDEXED2
	| DDPF_PALETTEINDEXED4 | DDPF_PALETTEINDEXED8 | DDPF_FOURCC
	| DDPF_ZBUFFER | DDPF_STENCILBUFFER;

    if ((requested->dwFlags & provided->dwFlags) != requested->dwFlags)
	return FALSE;

    if ((requested->dwFlags & must_match) != (provided->dwFlags & must_match))
	return FALSE;

    if (requested->dwFlags & DDPF_FOURCC)
	if (requested->dwFourCC != provided->dwFourCC)
	    return FALSE;

    if (requested->dwFlags & (DDPF_RGB|DDPF_YUV|DDPF_ZBUFFER|DDPF_ALPHA
			      |DDPF_LUMINANCE|DDPF_BUMPDUDV))
	if (requested->u1.dwRGBBitCount != provided->u1.dwRGBBitCount)
	    return FALSE;

    if (requested->dwFlags & (DDPF_RGB|DDPF_YUV|DDPF_STENCILBUFFER
			      |DDPF_LUMINANCE|DDPF_BUMPDUDV))
	if (requested->u2.dwRBitMask != provided->u2.dwRBitMask)
	    return FALSE;

    if (requested->dwFlags & (DDPF_RGB|DDPF_YUV|DDPF_ZBUFFER|DDPF_BUMPDUDV))
	if (requested->u3.dwGBitMask != provided->u3.dwGBitMask)
	    return FALSE;

    /* I could be wrong about the bumpmapping. MSDN docs are vague. */
    if (requested->dwFlags & (DDPF_RGB|DDPF_YUV|DDPF_STENCILBUFFER
			      |DDPF_BUMPDUDV))
	if (requested->u4.dwBBitMask != provided->u4.dwBBitMask)
	    return FALSE;

    if (requested->dwFlags & (DDPF_ALPHAPIXELS|DDPF_ZPIXELS))
	if (requested->u5.dwRGBAlphaBitMask != provided->u5.dwRGBAlphaBitMask)
	    return FALSE;

    return TRUE;
}

BOOL Main_DirectDraw_DDSD_Match(const DDSURFACEDESC2* requested,
				const DDSURFACEDESC2* provided)
{
    struct compare_info
    {
	DWORD flag;
	ptrdiff_t offset;
	size_t size;
    };

#define CMP(FLAG, FIELD)				\
	{ DDSD_##FLAG, offsetof(DDSURFACEDESC2, FIELD),	\
	  sizeof(((DDSURFACEDESC2 *)(NULL))->FIELD) }

    static const struct compare_info compare[] = {
	CMP(ALPHABITDEPTH, dwAlphaBitDepth),
	CMP(BACKBUFFERCOUNT, dwBackBufferCount),
	CMP(CAPS, ddsCaps),
	CMP(CKDESTBLT, ddckCKDestBlt),
	CMP(CKDESTOVERLAY, u3.ddckCKDestOverlay),
	CMP(CKSRCBLT, ddckCKSrcBlt),
	CMP(CKSRCOVERLAY, ddckCKSrcOverlay),
	CMP(HEIGHT, dwHeight),
	CMP(LINEARSIZE, u1.dwLinearSize),
	CMP(LPSURFACE, lpSurface),
	CMP(MIPMAPCOUNT, u2.dwMipMapCount),
	CMP(PITCH, u1.lPitch),
	/* PIXELFORMAT: manual */
	CMP(REFRESHRATE, u2.dwRefreshRate),
	CMP(TEXTURESTAGE, dwTextureStage),
	CMP(WIDTH, dwWidth),
	/* ZBUFFERBITDEPTH: "obsolete" */
    };

#undef CMP

    int i;

    if ((requested->dwFlags & provided->dwFlags) != requested->dwFlags)
	return FALSE;

    for (i=0; i < sizeof(compare)/sizeof(compare[0]); i++)
    {
	if (requested->dwFlags & compare[i].flag
	    && memcmp((const char *)provided + compare[i].offset,
		      (const char *)requested + compare[i].offset,
		      compare[i].size) != 0)
	    return FALSE;
    }

    if (requested->dwFlags & DDSD_PIXELFORMAT)
    {
	if (!Main_DirectDraw_DDPIXELFORMAT_Match(&requested->u4.ddpfPixelFormat,
						 &provided->u4.ddpfPixelFormat))
	    return FALSE;
    }

    return TRUE;
}

#define DDENUMSURFACES_SEARCHTYPE (DDENUMSURFACES_CANBECREATED|DDENUMSURFACES_DOESEXIST)
#define DDENUMSURFACES_MATCHTYPE (DDENUMSURFACES_ALL|DDENUMSURFACES_MATCH|DDENUMSURFACES_NOMATCH)

/* This should be extended so that it can be used by
 * IDirectDrawSurface7::EnumAttachedSurfaces. */
HRESULT
Main_DirectDraw_EnumExistingSurfaces(IDirectDrawImpl *This, DWORD dwFlags,
				     LPDDSURFACEDESC2 lpDDSD2, LPVOID context,
				     LPDDENUMSURFACESCALLBACK7 callback)
{
    IDirectDrawSurfaceImpl *surf;
    BOOL all, nomatch;

    /* A NULL lpDDSD2 is permitted if we are enumerating all surfaces anyway */
    if (lpDDSD2 == NULL && !(dwFlags & DDENUMSURFACES_ALL))
	return DDERR_INVALIDPARAMS;

    all = dwFlags & DDENUMSURFACES_ALL;
    nomatch = dwFlags & DDENUMSURFACES_NOMATCH;

    for (surf = This->surfaces; surf != NULL; surf = surf->next_ddraw)
    {
	if (all
	    || (nomatch != Main_DirectDraw_DDSD_Match(lpDDSD2,
						      &surf->surface_desc)))
	{
	    LPDIRECTDRAWSURFACE7 surface = ICOM_INTERFACE(surf,
							  IDirectDrawSurface7);

	    /* BOGUS! Violates COM rules, but MSDN says so. */
	    IDirectDrawSurface7_AddRef(surface);

	    if (callback(surface, &surf->surface_desc, context)
		== DDENUMRET_CANCEL)
		break;
	}
    }

    return DD_OK;
}

/* I really don't understand how this is supposed to work.
 * We only consider dwHeight, dwWidth and ddpfPixelFormat.dwFlags. */
HRESULT
Main_DirectDraw_EnumCreateableSurfaces(IDirectDrawImpl *This, DWORD dwFlags,
				       LPDDSURFACEDESC2 lpDDSD2,
				       LPVOID context,
				       LPDDENUMSURFACESCALLBACK7 callback)
{
    FIXME("This isn't going to work.\n");

    if ((dwFlags & DDENUMSURFACES_MATCHTYPE) != DDENUMSURFACES_MATCH)
	return DDERR_INVALIDPARAMS;

    /* TODO: implement this.
     * Does this work before SCL is called?
     * Does it only consider off-screen surfaces?
     */

    return E_FAIL;
}

/* For unsigned x. 0 is not a power of 2. */
#define IS_POW_2(x) (((x) & ((x) - 1)) == 0)

HRESULT WINAPI
Main_DirectDraw_EnumSurfaces(LPDIRECTDRAW7 iface, DWORD dwFlags,
			     LPDDSURFACEDESC2 lpDDSD2, LPVOID context,
			     LPDDENUMSURFACESCALLBACK7 callback)
{
    ICOM_THIS(IDirectDrawImpl, iface);
    TRACE("(%p)->(0x%lx, %p, %p, %p)\n", iface, dwFlags, lpDDSD2, context,
	  callback);

    if (callback == NULL)
	return DDERR_INVALIDPARAMS;

    if (dwFlags & ~(DDENUMSURFACES_SEARCHTYPE|DDENUMSURFACES_MATCHTYPE))
	return DDERR_INVALIDPARAMS;

    if (!IS_POW_2(dwFlags & DDENUMSURFACES_SEARCHTYPE)
	|| !IS_POW_2(dwFlags & DDENUMSURFACES_MATCHTYPE))
	return DDERR_INVALIDPARAMS;

    if (dwFlags & DDENUMSURFACES_DOESEXIST)
    {
	return Main_DirectDraw_EnumExistingSurfaces(This, dwFlags, lpDDSD2,
						    context, callback);
    }
    else
    {
	return Main_DirectDraw_EnumCreateableSurfaces(This, dwFlags, lpDDSD2,
						      context, callback);
    }
}

HRESULT WINAPI
Main_DirectDraw_EvaluateMode(LPDIRECTDRAW7 iface,DWORD a,DWORD* b)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    FIXME("(%p)->() stub\n", This);

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_FlipToGDISurface(LPDIRECTDRAW7 iface)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    TRACE("(%p)->()\n",This);
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_GetCaps(LPDIRECTDRAW7 iface, LPDDCAPS pDriverCaps,
			LPDDCAPS pHELCaps)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    TRACE("(%p,%p,%p), stub\n",This,pDriverCaps,pHELCaps);
    if (pDriverCaps != NULL)
	DD_STRUCT_COPY_BYSIZE(pDriverCaps,&This->caps);
    if (pHELCaps != NULL)
	DD_STRUCT_COPY_BYSIZE(pHELCaps,&This->caps);
    return DD_OK;
}

/* GetCaps */
/* GetDeviceIdentifier */
/* GetDIsplayMode */

HRESULT WINAPI
Main_DirectDraw_GetFourCCCodes(LPDIRECTDRAW7 iface, LPDWORD pNumCodes,
			       LPDWORD pCodes)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    if (*pNumCodes) {
	    *pNumCodes=0;
    }
    FIXME("(%p,%p,%p), stub\n",This,pNumCodes,pCodes);
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_GetGDISurface(LPDIRECTDRAW7 iface,
			      LPDIRECTDRAWSURFACE7 *lplpGDIDDSSurface)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    TRACE("(%p)->(%p)\n", This, lplpGDIDDSSurface);
    TRACE("returning primary (%p)\n", This->primary_surface);
    *lplpGDIDDSSurface = ICOM_INTERFACE(This->primary_surface, IDirectDrawSurface7);
    if (*lplpGDIDDSSurface)
	IDirectDrawSurface7_AddRef(*lplpGDIDDSSurface);
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_GetMonitorFrequency(LPDIRECTDRAW7 iface,LPDWORD freq)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    FIXME("(%p)->(%p) returns 60 Hz always\n",This,freq);
    *freq = 60*100; /* 60 Hz */
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_GetScanLine(LPDIRECTDRAW7 iface, LPDWORD lpdwScanLine)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    FIXME("(%p)->(%p)\n", This, lpdwScanLine);

    *lpdwScanLine = 1;

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_GetSurfaceFromDC(LPDIRECTDRAW7 iface, HDC hdc,
				 LPDIRECTDRAWSURFACE7 *lpDDS)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    FIXME("(%p)->(%08ld,%p)\n", This, (DWORD) hdc, lpDDS);

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_GetVerticalBlankStatus(LPDIRECTDRAW7 iface, LPBOOL status)
{
    /* Just constantly flip from one state to the other to convince
     * apps that rely on this that we are ready for another frame.
     * Ideally, we would be able to get VBlank info from X, but that's
     * not available yet (is it?) */
    static BOOL laststatus = FALSE;
    ICOM_THIS(IDirectDrawImpl,iface);
    TRACE("(%p)->(%p)\n",This,status);
    *status = laststatus;
    laststatus = !laststatus;
    return DD_OK;
}

/* If we were not initialised then Uninit_Main_IDirectDraw7_Initialize would
 * have been called instead. */
HRESULT WINAPI
Main_DirectDraw_Initialize(LPDIRECTDRAW7 iface, LPGUID lpGuid)
{
    TRACE("(%p)->(%s)\n", iface, debugstr_guid(lpGuid));

    return DDERR_ALREADYINITIALIZED;
}

HRESULT WINAPI
Main_DirectDraw_RestoreAllSurfaces(LPDIRECTDRAW7 iface)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    IDirectDrawSurfaceImpl* surf;

    TRACE("(%p)->()\n", This);

    for (surf = This->surfaces; surf != NULL; surf = surf->next_ddraw)
	IDirectDrawSurface7_Restore(ICOM_INTERFACE(surf, IDirectDrawSurface7));

    return DD_OK;
}

static LRESULT WINAPI
DDRAW_WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    IDirectDrawImpl* This = (IDirectDrawImpl*)GetPropA(hWnd, ddProp);
    LPDIRECTDRAW7 iface = ICOM_INTERFACE(This, IDirectDraw7);
    TRACE("(%p)->(%08x,%x,%x,%lx)\n", iface, hWnd, iMsg, wParam, lParam);

    switch (iMsg)
    {
    case WM_SYSKEYDOWN:
    case WM_KEYDOWN:
    case WM_SYSKEYUP:
    case WM_KEYUP:
    case WM_SYSCHAR:
    case WM_CHAR:
	if (This->local.dwLocalFlags & DDRAWILCL_ISFULLSCREEN)
	{
            BOOL bMinimize = FALSE;

	    if ((lParam & (1 << 29)) && wParam)
	    {
		/* ALT */
		if (wParam == VK_TAB || wParam == VK_ESCAPE)
		    bMinimize = TRUE;
	    }
	    else
	    {
		/* CTRL */
		if (wParam == VK_ESCAPE && (GetKeyState(VK_CONTROL) < 0))
		    bMinimize = TRUE;
	    }

            if( bMinimize )
            {
                if (iMsg == WM_KEYDOWN || iMsg == WM_SYSKEYDOWN) {
                    TRACE( "Minimizing window from key combination %d 0x%lx\n", wParam, lParam );
                    ShowWindow(hWnd, SW_MINIMIZE);
                }
                TRACE( "Not passing on key combination %d 0x%lx\n", wParam, lParam );
                return 0;
            }
	}
	break;
    case WM_SHOWWINDOW:
        TRACE( "SHOWWINDOW lParam=%ld wParam=%d\n", lParam, wParam );
        if( wParam )
        {
	    IDirectDraw7_SetDisplayMode(iface, This->width, This->height,
					This->orig_pixelformat.u1.dwRGBBitCount,
					0, 0);
#if 1 /* FIXME */
            /* Presently we cannot rely on our windowing code to correctly send
             * either an WM_ACTIVATE or WM_ACTIVATEAPP message when maximizing. So
             * we don't beat around the bush - we just do it.
             */
            WARN( "Sending activation messages to %d\n", hWnd );
            SendMessageA( hWnd, WM_ACTIVATE, WA_ACTIVE, 0 );
            SendMessageA( hWnd, WM_ACTIVATEAPP, TRUE, 0 /* Not correct */ );
#endif
        }
        break;
    case WM_SIZE:
        if( wParam == SIZE_MINIMIZED )
        {
           /* Since the main ddraw window is minimizing, we should revert rid of 
            * the display mode we have set.
            * We also strictly check that we're minimizing to 0x0 since we have bugs
            * in our code whereby we sometimes send minimize messages when we mean to
            * maximize or restore (FIXME):( 
            */
           if( LOWORD(lParam) == HIWORD(lParam) && HIWORD(lParam) == 0 )
           {

#if 1 /* FIXME */
               /* Presently we cannot rely on our windowing code to correctly send
                * either an WM_ACTIVATE or WM_ACTIVATEAPP message when maximizing. So
                * we don't beat around the bush - we just do it.
                */
               if( This->local.hWnd )
               {
                   WARN( "Sending deactivate messages to %d\n", This->local.hWnd );
                   SendMessageA( This->local.hWnd, WM_ACTIVATE, WA_INACTIVE, This->local.hWnd );
                   SendMessageA( This->local.hWnd, WM_ACTIVATEAPP, FALSE, 0 /* Wrong */ );
               }
#endif

               IDirectDraw7_RestoreDisplayMode(iface);
           }
        }
        break;
    }


    return CallWindowProcA(This->original_wndproc, hWnd, iMsg, wParam, lParam);
}

static void DDRAW_RedecorateSubclassWindow(IDirectDrawImpl* This)
{
    DWORD style = This->old_style;

    TRACE( "Present Style: 0x%08lx Saved Style: 0x%08lx Flags: 0x%08lx\n",
           GetWindowLongA(This->local.hWnd, GWL_STYLE), style, This->local.dwLocalFlags );

    /* Remove or restore device window decorations */
    if (This->local.dwLocalFlags & DDRAWILCL_ISFULLSCREEN) {
	style &= ~WS_OVERLAPPEDWINDOW;
	style |= WS_POPUP;
    }

    /* Set the style if it's changed. */
    if( style != This->old_style )
       SetWindowLongA(This->local.hWnd, GWL_STYLE, style);  
}


static void DDRAW_SubclassWindow(IDirectDrawImpl* This)
{
    /* Well we don't actually subclass the window yet. */
    SetPropA(This->local.hWnd, ddProp, (LONG)This);

    /* Um, this shouldn't happen. */
    if ((WNDPROC)GetWindowLongA(This->local.hWnd, GWL_WNDPROC) != DDRAW_WndProc)
	This->original_wndproc = (WNDPROC)SetWindowLongA(This->local.hWnd,
							 GWL_WNDPROC,
							 (LONG)DDRAW_WndProc);

    /* Save original window decorations */
    This->old_style = GetWindowLongA(This->local.hWnd, GWL_STYLE);
    DDRAW_RedecorateSubclassWindow(This);
}

static void DDRAW_UnsubclassWindow(IDirectDrawImpl* This)
{
    if (This->local.dwLocalFlags & DDRAWILCL_ISFULLSCREEN) {
	SetWindowLongA(This->local.hWnd, GWL_STYLE, This->old_style);
    }
    SetWindowLongA(This->local.hWnd, GWL_WNDPROC, (LONG)This->original_wndproc);
    RemovePropA(This->local.hWnd, ddProp);
}

HRESULT WINAPI
Main_DirectDraw_SetCooperativeLevel(LPDIRECTDRAW7 iface, HWND hwnd,
				    DWORD cooplevel)
{
    ICOM_THIS(IDirectDrawImpl,iface);

    FIXME("(%p)->(%08lx,%08lx)\n",This,(DWORD)hwnd,cooplevel);
    DDRAW_dump_cooperativelevel(cooplevel);

    if (!(cooplevel & (DDSCL_EXCLUSIVE|DDSCL_NORMAL)))
	return DDERR_INVALIDPARAMS;

#if 0 /* "It cannot be reset while the process has surfaces or palettes created" */
    if (This->local.hWnd && This->local.hWnd != hwnd) {
	/* FIXME: check that condition */
	return DDERR_HWNDALREADYSET;
    }
#endif

    if (cooplevel & DDSCL_CREATEDEVICEWINDOW) {
	FIXME("DDSCL_CREATEDEVICEWINDOW unsupported\n");
    }

    if (cooplevel & DDSCL_SETFOCUSWINDOW) {
	FIXME("DDSCL_SETFOCUSWINDOW unsupported\n");
    }

    if (!(cooplevel & DDSCL_EXCLUSIVE)) {
	/* normal mode (window is not required) */
	if (cooplevel & DDSCL_FULLSCREEN) return DDERR_INVALIDPARAMS;

	if (This->local.dwLocalFlags & DDRAWILCL_HASEXCLUSIVEMODE) {
	    /* returning from exclusive mode */
	    IDirectDraw7_RestoreDisplayMode(iface);
	    if (This->local.lpGbl && This->local.lpGbl->lpExclusiveOwner == &This->local)
		This->local.lpGbl->lpExclusiveOwner = NULL;
	    if (This->set_exclusive_mode)
		This->set_exclusive_mode(This, FALSE);
	}
	if (This->local.hWnd != hwnd) DDRAW_UnsubclassWindow(This);
	This->local.hWnd = hwnd;
    } else {
	/* exclusive mode (window is required) */
	if (!hwnd) return DDERR_INVALIDPARAMS;
	if (!(cooplevel & DDSCL_FULLSCREEN)) return DDERR_INVALIDPARAMS;

	if (This->local.dwLocalFlags & DDRAWILCL_HASEXCLUSIVEMODE) {
	    ShowWindow(hwnd, SW_SHOW);
	    /* return DDERR_EXCLUSIVEMODEALREADYSET; */ /* Diablo II doesn't like */
	    return DD_OK;
	}

	/* assume that this app is the active app (in wine, there's
	 * probably only one app per global ddraw object anyway) */
	if (This->local.lpGbl)
	    This->local.lpGbl->lpExclusiveOwner = &This->local;
	if (This->local.hWnd != hwnd) DDRAW_UnsubclassWindow(This);
	This->local.hWnd = hwnd;
	if (This->set_exclusive_mode)
	    This->set_exclusive_mode(This, TRUE);
    }

    This->local.dwLocalFlags &= ~(DDRAWILCL_HASEXCLUSIVEMODE | DDRAWILCL_ISFULLSCREEN |
				  DDRAWILCL_ALLOWMODEX | DDRAWILCL_MULTITHREADED |
				  DDRAWILCL_FPUSETUP | DDRAWILCL_FPUPRESERVE);
    This->local.dwLocalFlags |= DDRAWILCL_SETCOOPCALLED;
    /* not entirely sure about these */
    if (cooplevel & DDSCL_EXCLUSIVE)     This->local.dwLocalFlags |= DDRAWILCL_HASEXCLUSIVEMODE;
    if (cooplevel & DDSCL_FULLSCREEN)    This->local.dwLocalFlags |= DDRAWILCL_ISFULLSCREEN;
    if (cooplevel & DDSCL_ALLOWMODEX)    This->local.dwLocalFlags |= DDRAWILCL_ALLOWMODEX;
    if (cooplevel & DDSCL_MULTITHREADED) This->local.dwLocalFlags |= DDRAWILCL_MULTITHREADED;
    if (cooplevel & DDSCL_FPUSETUP)      This->local.dwLocalFlags |= DDRAWILCL_FPUSETUP;
    if (cooplevel & DDSCL_FPUPRESERVE)   This->local.dwLocalFlags |= DDRAWILCL_FPUPRESERVE;

    /* FIXME: there's supposed to be some WM_ACTIVATEAPP business somewhere */
    /* and I think the window is supposed to be maximized in SetDisplayMode or something,
     * but then how about RestoreDisplayMode and final_release? */
    ShowWindow(hwnd, SW_SHOW);

    if (!GetPropA(This->local.hWnd, ddProp))
	DDRAW_SubclassWindow(This);
    else {
        /* This window has already been subclassed, just adjust the decorations if required */
        DDRAW_RedecorateSubclassWindow(This);
    }

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_SetDisplayMode(LPDIRECTDRAW7 iface, DWORD dwWidth,
			       DWORD dwHeight, LONG lPitch,
			       DWORD dwRefreshRate, DWORD dwFlags,
			       const DDPIXELFORMAT* pixelformat)
{
    ICOM_THIS(IDirectDrawImpl,iface);

    TRACE("(%p)->SetDisplayMode(%ld,%ld,%ld,%ld Hz,%08lx,%p)\n",This,dwWidth,dwHeight,
								lPitch,dwRefreshRate,
								dwFlags,pixelformat);
    TRACE(" bpp=%ld\n", pixelformat->u1.dwRGBBitCount);

    if (!(This->local.dwLocalFlags & DDRAWILCL_HASEXCLUSIVEMODE))
	return DDERR_NOEXCLUSIVEMODE;

    if (!IsWindow(This->local.hWnd))
	return DDERR_GENERIC; /* unchecked */

    LosePrimarySurface(This);

    This->width = dwWidth;
    This->height = dwHeight;
    This->pitch = lPitch;
    This->pixelformat = *pixelformat;

    /* Position the window in the center of the screen - don't center for now */
    /* MoveWindow(This->local.hWnd, (orig_width-dwWidth)/2, (orig_height-dwHeight)/2,
                  dwWidth, dwHeight, TRUE);*/
    MoveWindow(This->local.hWnd, 0, 0, dwWidth, dwHeight, TRUE);

    SetFocus(This->local.hWnd);

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_RestoreDisplayMode(LPDIRECTDRAW7 iface)
{
    ICOM_THIS(IDirectDrawImpl,iface);

    TRACE("(%p)\n",This);
    if (!(This->local.dwLocalFlags & DDRAWILCL_HASEXCLUSIVEMODE))
	return DDERR_NOEXCLUSIVEMODE;

    /* Lose the primary surface if the resolution changes. */
    if (This->orig_width != This->width || This->orig_height != This->height
	|| This->orig_pitch != This->pitch
	|| This->orig_pixelformat.dwFlags != This->pixelformat.dwFlags
	|| !Main_DirectDraw_DDPIXELFORMAT_Match(&This->pixelformat,
						&This->orig_pixelformat))
    {
	LosePrimarySurface(This);
    }

    /* TODO Move the window back where it belongs. */

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_WaitForVerticalBlank(LPDIRECTDRAW7 iface, DWORD dwFlags,
				     HANDLE h)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    FIXME("(%p)->(flags=0x%08lx,handle=0x%08x)\n",This,dwFlags,h);
    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_GetDisplayMode(LPDIRECTDRAW7 iface, LPDDSURFACEDESC2 pDDSD)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    TRACE("(%p)->GetDisplayMode(%p)\n",This,pDDSD);

    pDDSD->dwFlags = DDSD_HEIGHT|DDSD_WIDTH|DDSD_PITCH|DDSD_PIXELFORMAT|DDSD_REFRESHRATE;
    pDDSD->dwHeight = This->height;
    pDDSD->dwWidth = This->width;
    pDDSD->u1.lPitch = This->pitch;
    pDDSD->u2.dwRefreshRate = 60;
    pDDSD->u4.ddpfPixelFormat = This->pixelformat;
    pDDSD->ddsCaps.dwCaps = 0;

    return DD_OK;
}

void DirectDraw_Allocate_Memory(IDirectDrawImpl *This, DWORD size)
{
    if (size > This->caps.dwVidMemFree)
    {
        ERR("uhho, we have run out of (what we consider) to be video memory. but chances are we haven't.. (FIXME: static initial memory size needs changing\n");
    }
    This->caps.dwVidMemFree -= size;
}

void DirectDraw_Deallocate_Memory(IDirectDrawImpl *This, DWORD size)
{
    This->caps.dwVidMemFree += size;
}

HRESULT WINAPI
Main_DirectDraw_GetAvailableVidMem(LPDIRECTDRAW7 iface, LPDDSCAPS2 ddscaps,
				   LPDWORD total, LPDWORD free)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    TRACE("(%p)->(%p,%p,%p)\n", This,ddscaps,total,free);
    TRACE("total=%ld, free=%ld\n", This->caps.dwVidMemTotal, This->caps.dwVidMemFree);

    if (total)	*total= This->caps.dwVidMemTotal;
    if (free)	*free = This->caps.dwVidMemFree;
    return DD_OK;
}

HRESULT WINAPI Main_DirectDraw_TestCooperativeLevel(LPDIRECTDRAW7 iface) {
    ICOM_THIS(IDirectDrawImpl,iface);
    DWORD style;

    TRACE( "(%p)->(): 0x%08lx\n", This, This->local.dwLocalFlags );

    if (This->primary_surface && !This->primary_surface->lost)
        return DD_OK;

    /* Fullscreen:
     *  - return DDERR_NOEXCLUSIVEMODE if user switched away from us
     *  - return DD_OK when user has switched back to us
     * Windowed:
     *  - return DDERR_EXCLUSIVEMODEALREADYSET if someone else is fullscreen
     *  - return DDERR_WRONGMODE if the user switched display mode
     *    (losing all our surfaces)
     *  - return DD_OK otherwise */

    if (This->local.dwLocalFlags & DDRAWILCL_ISFULLSCREEN) {
	style = GetWindowLongA(This->local.hWnd, GWL_STYLE);
	if (style & WS_MINIMIZE) return DDERR_NOEXCLUSIVEMODE;
    }

    return DD_OK;
}

HRESULT WINAPI
Main_DirectDraw_StartModeTest(LPDIRECTDRAW7 iface, LPSIZE pModes,
			      DWORD dwNumModes, DWORD dwFlags)
{
    ICOM_THIS(IDirectDrawImpl,iface);
    FIXME("(%p)->() stub\n", This);

    return DD_OK;
}

/*** Owned object management. */

void Main_DirectDraw_AddSurface(IDirectDrawImpl* This,
				IDirectDrawSurfaceImpl* surface)
{
    assert(surface->ddraw_owner == NULL || surface->ddraw_owner == This);

    surface->ddraw_owner = This;

    /* where should it go? */
    surface->next_ddraw = This->surfaces;
    surface->prev_ddraw = NULL;
    if (This->surfaces)
	This->surfaces->prev_ddraw = surface;
    This->surfaces = surface;
}

void Main_DirectDraw_RemoveSurface(IDirectDrawImpl* This,
				   IDirectDrawSurfaceImpl* surface)
{
    assert(surface->ddraw_owner == This);

    if (This->surfaces == surface)
	This->surfaces = surface->next_ddraw;

    if (This->primary_surface == surface)
	This->primary_surface = NULL;

    if (surface->next_ddraw)
	surface->next_ddraw->prev_ddraw = surface->prev_ddraw;
    if (surface->prev_ddraw)
	surface->prev_ddraw->next_ddraw = surface->next_ddraw;
}

static void Main_DirectDraw_DeleteSurfaces(IDirectDrawImpl* This)
{
    if (This->local.dwLocalFlags & DDRAWILCL_DIRECTDRAW7)
    {
	TRACE("ddraw shutting down, deleting surfaces\n");
	while (This->surfaces != NULL)
	    Main_DirectDrawSurface_ForceDestroy(This->surfaces);
    }
}

void Main_DirectDraw_AddClipper(IDirectDrawImpl* This,
				IDirectDrawClipperImpl* clipper)
{
    assert(clipper->ddraw_owner == NULL || clipper->ddraw_owner == This);

    clipper->ddraw_owner = This;

    clipper->next_ddraw = This->clippers;
    clipper->prev_ddraw = NULL;
    if (This->clippers)
	This->clippers->prev_ddraw = clipper;
    This->clippers = clipper;
}

void Main_DirectDraw_RemoveClipper(IDirectDrawImpl* This,
				   IDirectDrawClipperImpl* clipper)
{
    assert(clipper->ddraw_owner == This);

    if (This->clippers == clipper)
	This->clippers = clipper->next_ddraw;

    if (clipper->next_ddraw)
	clipper->next_ddraw->prev_ddraw = clipper->prev_ddraw;
    if (clipper->prev_ddraw)
	clipper->prev_ddraw->next_ddraw = clipper->next_ddraw;
}

static void Main_DirectDraw_DeleteClippers(IDirectDrawImpl* This)
{
    while (This->clippers != NULL)
	Main_DirectDrawClipper_ForceDestroy(This->clippers);
}

void Main_DirectDraw_AddPalette(IDirectDrawImpl* This,
				IDirectDrawPaletteImpl* palette)
{
    assert(palette->ddraw_owner == NULL || palette->ddraw_owner == This);

    palette->ddraw_owner = This;

    /* where should it go? */
    palette->next_ddraw = This->palettes;
    palette->prev_ddraw = NULL;
    if (This->palettes)
	This->palettes->prev_ddraw = palette;
    This->palettes = palette;
}

void Main_DirectDraw_RemovePalette(IDirectDrawImpl* This,
				   IDirectDrawPaletteImpl* palette)
{
    assert(palette->ddraw_owner == This);

    if (This->palettes == palette)
	This->palettes = palette->next_ddraw;

    if (palette->next_ddraw)
	palette->next_ddraw->prev_ddraw = palette->prev_ddraw;
    if (palette->prev_ddraw)
	palette->prev_ddraw->next_ddraw = palette->next_ddraw;
}

static void Main_DirectDraw_DeletePalettes(IDirectDrawImpl* This)
{
    while (This->palettes != NULL)
	Main_DirectDrawPalette_ForceDestroy(This->palettes);
}

void Main_DirectDraw_Add3DDevice(IDirectDrawImpl* This,
				 IDirect3DDeviceImpl* device)
{
    assert(device->ddraw_owner == NULL || device->ddraw_owner == This);

    device->ddraw_owner = This;

    /* where should it go? */
    device->next_ddraw = This->devices;
    device->prev_ddraw = NULL;
    if (This->devices)
	This->devices->prev_ddraw = device;
    This->devices = device;
}

void Main_DirectDraw_Remove3DDevice(IDirectDrawImpl* This,
				    IDirect3DDeviceImpl* device)
{
    assert(device->ddraw_owner == This);

    if (This->devices == device)
	This->devices = device->next_ddraw;

    if (device->next_ddraw)
	device->next_ddraw->prev_ddraw = device->prev_ddraw;
    if (device->prev_ddraw)
	device->prev_ddraw->next_ddraw = device->next_ddraw;
}

static void Main_DirectDraw_Delete3DDevices(IDirectDrawImpl* This)
{
    while (This->devices != NULL)
	Main_Direct3DDevice_ForceDestroy(This->devices);
}

/*** ??? */

static void
LoseSurface(IDirectDrawSurfaceImpl *surface)
{
    if (surface != NULL) surface->lose_surface(surface);
}

static void
LosePrimarySurface(IDirectDrawImpl *This)
{
    /* MSDN: "If another application changes the display mode, the primary
     * surface is lost, and the method returns DDERR_SURFACELOST until the
     * primary surface is recreated to match the new display mode."
     *
     * We mark all the primary surfaces as lost as soon as the display
     * mode is changed (by any application). */

    LoseSurface(This->primary_surface);
}

/******************************************************************************
 * Uninitialised DirectDraw functions
 *
 * This vtable is used when a DirectDraw object is created with
 * CoCreateInstance. The only usable method is Initialize.
 */

void Uninit_DirectDraw_final_release(IDirectDrawImpl *This)
{
    Main_DirectDraw_final_release(This);
}

static ICOM_VTABLE(IDirectDraw7) Uninit_DirectDraw_VTable;

/* Not called from the vtable. */
HRESULT Uninit_DirectDraw_Construct(IDirectDrawImpl *This, BOOL ex)
{
    HRESULT hr;

    hr = Main_DirectDraw_Construct(This, ex);
    if (FAILED(hr)) return hr;

    This->final_release = Uninit_DirectDraw_final_release;
    ICOM_INIT_INTERFACE(This, IDirectDraw7, Uninit_DirectDraw_VTable);

    return S_OK;
}

HRESULT Uninit_DirectDraw_Create(const GUID* pGUID,
				       LPDIRECTDRAW7* pIface,
				       IUnknown* pUnkOuter, BOOL ex)
{
    HRESULT hr;
    IDirectDrawImpl* This;

    assert(pUnkOuter == NULL); /* XXX no: we must check this */

    This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
		     sizeof(IDirectDrawImpl));
    if (This == NULL) return E_OUTOFMEMORY;

    hr = Uninit_DirectDraw_Construct(This, ex);
    if (FAILED(hr))
	HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, This);
    else
	*pIface = ICOM_INTERFACE(This, IDirectDraw7);

    return hr;
}

static HRESULT WINAPI
Uninit_DirectDraw_Initialize(LPDIRECTDRAW7 iface, LPGUID pDeviceGuid)
{
    const ddraw_driver* driver;
    ICOM_THIS(IDirectDrawImpl, iface);

    TRACE("(%p)->(%p)\n", iface, pDeviceGuid);

    driver = DDRAW_FindDriver(pDeviceGuid);
    /* XXX This return value is not documented. (Not checked.) */
    if (driver == NULL) return DDERR_INVALIDDIRECTDRAWGUID;

    return driver->init(This, pDeviceGuid);
}

static HRESULT WINAPI
Uninit_DirectDraw_Compact(LPDIRECTDRAW7 iface)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_CreateClipper(LPDIRECTDRAW7 iface, DWORD dwFlags,
				LPDIRECTDRAWCLIPPER *lplpDDClipper,
				IUnknown *pUnkOuter)

{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_CreatePalette(LPDIRECTDRAW7 iface, DWORD dwFlags,
				LPPALETTEENTRY lpColorTable,
				LPDIRECTDRAWPALETTE *lplpDDPalette,
				IUnknown *pUnkOuter)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_CreateSurface(LPDIRECTDRAW7 iface,
				LPDDSURFACEDESC2 lpDDSurfaceDesc,
				LPDIRECTDRAWSURFACE7 *lplpDDSurface,
				IUnknown *pUnkOuter)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_DuplicateSurface(LPDIRECTDRAW7 iface,
				   LPDIRECTDRAWSURFACE7 pSurf,
				   LPDIRECTDRAWSURFACE7 *pDupSurf)

{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_EnumDisplayModes(LPDIRECTDRAW7 iface, DWORD dwFlags,
				   LPDDSURFACEDESC2 lpDDSD,
				   LPVOID context,
				   LPDDENUMMODESCALLBACK2 cb)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_EnumSurfaces(LPDIRECTDRAW7 iface, DWORD dwFlags,
			       LPDDSURFACEDESC2 pDDSD, LPVOID context,
			       LPDDENUMSURFACESCALLBACK7 cb)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_FlipToGDISurface(LPDIRECTDRAW7 iface)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_GetCaps(LPDIRECTDRAW7 iface, LPDDCAPS pDriverCaps,
			  LPDDCAPS pHELCaps)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_GetDisplayMode(LPDIRECTDRAW7 iface,
				 LPDDSURFACEDESC2 pDDSD)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_GetFourCCCodes(LPDIRECTDRAW7 iface, LPDWORD pNumCodes,
				 LPDWORD pCodes)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_GetGDISurface(LPDIRECTDRAW7 iface,
				LPDIRECTDRAWSURFACE7 *pGDISurf)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_GetMonitorFrequency(LPDIRECTDRAW7 iface, LPDWORD pdwFreq)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_GetScanLine(LPDIRECTDRAW7 iface, LPDWORD pdwScanLine)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_GetVerticalBlankStatus(LPDIRECTDRAW7 iface, PBOOL pbIsInVB)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_RestoreDisplayMode(LPDIRECTDRAW7 iface)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_SetCooperativeLevel(LPDIRECTDRAW7 iface, HWND hWnd,
				      DWORD dwFlags)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_SetDisplayMode(LPDIRECTDRAW7 iface, DWORD dwWidth,
				 DWORD dwHeight, DWORD dwBPP,
				 DWORD dwRefreshRate, DWORD dwFlags)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_WaitForVerticalBlank(LPDIRECTDRAW7 iface, DWORD dwFlags,
				       HANDLE hEvent)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_GetAvailableVidMem(LPDIRECTDRAW7 iface, LPDDSCAPS2 pDDCaps,
				     LPDWORD pdwTotal, LPDWORD pdwFree)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_GetSurfaceFromDC(LPDIRECTDRAW7 iface, HDC hDC,
				   LPDIRECTDRAWSURFACE7 *pSurf)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_RestoreAllSurfaces(LPDIRECTDRAW7 iface)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_TestCooperativeLevel(LPDIRECTDRAW7 iface)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_GetDeviceIdentifier(LPDIRECTDRAW7 iface,
				      LPDDDEVICEIDENTIFIER2 pDDDI,
				      DWORD dwFlags)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_StartModeTest(LPDIRECTDRAW7 iface, LPSIZE pszModes,
				DWORD cModes, DWORD dwFlags)
{
    return DDERR_NOTINITIALIZED;
}

static HRESULT WINAPI
Uninit_DirectDraw_EvaluateMode(LPDIRECTDRAW7 iface, DWORD dwFlags,
			       LPDWORD pTimeout)
{
    return DDERR_NOTINITIALIZED;
}

static ICOM_VTABLE(IDirectDraw7) Uninit_DirectDraw_VTable =
{
    ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
    Main_DirectDraw_QueryInterface,
    Main_DirectDraw_AddRef,
    Main_DirectDraw_Release,
    Uninit_DirectDraw_Compact,
    Uninit_DirectDraw_CreateClipper,
    Uninit_DirectDraw_CreatePalette,
    Uninit_DirectDraw_CreateSurface,
    Uninit_DirectDraw_DuplicateSurface,
    Uninit_DirectDraw_EnumDisplayModes,
    Uninit_DirectDraw_EnumSurfaces,
    Uninit_DirectDraw_FlipToGDISurface,
    Uninit_DirectDraw_GetCaps,
    Uninit_DirectDraw_GetDisplayMode,
    Uninit_DirectDraw_GetFourCCCodes,
    Uninit_DirectDraw_GetGDISurface,
    Uninit_DirectDraw_GetMonitorFrequency,
    Uninit_DirectDraw_GetScanLine,
    Uninit_DirectDraw_GetVerticalBlankStatus,
    Uninit_DirectDraw_Initialize,
    Uninit_DirectDraw_RestoreDisplayMode,
    Uninit_DirectDraw_SetCooperativeLevel,
    Uninit_DirectDraw_SetDisplayMode,
    Uninit_DirectDraw_WaitForVerticalBlank,
    Uninit_DirectDraw_GetAvailableVidMem,
    Uninit_DirectDraw_GetSurfaceFromDC,
    Uninit_DirectDraw_RestoreAllSurfaces,
    Uninit_DirectDraw_TestCooperativeLevel,
    Uninit_DirectDraw_GetDeviceIdentifier,
    Uninit_DirectDraw_StartModeTest,
    Uninit_DirectDraw_EvaluateMode
};
