text: /*
   * Copyright 2008 Stefan Dösinger for CodeWeavers
   *
   * 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
   */
  
  #define COBJMACROS
  #define NONAMELESSUNION
  
  #include <stdarg.h>
  #include "windef.h"
  #include "winbase.h"
  #include "wingdi.h"
  
  #include "ddraw.h"
  #include "d3d.h"
  
  #include "ddrawex_private.h"
  #include "wine/debug.h"
  
  WINE_DEFAULT_DEBUG_CHANNEL(ddrawex);
  
  /******************************************************************************
   * Helper functions for COM management
   ******************************************************************************/
  static IDirectDrawImpl *impl_from_dd1(IDirectDraw *iface)
  {
      return (IDirectDrawImpl *)((char*)iface - FIELD_OFFSET(IDirectDrawImpl, IDirectDraw_Vtbl));
  }
  static IDirectDraw *dd1_from_impl(IDirectDrawImpl *This)
  {
      return (IDirectDraw *) &This->IDirectDraw_Vtbl;
  }
  
  static IDirectDrawImpl *impl_from_dd2(IDirectDraw2 *iface)
  {
      return (IDirectDrawImpl *)((char*)iface - FIELD_OFFSET(IDirectDrawImpl, IDirectDraw2_Vtbl));
  }
  static IDirectDraw2 *dd2_from_impl(IDirectDrawImpl *This)
  {
      return (IDirectDraw2 *) &This->IDirectDraw2_Vtbl;
  }
  
  static IDirectDrawImpl *impl_from_dd3(IDirectDraw3 *iface)
  {
      return (IDirectDrawImpl *)((char*)iface - FIELD_OFFSET(IDirectDrawImpl, IDirectDraw3_Vtbl));
  }
  static IDirectDraw3 *dd3_from_impl(IDirectDrawImpl *This)
  {
      return (IDirectDraw3 *) &This->IDirectDraw3_Vtbl;
  }
  
  static IDirectDrawImpl *impl_from_dd4(IDirectDraw4 *iface)
  {
      return (IDirectDrawImpl *)((char*)iface - FIELD_OFFSET(IDirectDrawImpl, IDirectDraw4_Vtbl));
  }
  static IDirectDraw4 *dd4_from_impl(IDirectDrawImpl *This)
  {
      return (IDirectDraw4 *) &This->IDirectDraw4_Vtbl;
  }
  
  /******************************************************************************
   * IDirectDraw4 -> ddraw.dll wrappers
   ******************************************************************************/
  static HRESULT WINAPI
  IDirectDraw4Impl_QueryInterface(IDirectDraw4 *iface,
                                  REFIID refiid,
                                  void **obj)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
  
      TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(refiid), obj);
      *obj = NULL;
  
      if(!refiid)
      {
          return DDERR_INVALIDPARAMS;
      }
  
      if (IsEqualGUID( &IID_IDirectDraw7, refiid ) )
      {
          WARN("IDirectDraw7 not allowed in ddrawex.dll\n");
          return E_NOINTERFACE;
      }
      else if ( IsEqualGUID( &IID_IUnknown, refiid ) ||
                IsEqualGUID( &IID_IDirectDraw4, refiid ) )
      {
          *obj = dd4_from_impl(This);
          TRACE("(%p) Returning IDirectDraw4 interface at %p\n", This, *obj);
          IDirectDraw4_AddRef((IDirectDraw4 *) *obj);
      }
      else if ( IsEqualGUID( &IID_IDirectDraw3, refiid ) )
      {
          *obj = dd3_from_impl(This);
          TRACE("(%p) Returning IDirectDraw3 interface at %p\n", This, *obj);
          IDirectDraw3_AddRef((IDirectDraw3 *) *obj);
      }
      else if ( IsEqualGUID( &IID_IDirectDraw2, refiid ) )
      {
          *obj = dd2_from_impl(This);
          TRACE("(%p) Returning IDirectDraw2 interface at %p\n", This, *obj);
          IDirectDraw2_AddRef((IDirectDraw2 *) *obj);
      }
      else if ( IsEqualGUID( &IID_IDirectDraw, refiid ) )
      {
          *obj = dd1_from_impl(This);
          TRACE("(%p) Returning IDirectDraw interface at %p\n", This, *obj);
          IDirectDraw_AddRef((IDirectDraw *) *obj);
      }
      else if ( IsEqualGUID( &IID_IDirect3D  , refiid ) ||
                IsEqualGUID( &IID_IDirect3D2 , refiid ) ||
                IsEqualGUID( &IID_IDirect3D3 , refiid ) ||
                IsEqualGUID( &IID_IDirect3D7 , refiid ) )
      {
          WARN("Direct3D not allowed in ddrawex.dll\n");
          return E_NOINTERFACE;
      }
      /* Unknown interface */
      else
      {
          ERR("(%p)->(%s, %p): No interface found\n", This, debugstr_guid(refiid), obj);
          return E_NOINTERFACE;
      }
      TRACE("Returning S_OK\n");
      return S_OK;
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_QueryInterface(IDirectDraw3 *iface,
                                  REFIID refiid,
                                  void **obj)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_QueryInterface(dd4_from_impl(This), refiid, obj);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_QueryInterface(IDirectDraw2 *iface,
                                  REFIID refiid,
                                  void **obj)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_QueryInterface(dd4_from_impl(This), refiid, obj);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_QueryInterface(IDirectDraw *iface,
                                 REFIID refiid,
                                 void **obj)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_QueryInterface(dd4_from_impl(This), refiid, obj);
  }
  
  static ULONG WINAPI
  IDirectDraw4Impl_AddRef(IDirectDraw4 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      ULONG ref = InterlockedIncrement(&This->ref);
  
      TRACE("(%p) : incrementing refcount from %u.\n", This, ref - 1);
  
      return ref;
  }
  
  static ULONG WINAPI
  IDirectDraw3Impl_AddRef(IDirectDraw3 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_AddRef(dd4_from_impl(This));
  }
  
  static ULONG WINAPI
  IDirectDraw2Impl_AddRef(IDirectDraw2 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_AddRef(dd4_from_impl(This));
  }
  
  static ULONG WINAPI
  IDirectDrawImpl_AddRef(IDirectDraw *iface)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_AddRef(dd4_from_impl(This));
  }
  
  static ULONG WINAPI
  IDirectDraw4Impl_Release(IDirectDraw4 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      ULONG ref = InterlockedDecrement(&This->ref);
  
      TRACE("(%p) : decrementing refcount to %u.\n", This, ref);
  
      if(ref == 0)
      {
          TRACE("Destroying object\n");
          IDirectDraw4_Release(This->parent);
          HeapFree(GetProcessHeap(), 0, This);
      }
      return ref;
  }
  
  static ULONG WINAPI
  IDirectDraw3Impl_Release(IDirectDraw3 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_Release(dd4_from_impl(This));
  }
  
  static ULONG WINAPI
  IDirectDraw2Impl_Release(IDirectDraw2 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_Release(dd4_from_impl(This));
  }
  
  static ULONG WINAPI
  IDirectDrawImpl_Release(IDirectDraw *iface)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_Release(dd4_from_impl(This));
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_Compact(IDirectDraw4 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)\n", This);
  
      return IDirectDraw4_Compact(This->parent);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_Compact(IDirectDraw3 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_Compact(dd4_from_impl(This));
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_Compact(IDirectDraw2 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_Compact(dd4_from_impl(This));
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_Compact(IDirectDraw *iface)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_Compact(dd4_from_impl(This));
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_CreateClipper(IDirectDraw4 *iface,
                                 DWORD Flags,
                                 IDirectDrawClipper **clipper,
                                 IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(0x%08x, %p, %p)\n", This, Flags, clipper, UnkOuter);
  
      if(UnkOuter != NULL)
      {
          /* This may require a wrapper interface for clippers too which handles this */
          FIXME("Test and implement Aggregation for ddrawex clippers\n");
      }
  
      return IDirectDraw4_CreateClipper(This->parent, Flags, clipper, UnkOuter);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_CreateClipper(IDirectDraw3 *iface,
                                 DWORD Flags,
                                 IDirectDrawClipper **clipper,
                                 IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_CreateClipper(dd4_from_impl(This), Flags, clipper, UnkOuter);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_CreateClipper(IDirectDraw2 *iface,
                                 DWORD Flags,
                                 IDirectDrawClipper **clipper,
                                 IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_CreateClipper(dd4_from_impl(This), Flags, clipper, UnkOuter);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_CreateClipper(IDirectDraw *iface,
                                DWORD Flags,
                                IDirectDrawClipper **clipper,
                                IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_CreateClipper(dd4_from_impl(This), Flags, clipper, UnkOuter);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_CreatePalette(IDirectDraw4 *iface,
                                 DWORD Flags,
                                 PALETTEENTRY *ColorTable,
                                 IDirectDrawPalette **Palette,
                                 IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)(0x%08x,%p,%p,%p)\n", This, Flags, ColorTable, Palette, UnkOuter);
  
      if(UnkOuter != NULL)
      {
          /* This may require a wrapper interface for palettes too which handles this */
          FIXME("Test and implement Aggregation for ddrawex palettes\n");
      }
  
      return IDirectDraw4_CreatePalette(This->parent, Flags, ColorTable, Palette, UnkOuter);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_CreatePalette(IDirectDraw3 *iface,
                                 DWORD Flags,
                                 PALETTEENTRY *ColorTable,
                                 IDirectDrawPalette **Palette,
                                 IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_CreatePalette(dd4_from_impl(This), Flags, ColorTable, Palette, UnkOuter);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_CreatePalette(IDirectDraw2 *iface,
                                 DWORD Flags,
                                 PALETTEENTRY *ColorTable,
                                 IDirectDrawPalette **Palette,
                                 IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_CreatePalette(dd4_from_impl(This), Flags, ColorTable, Palette, UnkOuter);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_CreatePalette(IDirectDraw *iface,
                                DWORD Flags,
                                PALETTEENTRY *ColorTable,
                                IDirectDrawPalette **Palette,
                                IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("Thunking to IDirectDraw4\n");
      return IDirectDraw4_CreatePalette(dd4_from_impl(This), Flags, ColorTable, Palette, UnkOuter);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_CreateSurface(IDirectDraw4 *iface,
                                 DDSURFACEDESC2 *DDSD,
                                 IDirectDrawSurface4 **Surf,
                                 IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      HRESULT hr;
      const DWORD perm_dc_flags = DDSCAPS_VIDEOMEMORY | DDSCAPS_SYSTEMMEMORY;
      BOOL permanent_dc;
      TRACE("(%p)(%p, %p, %p)\n", This, DDSD, Surf, UnkOuter);
  
      if(UnkOuter != NULL)
      {
          /* Handle this in this dll. Don't forward the UnkOuter to ddraw.dll */
          FIXME("Implement aggregation for ddrawex surfaces\n");
      }
  
      /* plain ddraw.dll refuses to create a surface that has both VIDMEM and SYSMEM flags
       * set. In ddrawex this succeeds, and the GetDC() call changes the behavior. The DC
       * is permanently valid, and the surface can be locked between GetDC() and ReleaseDC()
       * calls. GetDC() can be called more than once too
       */
      if((DDSD->ddsCaps.dwCaps & perm_dc_flags) == perm_dc_flags)
      {
          permanent_dc = TRUE;
          DDSD->ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY;
          DDSD->ddsCaps.dwCaps |= DDSCAPS_OWNDC;
      }
      else
      {
          permanent_dc = FALSE;
      }
  
      hr = IDirectDraw4_CreateSurface(This->parent, DDSD, Surf, UnkOuter);
      *Surf = dds_get_outer(*Surf);
      if(permanent_dc) prepare_permanent_dc(*Surf);
      return hr;
  }
  
  void DDSD_to_DDSD2(const DDSURFACEDESC *in, DDSURFACEDESC2 *out)
  {
      memset(out, 0, sizeof(*out));
      out->dwSize = sizeof(*out);
      out->dwFlags = in->dwFlags;
      if(in->dwFlags & DDSD_WIDTH) out->dwWidth = in->dwWidth;
      if(in->dwFlags & DDSD_HEIGHT) out->dwHeight = in->dwHeight;
      if(in->dwFlags & DDSD_PIXELFORMAT) out->u4.ddpfPixelFormat = in->ddpfPixelFormat;
      if(in->dwFlags & DDSD_CAPS) out->ddsCaps.dwCaps = in->ddsCaps.dwCaps;
      if(in->dwFlags & DDSD_PITCH) out->u1.lPitch = in->u1.lPitch;
      if(in->dwFlags & DDSD_BACKBUFFERCOUNT) out->dwBackBufferCount = in->dwBackBufferCount;
      if(in->dwFlags & DDSD_ZBUFFERBITDEPTH) out->u2.dwMipMapCount = in->u2.dwZBufferBitDepth; /* same union */
      if(in->dwFlags & DDSD_ALPHABITDEPTH) out->dwAlphaBitDepth = in->dwAlphaBitDepth;
      /* DDraw(native, and wine) does not set the DDSD_LPSURFACE, so always copy */
      out->lpSurface = in->lpSurface;
      if(in->dwFlags & DDSD_CKDESTOVERLAY) out->u3.ddckCKDestOverlay = in->ddckCKDestOverlay;
      if(in->dwFlags & DDSD_CKDESTBLT) out->ddckCKDestBlt = in->ddckCKDestBlt;
      if(in->dwFlags & DDSD_CKSRCOVERLAY) out->ddckCKSrcOverlay = in->ddckCKSrcOverlay;
      if(in->dwFlags & DDSD_CKSRCBLT) out->ddckCKSrcBlt = in->ddckCKSrcBlt;
      if(in->dwFlags & DDSD_MIPMAPCOUNT) out->u2.dwMipMapCount = in->u2.dwMipMapCount;
      if(in->dwFlags & DDSD_REFRESHRATE) out->u2.dwRefreshRate = in->u2.dwRefreshRate;
      if(in->dwFlags & DDSD_LINEARSIZE) out->u1.dwLinearSize = in->u1.dwLinearSize;
      /* Does not exist in DDSURFACEDESC:
       * DDSD_TEXTURESTAGE, DDSD_FVF, DDSD_SRCVBHANDLE,
       */
  }
  
  void DDSD2_to_DDSD(const DDSURFACEDESC2 *in, DDSURFACEDESC *out)
  {
      memset(out, 0, sizeof(*out));
      out->dwSize = sizeof(*out);
      out->dwFlags = in->dwFlags;
      if(in->dwFlags & DDSD_WIDTH) out->dwWidth = in->dwWidth;
      if(in->dwFlags & DDSD_HEIGHT) out->dwHeight = in->dwHeight;
      if(in->dwFlags & DDSD_PIXELFORMAT) out->ddpfPixelFormat = in->u4.ddpfPixelFormat;
      if(in->dwFlags & DDSD_CAPS) out->ddsCaps.dwCaps = in->ddsCaps.dwCaps;
      if(in->dwFlags & DDSD_PITCH) out->u1.lPitch = in->u1.lPitch;
      if(in->dwFlags & DDSD_BACKBUFFERCOUNT) out->dwBackBufferCount = in->dwBackBufferCount;
      if(in->dwFlags & DDSD_ZBUFFERBITDEPTH) out->u2.dwZBufferBitDepth = in->u2.dwMipMapCount; /* same union */
      if(in->dwFlags & DDSD_ALPHABITDEPTH) out->dwAlphaBitDepth = in->dwAlphaBitDepth;
      /* DDraw(native, and wine) does not set the DDSD_LPSURFACE, so always copy */
      out->lpSurface = in->lpSurface;
      if(in->dwFlags & DDSD_CKDESTOVERLAY) out->ddckCKDestOverlay = in->u3.ddckCKDestOverlay;
      if(in->dwFlags & DDSD_CKDESTBLT) out->ddckCKDestBlt = in->ddckCKDestBlt;
      if(in->dwFlags & DDSD_CKSRCOVERLAY) out->ddckCKSrcOverlay = in->ddckCKSrcOverlay;
      if(in->dwFlags & DDSD_CKSRCBLT) out->ddckCKSrcBlt = in->ddckCKSrcBlt;
      if(in->dwFlags & DDSD_MIPMAPCOUNT) out->u2.dwMipMapCount = in->u2.dwMipMapCount;
      if(in->dwFlags & DDSD_REFRESHRATE) out->u2.dwRefreshRate = in->u2.dwRefreshRate;
      if(in->dwFlags & DDSD_LINEARSIZE) out->u1.dwLinearSize = in->u1.dwLinearSize;
      /* Does not exist in DDSURFACEDESC:
       * DDSD_TEXTURESTAGE, DDSD_FVF, DDSD_SRCVBHANDLE,
       */
      if(in->dwFlags & DDSD_TEXTURESTAGE) WARN("Does not exist in DDSURFACEDESC: DDSD_TEXTURESTAGE\n");
      if(in->dwFlags & DDSD_FVF) WARN("Does not exist in DDSURFACEDESC: DDSD_FVF\n");
      if(in->dwFlags & DDSD_SRCVBHANDLE) WARN("Does not exist in DDSURFACEDESC: DDSD_SRCVBHANDLE\n");
      out->dwFlags &= ~(DDSD_TEXTURESTAGE | DDSD_FVF | DDSD_SRCVBHANDLE);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_CreateSurface(IDirectDraw3 *iface,
                                 DDSURFACEDESC *DDSD,
                                 IDirectDrawSurface **Surf,
                                 IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      DDSURFACEDESC2 ddsd2;
      IDirectDrawSurface4 *surf4 = NULL;
      HRESULT hr;
      TRACE("Thunking to IDirectDraw4\n");
  
      DDSD_to_DDSD2(DDSD, &ddsd2);
  
      hr = IDirectDraw4_CreateSurface(dd4_from_impl(This), &ddsd2, &surf4, UnkOuter);
      if(FAILED(hr))
      {
          *Surf = NULL;
          return hr;
      }
  
      TRACE("Got surface %p\n", surf4);
      IDirectDrawSurface4_QueryInterface(surf4, &IID_IDirectDrawSurface, (void **) Surf);
      IDirectDrawSurface4_Release(surf4);
      return hr;
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_CreateSurface(IDirectDraw2 *iface,
                                 DDSURFACEDESC *DDSD,
                                 IDirectDrawSurface **Surf,
                                 IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("Thunking to IDirectDraw3\n");
      return IDirectDraw3_CreateSurface(dd3_from_impl(This), DDSD, Surf, UnkOuter);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_CreateSurface(IDirectDraw *iface,
                                DDSURFACEDESC *DDSD,
                                IDirectDrawSurface **Surf,
                                IUnknown *UnkOuter)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("Thunking to IDirectDraw3\n");
      return IDirectDraw3_CreateSurface(dd3_from_impl(This), DDSD, Surf, UnkOuter);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_DuplicateSurface(IDirectDraw4 *iface,
                                    IDirectDrawSurface4 *src,
                                    IDirectDrawSurface4 **dst)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      FIXME("(%p)->(%p,%p). Create a wrapper surface\n", This, src, dst);
  
      return IDirectDraw4_DuplicateSurface(This->parent, dds_get_inner(src), dst);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_DuplicateSurface(IDirectDraw3 *iface,
                                    IDirectDrawSurface *src,
                                    IDirectDrawSurface **dst)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      IDirectDrawSurface4 *src_4;
      IDirectDrawSurface4 *dst_4;
      HRESULT hr;
  
      TRACE("Thunking to IDirectDraw4\n");
      IDirectDrawSurface_QueryInterface(src, &IID_IDirectDrawSurface4, (void **) &src_4);
      hr = IDirectDraw4_DuplicateSurface(dd4_from_impl(This), src_4, &dst_4);
      IDirectDrawSurface4_Release(src_4);
  
      if(FAILED(hr))
      {
          *dst = NULL;
          return hr;
      }
      IDirectDrawSurface4_QueryInterface(dst_4, &IID_IDirectDrawSurface, (void **) dst);
      IDirectDrawSurface4_Release(dst_4);
      return hr;
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_DuplicateSurface(IDirectDraw2 *iface,
                                    IDirectDrawSurface *src,
                                    IDirectDrawSurface **dst)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("Thunking to IDirectDraw3\n");
      return IDirectDraw3_DuplicateSurface(dd3_from_impl(This), src, dst);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_DuplicateSurface(IDirectDraw *iface,
                                   IDirectDrawSurface *src,
                                   IDirectDrawSurface **dst)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("Thunking to IDirectDraw3\n");
      return IDirectDraw3_DuplicateSurface(dd3_from_impl(This), src, dst);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_EnumDisplayModes(IDirectDraw4 *iface,
                                    DWORD Flags,
                                    DDSURFACEDESC2 *DDSD,
                                    void *Context,
                                    LPDDENUMMODESCALLBACK2 cb)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(0x%08x,%p,%p,%p)\n", This, Flags, DDSD, Context, cb);
  
      return IDirectDraw4_EnumDisplayModes(This->parent, Flags, DDSD, Context, cb);
  }
  
  struct enummodes_ctx
  {
      LPDDENUMMODESCALLBACK orig_cb;
      void *orig_ctx;
  };
  
  static HRESULT WINAPI
  enum_modes_cb2(DDSURFACEDESC2 *ddsd2, void *vctx)
  {
      struct enummodes_ctx *ctx = vctx;
      DDSURFACEDESC ddsd;
  
      DDSD2_to_DDSD(ddsd2, &ddsd);
      return ctx->orig_cb(&ddsd, ctx->orig_ctx);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_EnumDisplayModes(IDirectDraw3 *iface,
                                    DWORD Flags,
                                    DDSURFACEDESC *DDSD,
                                    void *Context,
                                    LPDDENUMMODESCALLBACK cb)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      DDSURFACEDESC2 ddsd2;
      struct enummodes_ctx ctx;
      TRACE("(%p)->(0x%08x,%p,%p,%p): Thunking to IDirectDraw4\n", This, Flags, DDSD, Context, cb);
  
      DDSD_to_DDSD2(DDSD, &ddsd2);
      ctx.orig_cb = cb;
      ctx.orig_ctx = Context;
      return IDirectDraw4_EnumDisplayModes(dd4_from_impl(This), Flags, &ddsd2, &ctx, enum_modes_cb2);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_EnumDisplayModes(IDirectDraw2 *iface,
                                    DWORD Flags,
                                    DDSURFACEDESC *DDSD,
                                    void *Context,
                                    LPDDENUMMODESCALLBACK cb)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(0x%08x,%p,%p,%p): Thunking to IDirectDraw3\n", This, Flags, DDSD, Context, cb);
      return IDirectDraw3_EnumDisplayModes(dd3_from_impl(This), Flags, DDSD, Context, cb);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_EnumDisplayModes(IDirectDraw *iface,
                                   DWORD Flags,
                                   DDSURFACEDESC *DDSD,
                                   void *Context,
                                   LPDDENUMMODESCALLBACK cb)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(0x%08x,%p,%p,%p): Thunking to IDirectDraw3\n", This, Flags, DDSD, Context, cb);
      return IDirectDraw3_EnumDisplayModes(dd3_from_impl(This), Flags, DDSD, Context, cb);
  }
  
  struct enumsurfaces4_ctx
  {
      LPDDENUMSURFACESCALLBACK2 orig_cb;
      void *orig_ctx;
  };
  
  static HRESULT WINAPI
  enum_surfaces_wrapper(IDirectDrawSurface4 *surf4, DDSURFACEDESC2 *ddsd2, void *vctx)
  {
      struct enumsurfaces4_ctx *ctx = vctx;
      IDirectDrawSurface4 *outer = dds_get_outer(surf4);
      IDirectDrawSurface4_AddRef(outer);
      IDirectDrawSurface4_Release(surf4);
      TRACE("Returning wrapper surface %p for enumerated inner surface %p\n", outer, surf4);
      return ctx->orig_cb(outer, ddsd2, ctx->orig_ctx);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_EnumSurfaces(IDirectDraw4 *iface,
                                DWORD Flags,
                                DDSURFACEDESC2 *DDSD,
                                void *Context,
                                LPDDENUMSURFACESCALLBACK2 Callback)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      struct enumsurfaces4_ctx ctx;
      TRACE("(%p)->(0x%08x,%p,%p,%p)\n", This, Flags, DDSD, Context, Callback);
  
      ctx.orig_cb = Callback;
      ctx.orig_ctx = Context;
      return IDirectDraw4Impl_EnumSurfaces(This->parent, Flags, DDSD, &ctx, enum_surfaces_wrapper);
  }
  
  struct enumsurfaces_ctx
  {
      LPDDENUMSURFACESCALLBACK orig_cb;
      void *orig_ctx;
  };
  
  static HRESULT WINAPI
  enum_surfaces_cb2(IDirectDrawSurface4 *surf4, DDSURFACEDESC2 *ddsd2, void *vctx)
  {
      struct enumsurfaces_ctx *ctx = vctx;
      IDirectDrawSurface *surf1;
      DDSURFACEDESC ddsd;
  
      /* Keep the reference, it goes to the application */
      IDirectDrawSurface4_QueryInterface(surf4, &IID_IDirectDrawSurface, (void **) &surf1);
      /* Release the reference this function got */
      IDirectDrawSurface4_Release(surf4);
  
      DDSD2_to_DDSD(ddsd2, &ddsd);
      return ctx->orig_cb(surf1, &ddsd, ctx->orig_ctx);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_EnumSurfaces(IDirectDraw3 *iface,
                                DWORD Flags,
                                DDSURFACEDESC *DDSD,
                                void *Context,
                                LPDDENUMSURFACESCALLBACK Callback)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      DDSURFACEDESC2 ddsd2;
      struct enumsurfaces_ctx ctx;
      TRACE("(%p)->(0x%08x,%p,%p,%p): Thunking to IDirectDraw4\n", This, Flags, DDSD, Context, Callback);
  
      DDSD_to_DDSD2(DDSD, &ddsd2);
      ctx.orig_cb = Callback;
      ctx.orig_ctx = Context;
      return IDirectDraw4_EnumSurfaces(dd4_from_impl(This), Flags, &ddsd2, &ctx, enum_surfaces_cb2);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_EnumSurfaces(IDirectDraw2 *iface,
                                DWORD Flags,
                                DDSURFACEDESC *DDSD,
                                void *Context,
                                LPDDENUMSURFACESCALLBACK Callback)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(0x%08x,%p,%p,%p): Thunking to IDirectDraw3\n", This, Flags, DDSD, Context, Callback);
      return IDirectDraw3_EnumSurfaces(dd3_from_impl(This), Flags, DDSD, Context, Callback);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_EnumSurfaces(IDirectDraw *iface,
                               DWORD Flags,
                               DDSURFACEDESC *DDSD,
                               void *Context,
                               LPDDENUMSURFACESCALLBACK Callback)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(0x%08x,%p,%p,%p): Thunking to IDirectDraw3\n", This, Flags, DDSD, Context, Callback);
      return IDirectDraw3_EnumSurfaces(dd3_from_impl(This), Flags, DDSD, Context, Callback);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_FlipToGDISurface(IDirectDraw4 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)\n", This);
  
      return IDirectDraw4_FlipToGDISurface(This->parent);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_FlipToGDISurface(IDirectDraw3 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("(%p). Thunking to IDirectDraw4\n", This);
      return IDirectDraw4_FlipToGDISurface(dd4_from_impl(This));
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_FlipToGDISurface(IDirectDraw2 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p). Thunking to IDirectDraw4\n", This);
      return IDirectDraw4_FlipToGDISurface(dd4_from_impl(This));
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_FlipToGDISurface(IDirectDraw *iface)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p). Thunking to IDirectDraw4\n", This);
      return IDirectDraw4_FlipToGDISurface(dd4_from_impl(This));
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_GetCaps(IDirectDraw4 *iface,
                           DDCAPS *DriverCaps,
                           DDCAPS *HELCaps)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(%p,%p)\n", This, DriverCaps, HELCaps);
      return IDirectDraw4_GetCaps(This->parent, DriverCaps, HELCaps);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_GetCaps(IDirectDraw3 *iface,
                           DDCAPS *DriverCaps,
                           DDCAPS *HELCaps)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("(%p)->(%p,%p). Thunking to IDirectDraw4\n", This, DriverCaps, HELCaps);
      return IDirectDraw4_GetCaps(dd4_from_impl(This), DriverCaps, HELCaps);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_GetCaps(IDirectDraw2 *iface,
                           DDCAPS *DriverCaps,
                           DDCAPS *HELCaps)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(%p,%p). Thunking to IDirectDraw4\n", This, DriverCaps, HELCaps);
      return IDirectDraw4_GetCaps(dd4_from_impl(This), DriverCaps, HELCaps);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_GetCaps(IDirectDraw *iface,
                          DDCAPS *DriverCaps,
                          DDCAPS *HELCaps)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(%p,%p). Thunking to IDirectDraw4\n", This, DriverCaps, HELCaps);
      return IDirectDraw4_GetCaps(dd4_from_impl(This), DriverCaps, HELCaps);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_GetDisplayMode(IDirectDraw4 *iface,
                                  DDSURFACEDESC2 *DDSD)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(%p)\n", This, DDSD);
      return IDirectDraw4_GetDisplayMode(This->parent, DDSD);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_GetDisplayMode(IDirectDraw3 *iface,
                                  DDSURFACEDESC *DDSD)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      DDSURFACEDESC2 ddsd2;
      HRESULT hr;
  
      TRACE("(%p)->(%p): Thunking to IDirectDraw4\n", This, DDSD);
      hr = IDirectDraw4_GetDisplayMode(dd4_from_impl(This), &ddsd2);
      DDSD2_to_DDSD(&ddsd2, DDSD);
      return hr;
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_GetDisplayMode(IDirectDraw2 *iface,
                                  DDSURFACEDESC *DDSD)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw3\n", This, DDSD);
      return IDirectDraw3_GetDisplayMode(dd3_from_impl(This), DDSD);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_GetDisplayMode(IDirectDraw *iface,
                                 DDSURFACEDESC *DDSD)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw3\n", This, DDSD);
      return IDirectDraw3_GetDisplayMode(dd3_from_impl(This), DDSD);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_GetFourCCCodes(IDirectDraw4 *iface,
                                  DWORD *NumCodes,
                                  DWORD *Codes)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(%p, %p):\n", This, NumCodes, Codes);
      return IDirectDraw4_GetFourCCCodes(This->parent, NumCodes, Codes);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_GetFourCCCodes(IDirectDraw3 *iface,
                                  DWORD *NumCodes,
                                  DWORD *Codes)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("(%p)->(%p, %p): Thunking to IDirectDraw4\n", This, NumCodes, Codes);
      return IDirectDraw4_GetFourCCCodes(dd4_from_impl(This), NumCodes, Codes);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_GetFourCCCodes(IDirectDraw2 *iface,
                                  DWORD *NumCodes,
                                  DWORD *Codes)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(%p, %p): Thunking to IDirectDraw4\n", This, NumCodes, Codes);
      return IDirectDraw4_GetFourCCCodes(dd4_from_impl(This), NumCodes, Codes);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_GetFourCCCodes(IDirectDraw *iface,
                                 DWORD *NumCodes,
                                 DWORD *Codes)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(%p, %p): Thunking to IDirectDraw4\n", This, NumCodes, Codes);
      return IDirectDraw4_GetFourCCCodes(dd4_from_impl(This), NumCodes, Codes);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_GetGDISurface(IDirectDraw4 *iface,
                                 IDirectDrawSurface4 **GDISurface)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      IDirectDrawSurface4 *inner = NULL;
      HRESULT hr;
      TRACE("(%p)->(%p)\n", This, GDISurface);
  
      hr = IDirectDraw4_GetGDISurface(This->parent, &inner);
      if(SUCCEEDED(hr))
      {
          *GDISurface = dds_get_outer(inner);
          IDirectDrawSurface4_AddRef(*GDISurface);
          IDirectDrawSurface4_Release(inner);
      }
      else
      {
          *GDISurface = NULL;
      }
      return hr;
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_GetGDISurface(IDirectDraw3 *iface,
                                 IDirectDrawSurface **GDISurface)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      IDirectDrawSurface4 *surf4;
      HRESULT hr;
      TRACE("(%p)->(%p): Thunking to IDirectDraw4\n", This, GDISurface);
  
      hr = IDirectDraw4_GetGDISurface(dd4_from_impl(This), &surf4);
      if(FAILED(hr)) {
          *GDISurface = NULL;
          return hr;
      }
  
      /* Release the reference we got from the DDraw4 call, and pass a reference to the caller */
      IDirectDrawSurface4_QueryInterface(surf4, &IID_IDirectDrawSurface, (void **) GDISurface);
      IDirectDrawSurface4_Release(surf4);
      return hr;
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_GetGDISurface(IDirectDraw2 *iface,
                                 IDirectDrawSurface **GDISurface)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw3\n", This, GDISurface);
      return IDirectDraw3_GetGDISurface(dd3_from_impl(This), GDISurface);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_GetGDISurface(IDirectDraw *iface,
                                IDirectDrawSurface **GDISurface)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw3\n", This, GDISurface);
      return IDirectDraw3_GetGDISurface(dd3_from_impl(This), GDISurface);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_GetMonitorFrequency(IDirectDraw4 *iface,
                                       DWORD *Freq)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(%p)\n", This, Freq);
      return IDirectDraw4_GetMonitorFrequency(This->parent, Freq);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_GetMonitorFrequency(IDirectDraw3 *iface,
                                       DWORD *Freq)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw4\n", This, Freq);
      return IDirectDraw4_GetMonitorFrequency(dd4_from_impl(This), Freq);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_GetMonitorFrequency(IDirectDraw2 *iface,
                                       DWORD *Freq)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw4\n", This, Freq);
      return IDirectDraw4_GetMonitorFrequency(dd4_from_impl(This), Freq);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_GetMonitorFrequency(IDirectDraw *iface,
                                      DWORD *Freq)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw4\n", This, Freq);
      return IDirectDraw4_GetMonitorFrequency(dd4_from_impl(This), Freq);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_GetScanLine(IDirectDraw4 *iface,
                               DWORD *Scanline)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(%p)\n", This, Scanline);
      return IDirectDraw4_GetScanLine(This->parent, Scanline);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_GetScanLine(IDirectDraw3 *iface,
                               DWORD *Scanline)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw4\n", This, Scanline);
      return IDirectDraw4_GetScanLine(dd4_from_impl(This), Scanline);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_GetScanLine(IDirectDraw2 *iface,
                               DWORD *Scanline)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw4\n", This, Scanline);
      return IDirectDraw4_GetScanLine(dd4_from_impl(This), Scanline);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_GetScanLine(IDirectDraw *iface,
                              DWORD *Scanline)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw4\n", This, Scanline);
      return IDirectDraw4_GetScanLine(dd4_from_impl(This), Scanline);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_GetVerticalBlankStatus(IDirectDraw4 *iface,
                                          BOOL *status)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(%p)\n", This, status);
      return IDirectDraw4_GetVerticalBlankStatus(This->parent, status);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_GetVerticalBlankStatus(IDirectDraw3 *iface,
                                          BOOL *status)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw4\n", This, status);
      return IDirectDraw4_GetVerticalBlankStatus(dd4_from_impl(This), status);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_GetVerticalBlankStatus(IDirectDraw2 *iface,
                                          BOOL *status)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw4\n", This, status);
      return IDirectDraw4_GetVerticalBlankStatus(dd4_from_impl(This), status);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_GetVerticalBlankStatus(IDirectDraw *iface,
                                         BOOL *status)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(%p): Thunking to IDirectDraw4\n", This, status);
      return IDirectDraw4_GetVerticalBlankStatus(dd4_from_impl(This), status);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_Initialize(IDirectDraw4 *iface,
                              GUID *Guid)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(%s)\n", This, debugstr_guid(Guid));
      return IDirectDraw4_Initialize(This->parent, Guid);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_Initialize(IDirectDraw3 *iface,
                              GUID *Guid)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("(%p)->(%s): Thunking to IDirectDraw4\n", This, debugstr_guid(Guid));
      return IDirectDraw4_Initialize(dd4_from_impl(This), Guid);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_Initialize(IDirectDraw2 *iface,
                              GUID *Guid)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(%s): Thunking to IDirectDraw4\n", This, debugstr_guid(Guid));
      return IDirectDraw4_Initialize(dd4_from_impl(This), Guid);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_Initialize(IDirectDraw *iface,
                             GUID *Guid)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(%s): Thunking to IDirectDraw4\n", This, debugstr_guid(Guid));
      return IDirectDraw4_Initialize(dd4_from_impl(This), Guid);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_RestoreDisplayMode(IDirectDraw4 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)\n", This);
      return IDirectDraw4_RestoreDisplayMode(This->parent);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_RestoreDisplayMode(IDirectDraw3 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("(%p): Thunking to IDirectDraw4\n", This);
      return IDirectDraw4_RestoreDisplayMode(dd4_from_impl(This));
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_RestoreDisplayMode(IDirectDraw2 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p): Thunking to IDirectDraw4\n", This);
      return IDirectDraw4_RestoreDisplayMode(dd4_from_impl(This));
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_RestoreDisplayMode(IDirectDraw *iface)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p): Thunking to IDirectDraw4\n", This);
      return IDirectDraw4_RestoreDisplayMode(dd4_from_impl(This));
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_SetCooperativeLevel(IDirectDraw4 *iface,
                                       HWND hwnd,
                                       DWORD cooplevel)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(%p, 0x%08x)\n", This, hwnd, cooplevel);
      return IDirectDraw4_SetCooperativeLevel(This->parent, hwnd, cooplevel);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_SetCooperativeLevel(IDirectDraw3 *iface,
                                       HWND hwnd,
                                       DWORD cooplevel)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("(%p)->(%p, 0x%08x): Thunking to IDirectDraw4\n", This, hwnd, cooplevel);
      return IDirectDraw4_SetCooperativeLevel(dd4_from_impl(This), hwnd, cooplevel);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_SetCooperativeLevel(IDirectDraw2 *iface,
                                       HWND hwnd,
                                       DWORD cooplevel)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(%p, 0x%08x): Thunking to IDirectDraw4\n", This, hwnd, cooplevel);
      return IDirectDraw4_SetCooperativeLevel(dd4_from_impl(This), hwnd, cooplevel);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_SetCooperativeLevel(IDirectDraw *iface,
                                      HWND hwnd,
                                      DWORD cooplevel)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(%p, 0x%08x): Thunking to IDirectDraw4\n", This, hwnd, cooplevel);
      return IDirectDraw4_SetCooperativeLevel(dd4_from_impl(This), hwnd, cooplevel);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_SetDisplayMode(IDirectDraw4 *iface,
                                  DWORD Width,
                                  DWORD Height,
                                  DWORD BPP,
                                  DWORD RefreshRate,
                                  DWORD Flags)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(%u, %u, %u, %u, 0x%08x)\n", This, Width, Height, BPP, RefreshRate, Flags);
      return IDirectDraw4_SetDisplayMode(This->parent, Width, Height, BPP, RefreshRate, Flags);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_SetDisplayMode(IDirectDraw3 *iface,
                                  DWORD Width,
                                  DWORD Height,
                                  DWORD BPP,
                                  DWORD RefreshRate,
                                  DWORD Flags)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("(%p)->(%u, %u, %u, %u, 0x%08x): Thunking to IDirectDraw4\n", This, Width, Height, BPP, RefreshRate, Flags);
      return IDirectDraw3_SetDisplayMode(dd4_from_impl(This), Width, Height, BPP, RefreshRate, Flags);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_SetDisplayMode(IDirectDraw2 *iface,
                                  DWORD Width,
                                  DWORD Height,
                                  DWORD BPP,
                                  DWORD RefreshRate,
                                  DWORD Flags)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(%u, %u, %u, %u, 0x%08x): Thunking to IDirectDraw4\n", This, Width, Height, BPP, RefreshRate, Flags);
      return IDirectDraw3_SetDisplayMode(dd4_from_impl(This), Width, Height, BPP, RefreshRate, Flags);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_SetDisplayMode(IDirectDraw *iface,
                                 DWORD Width,
                                 DWORD Height,
                                 DWORD BPP)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(%u, %u, %u): Thunking to IDirectDraw4\n", This, Width, Height, BPP);
      return IDirectDraw3_SetDisplayMode(dd4_from_impl(This), Width, Height, BPP, 0, 0);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_WaitForVerticalBlank(IDirectDraw4 *iface,
                                        DWORD Flags,
                                        HANDLE h)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(0x%08x, %p)\n", This, Flags, h);
      return IDirectDraw4_WaitForVerticalBlank(This->parent, Flags, h);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_WaitForVerticalBlank(IDirectDraw3 *iface,
                                        DWORD Flags,
                                        HANDLE h)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      TRACE("(%p)->(0x%08x, %p): Thunking to IDirectDraw4\n", This, Flags, h);
      return IDirectDraw4_WaitForVerticalBlank(dd4_from_impl(This), Flags, h);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_WaitForVerticalBlank(IDirectDraw2 *iface,
                                        DWORD Flags,
                                        HANDLE h)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      TRACE("(%p)->(0x%08x, %p): Thunking to IDirectDraw4\n", This, Flags, h);
      return IDirectDraw4_WaitForVerticalBlank(dd4_from_impl(This), Flags, h);
  }
  
  static HRESULT WINAPI
  IDirectDrawImpl_WaitForVerticalBlank(IDirectDraw *iface,
                                       DWORD Flags,
                                       HANDLE h)
  {
      IDirectDrawImpl *This = impl_from_dd1(iface);
      TRACE("(%p)->(0x%08x, %p): Thunking to IDirectDraw4\n", This, Flags, h);
      return IDirectDraw4_WaitForVerticalBlank(dd4_from_impl(This), Flags, h);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_GetAvailableVidMem(IDirectDraw4 *iface,
                                      DDSCAPS2 *Caps,
                                      DWORD *total,
                                      DWORD *free)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(%p, %p, %p)\n", This, Caps, total, free);
      return IDirectDraw4_GetAvailableVidMem(This->parent, Caps, total, free);
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_GetAvailableVidMem(IDirectDraw3 *iface,
                                      DDSCAPS *Caps,
                                      DWORD *total,
                                      DWORD *free)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      DDSCAPS2 caps2;
      TRACE("(%p)->(%p, %p, %p): Thunking to IDirectDraw4\n", This, Caps, total, free);
      memset(&caps2, 0, sizeof(caps2));
      caps2.dwCaps = Caps->dwCaps;
      return IDirectDraw4_GetAvailableVidMem(dd4_from_impl(This), &caps2, total, free);
  }
  
  static HRESULT WINAPI
  IDirectDraw2Impl_GetAvailableVidMem(IDirectDraw2 *iface,
                                      DDSCAPS *Caps,
                                      DWORD *total,
                                      DWORD *free)
  {
      IDirectDrawImpl *This = impl_from_dd2(iface);
      DDSCAPS2 caps2;
      TRACE("(%p)->(%p, %p, %p): Thunking to IDirectDraw4\n", This, Caps, total, free);
      memset(&caps2, 0, sizeof(caps2));
      caps2.dwCaps = Caps->dwCaps;
      return IDirectDraw4_GetAvailableVidMem(dd4_from_impl(This), &caps2, total, free);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_GetSurfaceFromDC(IDirectDraw4 *iface,
                                    HDC hdc,
                                    IDirectDrawSurface4 **Surface)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      HRESULT hr;
      TRACE("(%p)->(%p, %p)\n", This, hdc, Surface);
      hr = IDirectDraw4_GetSurfaceFromDC(This->parent,hdc, Surface);
  
      return hr;
  }
  
  static HRESULT WINAPI
  IDirectDraw3Impl_GetSurfaceFromDC(IDirectDraw3 *iface,
                                    HDC hdc,
                                    IDirectDrawSurface **Surface)
  {
      IDirectDrawImpl *This = impl_from_dd3(iface);
      IDirectDrawSurface4 *surf4, *outer;
      IDirectDrawSurface *inner;
      HRESULT hr;
      TRACE("(%p)->(%p, %p): Thunking to IDirectDraw4\n", This, hdc, Surface);
  
      if (!Surface) return E_POINTER;
  
      hr = IDirectDraw4_GetSurfaceFromDC(This->parent, hdc, (IDirectDrawSurface4 **)&inner);
      if(FAILED(hr))
      {
          *Surface = NULL;
          return hr;
      }
  
      hr = IDirectDrawSurface_QueryInterface(inner, &IID_IDirectDrawSurface4, (void **)&surf4);
      IDirectDrawSurface_Release(inner);
      if (FAILED(hr))
      {
          *Surface = NULL;
          return hr;
      }
  
      outer = dds_get_outer(surf4);
      hr = IDirectDrawSurface4_QueryInterface(outer, &IID_IDirectDrawSurface, (void **)Surface);
      IDirectDrawSurface4_Release(surf4);
      return hr;
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_RestoreAllSurfaces(IDirectDraw4 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)\n", This);
      return IDirectDraw4_RestoreAllSurfaces(This->parent);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_TestCooperativeLevel(IDirectDraw4 *iface)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)\n", This);
      return IDirectDraw4_TestCooperativeLevel(This->parent);
  }
  
  static HRESULT WINAPI
  IDirectDraw4Impl_GetDeviceIdentifier(IDirectDraw4 *iface,
                                       DDDEVICEIDENTIFIER *DDDI,
                                       DWORD Flags)
  {
      IDirectDrawImpl *This = impl_from_dd4(iface);
      TRACE("(%p)->(%p,0x%08x)\n", This, DDDI, Flags);
      return IDirectDraw4_GetDeviceIdentifier(This->parent, DDDI, Flags);
  }
  
  static const IDirectDrawVtbl IDirectDraw1_Vtbl =
  {
      IDirectDrawImpl_QueryInterface,
      IDirectDrawImpl_AddRef,
      IDirectDrawImpl_Release,
      IDirectDrawImpl_Compact,
      IDirectDrawImpl_CreateClipper,
      IDirectDrawImpl_CreatePalette,
      IDirectDrawImpl_CreateSurface,
      IDirectDrawImpl_DuplicateSurface,
      IDirectDrawImpl_EnumDisplayModes,
      IDirectDrawImpl_EnumSurfaces,
      IDirectDrawImpl_FlipToGDISurface,
      IDirectDrawImpl_GetCaps,
      IDirectDrawImpl_GetDisplayMode,
      IDirectDrawImpl_GetFourCCCodes,
      IDirectDrawImpl_GetGDISurface,
      IDirectDrawImpl_GetMonitorFrequency,
      IDirectDrawImpl_GetScanLine,
      IDirectDrawImpl_GetVerticalBlankStatus,
      IDirectDrawImpl_Initialize,
      IDirectDrawImpl_RestoreDisplayMode,
      IDirectDrawImpl_SetCooperativeLevel,
      IDirectDrawImpl_SetDisplayMode,
      IDirectDrawImpl_WaitForVerticalBlank,
  };
  
  static const IDirectDraw2Vtbl IDirectDraw2_Vtbl =
  {
      IDirectDraw2Impl_QueryInterface,
      IDirectDraw2Impl_AddRef,
      IDirectDraw2Impl_Release,
      IDirectDraw2Impl_Compact,
      IDirectDraw2Impl_CreateClipper,
      IDirectDraw2Impl_CreatePalette,
      IDirectDraw2Impl_CreateSurface,
      IDirectDraw2Impl_DuplicateSurface,
      IDirectDraw2Impl_EnumDisplayModes,
      IDirectDraw2Impl_EnumSurfaces,
      IDirectDraw2Impl_FlipToGDISurface,
      IDirectDraw2Impl_GetCaps,
      IDirectDraw2Impl_GetDisplayMode,
      IDirectDraw2Impl_GetFourCCCodes,
      IDirectDraw2Impl_GetGDISurface,
      IDirectDraw2Impl_GetMonitorFrequency,
      IDirectDraw2Impl_GetScanLine,
      IDirectDraw2Impl_GetVerticalBlankStatus,
      IDirectDraw2Impl_Initialize,
      IDirectDraw2Impl_RestoreDisplayMode,
      IDirectDraw2Impl_SetCooperativeLevel,
      IDirectDraw2Impl_SetDisplayMode,
      IDirectDraw2Impl_WaitForVerticalBlank,
      IDirectDraw2Impl_GetAvailableVidMem
  };
  
  static const IDirectDraw3Vtbl IDirectDraw3_Vtbl =
  {
      IDirectDraw3Impl_QueryInterface,
      IDirectDraw3Impl_AddRef,
      IDirectDraw3Impl_Release,
      IDirectDraw3Impl_Compact,
      IDirectDraw3Impl_CreateClipper,
      IDirectDraw3Impl_CreatePalette,
      IDirectDraw3Impl_CreateSurface,
      IDirectDraw3Impl_DuplicateSurface,
      IDirectDraw3Impl_EnumDisplayModes,
      IDirectDraw3Impl_EnumSurfaces,
      IDirectDraw3Impl_FlipToGDISurface,
      IDirectDraw3Impl_GetCaps,
      IDirectDraw3Impl_GetDisplayMode,
      IDirectDraw3Impl_GetFourCCCodes,
      IDirectDraw3Impl_GetGDISurface,
      IDirectDraw3Impl_GetMonitorFrequency,
      IDirectDraw3Impl_GetScanLine,
      IDirectDraw3Impl_GetVerticalBlankStatus,
      IDirectDraw3Impl_Initialize,
      IDirectDraw3Impl_RestoreDisplayMode,
      IDirectDraw3Impl_SetCooperativeLevel,
      IDirectDraw3Impl_SetDisplayMode,
      IDirectDraw3Impl_WaitForVerticalBlank,
      IDirectDraw3Impl_GetAvailableVidMem,
      IDirectDraw3Impl_GetSurfaceFromDC,
  };
  
  static const IDirectDraw4Vtbl IDirectDraw4_Vtbl =
  {
      IDirectDraw4Impl_QueryInterface,
      IDirectDraw4Impl_AddRef,
      IDirectDraw4Impl_Release,
      IDirectDraw4Impl_Compact,
      IDirectDraw4Impl_CreateClipper,
      IDirectDraw4Impl_CreatePalette,
      IDirectDraw4Impl_CreateSurface,
      IDirectDraw4Impl_DuplicateSurface,
      IDirectDraw4Impl_EnumDisplayModes,
      IDirectDraw4Impl_EnumSurfaces,
      IDirectDraw4Impl_FlipToGDISurface,
      IDirectDraw4Impl_GetCaps,
      IDirectDraw4Impl_GetDisplayMode,
      IDirectDraw4Impl_GetFourCCCodes,
      IDirectDraw4Impl_GetGDISurface,
      IDirectDraw4Impl_GetMonitorFrequency,
      IDirectDraw4Impl_GetScanLine,
      IDirectDraw4Impl_GetVerticalBlankStatus,
      IDirectDraw4Impl_Initialize,
      IDirectDraw4Impl_RestoreDisplayMode,
      IDirectDraw4Impl_SetCooperativeLevel,
      IDirectDraw4Impl_SetDisplayMode,
      IDirectDraw4Impl_WaitForVerticalBlank,
      IDirectDraw4Impl_GetAvailableVidMem,
      IDirectDraw4Impl_GetSurfaceFromDC,
      IDirectDraw4Impl_RestoreAllSurfaces,
      IDirectDraw4Impl_TestCooperativeLevel,
      IDirectDraw4Impl_GetDeviceIdentifier
  };
  
  /*******************************************************************************
   * IDirectDrawFactoryImpl_CreateDirectDraw
   *******************************************************************************/
  HRESULT WINAPI
  IDirectDrawFactoryImpl_CreateDirectDraw(IDirectDrawFactory* iface,
                                          GUID * pGUID,
                                          HWND hWnd,
                                          DWORD dwCoopLevelFlags,
                                          DWORD dwReserved,
                                          IUnknown *pUnkOuter,
                                          IDirectDraw **ppDirectDraw)
  {
      HRESULT hr;
      IDirectDrawImpl *object = NULL;
      IDirectDraw *parent = NULL;
  
      TRACE("(%p)->(%s,%p,0x%08x,0x%08x,%p,%p)\n", iface, debugstr_guid(pGUID), hWnd, dwCoopLevelFlags,
            dwReserved, pUnkOuter, ppDirectDraw);
  
      if(pUnkOuter)
      {
          FIXME("Implement aggregation in ddrawex's IDirectDraw interface\n");
      }
  
      object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
      if(!object)
      {
          ERR("Out of memory\n");
          hr = E_OUTOFMEMORY;
          goto err;
      }
      object->ref = 1;
      object->IDirectDraw_Vtbl  = &IDirectDraw1_Vtbl;
      object->IDirectDraw2_Vtbl = &IDirectDraw2_Vtbl;
      object->IDirectDraw3_Vtbl = &IDirectDraw3_Vtbl;
      object->IDirectDraw4_Vtbl = &IDirectDraw4_Vtbl;
  
      hr = DirectDrawCreate(pGUID, &parent, NULL);
      if (FAILED(hr)) goto err;
  
      hr = IDirectDraw_QueryInterface(parent, &IID_IDirectDraw4, (void **) &object->parent);
      if(FAILED(hr)) goto err;
  
      hr = IDirectDraw_SetCooperativeLevel(dd1_from_impl(object), hWnd, dwCoopLevelFlags);
      if (FAILED(hr)) goto err;
  
      *ppDirectDraw = dd1_from_impl(object);
      IDirectDraw_Release(parent);
      return DD_OK;
  
  err:
      if(object && object->parent) IDirectDraw4_Release(object->parent);
      if(parent) IDirectDraw_Release(parent);
      HeapFree(GetProcessHeap(), 0, object);
      *ppDirectDraw = NULL;
      return hr;
  }
  
  IDirectDraw4 *dd_get_inner(IDirectDraw4 *outer)
  {
      IDirectDrawImpl *This = impl_from_dd4(outer);
      return This->parent;
  }
  