1 /* DirectDraw Surface Implementation
2 *
3 * Copyright (c) 1997-2000 Marcus Meissner
4 * Copyright (c) 1998-2000 Lionel Ulmer
5 * Copyright (c) 2000-2001 TransGaming Technologies Inc.
6 * Copyright (c) 2006 Stefan Dösinger
7 *
8 * This file contains the (internal) driver registration functions,
9 * driver enumeration APIs and DirectDraw creation functions.
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26 #include "config.h"
27 #include "wine/port.h"
28
29 #include <assert.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <stdlib.h>
33
34 #define COBJMACROS
35 #define NONAMELESSUNION
36
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winerror.h"
40 #include "wingdi.h"
41 #include "wine/exception.h"
42
43 #include "ddraw.h"
44 #include "d3d.h"
45
46 #include "ddraw_private.h"
47 #include "wine/debug.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
50
51 /*****************************************************************************
52 * IUnknown parts follow
53 *****************************************************************************/
54
55 /*****************************************************************************
56 * IDirectDrawSurface7::QueryInterface
57 *
58 * A normal QueryInterface implementation. For QueryInterface rules
59 * see ddraw.c, IDirectDraw7::QueryInterface. This method
60 * can Query IDirectDrawSurface interfaces in all version, IDirect3DTexture
61 * in all versions, the IDirectDrawGammaControl interface and it can
62 * create an IDirect3DDevice. (Uses IDirect3D7::CreateDevice)
63 *
64 * Params:
65 * riid: The interface id queried for
66 * obj: Address to write the pointer to
67 *
68 * Returns:
69 * S_OK on success
70 * E_NOINTERFACE if the requested interface wasn't found
71 *
72 *****************************************************************************/
73 static HRESULT WINAPI
74 IDirectDrawSurfaceImpl_QueryInterface(IDirectDrawSurface7 *iface,
75 REFIID riid,
76 void **obj)
77 {
78 ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirectDrawSurface7, iface);
79
80 /* According to COM docs, if the QueryInterface fails, obj should be set to NULL */
81 *obj = NULL;
82
83 if(!riid)
84 return DDERR_INVALIDPARAMS;
85
86 TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),obj);
87 if (IsEqualGUID(riid, &IID_IUnknown)
88 || IsEqualGUID(riid, &IID_IDirectDrawSurface7)
89 || IsEqualGUID(riid, &IID_IDirectDrawSurface4) )
90 {
91 IUnknown_AddRef(iface);
92 *obj = ICOM_INTERFACE(This, IDirectDrawSurface7);
93 TRACE("(%p) returning IDirectDrawSurface7 interface at %p\n", This, *obj);
94 return S_OK;
95 }
96 else if( IsEqualGUID(riid, &IID_IDirectDrawSurface3)
97 || IsEqualGUID(riid, &IID_IDirectDrawSurface2)
98 || IsEqualGUID(riid, &IID_IDirectDrawSurface) )
99 {
100 IUnknown_AddRef(iface);
101 *obj = ICOM_INTERFACE(This, IDirectDrawSurface3);
102 TRACE("(%p) returning IDirectDrawSurface3 interface at %p\n", This, *obj);
103 return S_OK;
104 }
105 else if( IsEqualGUID(riid, &IID_IDirectDrawGammaControl) )
106 {
107 IUnknown_AddRef(iface);
108 *obj = ICOM_INTERFACE(This, IDirectDrawGammaControl);
109 TRACE("(%p) returning IDirectDrawGammaControl interface at %p\n", This, *obj);
110 return S_OK;
111 }
112 else if( IsEqualGUID(riid, &IID_D3DDEVICE_WineD3D) ||
113 IsEqualGUID(riid, &IID_IDirect3DHALDevice)||
114 IsEqualGUID(riid, &IID_IDirect3DRGBDevice) )
115 {
116 IDirect3DDevice7 *d3d;
117
118 /* Call into IDirect3D7 for creation */
119 IDirect3D7_CreateDevice(ICOM_INTERFACE(This->ddraw, IDirect3D7),
120 riid,
121 ICOM_INTERFACE(This, IDirectDrawSurface7),
122 &d3d);
123
124 *obj = COM_INTERFACE_CAST(IDirect3DDeviceImpl, IDirect3DDevice7, IDirect3DDevice, d3d);
125 TRACE("(%p) Returning IDirect3DDevice interface at %p\n", This, *obj);
126
127 return S_OK;
128 }
129 else if (IsEqualGUID( &IID_IDirect3DTexture, riid ) ||
130 IsEqualGUID( &IID_IDirect3DTexture2, riid ))
131 {
132 if (IsEqualGUID( &IID_IDirect3DTexture, riid ))
133 {
134 *obj = ICOM_INTERFACE(This, IDirect3DTexture);
135 TRACE(" returning Direct3DTexture interface at %p.\n", *obj);
136 }
137 else
138 {
139 *obj = ICOM_INTERFACE(This, IDirect3DTexture2);
140 TRACE(" returning Direct3DTexture2 interface at %p.\n", *obj);
141 }
142 IUnknown_AddRef( (IUnknown *) *obj);
143 return S_OK;
144 }
145
146 ERR("No interface\n");
147 return E_NOINTERFACE;
148 }
149
150 /*****************************************************************************
151 * IDirectDrawSurface7::AddRef
152 *
153 * A normal addref implementation
154 *
155 * Returns:
156 * The new refcount
157 *
158 *****************************************************************************/
159 static ULONG WINAPI
160 IDirectDrawSurfaceImpl_AddRef(IDirectDrawSurface7 *iface)
161 {
162 ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirectDrawSurface7, iface);
163 ULONG refCount = InterlockedIncrement(&This->ref);
164
165 TRACE("(%p) : AddRef increasing from %d\n", This, refCount - 1);
166 return refCount;
167 }
168
169 /*****************************************************************************
170 * IDirectDrawSurfaceImpl_Destroy
171 *
172 * A helper function for IDirectDrawSurface7::Release
173 *
174 * Frees the surface, regardless of its refcount.
175 * See IDirectDrawSurface7::Release for more information
176 *
177 * Params:
178 * This: Surface to free
179 *
180 *****************************************************************************/
181 void IDirectDrawSurfaceImpl_Destroy(IDirectDrawSurfaceImpl *This)
182 {
183 TRACE("(%p)\n", This);
184
185 /* Check the refcount and give a warning */
186 if(This->ref > 1)
187 {
188 /* This can happen when a complex surface is destroyed,
189 * because the 2nd surface was addref()ed when the app
190 * called GetAttachedSurface
191 */
192 WARN("(%p): Destroying surface with refount %d\n", This, This->ref);
193 }
194
195 /* Check for attached surfaces and detach them */
196 if(This->first_attached != This)
197 {
198 /* Well, this shouldn't happen: The surface being attached is addref()ed
199 * in AddAttachedSurface, so it shouldn't be released until DeleteAttachedSurface
200 * is called, because the refcount is held. It looks like the app released()
201 * it often enough to force this
202 */
203 IDirectDrawSurface7 *root = ICOM_INTERFACE(This->first_attached, IDirectDrawSurface7);
204 IDirectDrawSurface7 *detach = ICOM_INTERFACE(This, IDirectDrawSurface7);
205
206 FIXME("(%p) Freeing a surface that is attached to surface %p\n", This, This->first_attached);
207
208 /* The refcount will drop to -1 here */
209 if(IDirectDrawSurface7_DeleteAttachedSurface(root, 0, detach) != DD_OK)
210 {
211 ERR("(%p) DeleteAttachedSurface failed!\n", This);
212 }
213 }
214
215 while(This->next_attached != NULL)
216 {
217 IDirectDrawSurface7 *root = ICOM_INTERFACE(This, IDirectDrawSurface7);
218 IDirectDrawSurface7 *detach = ICOM_INTERFACE(This->next_attached, IDirectDrawSurface7);
219
220 if(IDirectDrawSurface7_DeleteAttachedSurface(root, 0, detach) != DD_OK)
221 {
222 ERR("(%p) DeleteAttachedSurface failed!\n", This);
223 assert(0);
224 }
225 }
226
227 /* Now destroy the surface. Wait: It could have been released if we are a texture */
228 if(This->WineD3DSurface)
229 IWineD3DSurface_Release(This->WineD3DSurface);
230
231 /* Having a texture handle set implies that the device still exists */
232 if(This->Handle)
233 {
234 This->ddraw->d3ddevice->Handles[This->Handle - 1].ptr = NULL;
235 This->ddraw->d3ddevice->Handles[This->Handle - 1].type = DDrawHandle_Unknown;
236 }
237
238 /* Reduce the ddraw surface count */
239 InterlockedDecrement(&This->ddraw->surfaces);
240 list_remove(&This->surface_list_entry);
241
242 HeapFree(GetProcessHeap(), 0, This);
243 }
244
245 /*****************************************************************************
246 * IDirectDrawSurface7::Release
247 *
248 * Reduces the surface's refcount by 1. If the refcount falls to 0, the
249 * surface is destroyed.
250 *
251 * Destroying the surface is a bit tricky. For the connection between
252 * WineD3DSurfaces and DirectDrawSurfaces see IDirectDraw7::CreateSurface
253 * It has a nice graph explaining the connection.
254 *
255 * What happens here is basically this:
256 * When a surface is destroyed, its WineD3DSurface is released,
257 * and the refcount of the DirectDraw interface is reduced by 1. If it has
258 * complex surfaces attached to it, then these surfaces are destroyed too,
259 * regardless of their refcount. If any surface being destroyed has another
260 * surface attached to it (with a "soft" attachment, not complex), then
261 * this surface is detached with DeleteAttachedSurface.
262 *
263 * When the surface is a texture, the WineD3DTexture is released.
264 * If the surface is the Direct3D render target, then the D3D
265 * capabilities of the WineD3DDevice are uninitialized, which causes the
266 * swapchain to be released.
267 *
268 * When a complex sublevel falls to ref zero, then this is ignored.
269 *
270 * Returns:
271 * The new refcount
272 *
273 *****************************************************************************/
274 static ULONG WINAPI
275 IDirectDrawSurfaceImpl_Release(IDirectDrawSurface7 *iface)
276 {
277 ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirectDrawSurface7, iface);
278 ULONG ref;
279 TRACE("(%p) : Releasing from %d\n", This, This->ref);
280 ref = InterlockedDecrement(&This->ref);
281
282 if (ref == 0)
283 {
284
285 IDirectDrawSurfaceImpl *surf;
286 IDirectDrawImpl *ddraw;
287 IUnknown *ifaceToRelease = This->ifaceToRelease;
288 UINT i;
289
290 /* Complex attached surfaces are destroyed implicitly when the root is released */
291 EnterCriticalSection(&ddraw_cs);
292 if(!This->is_complex_root)
293 {
294 WARN("(%p) Attempt to destroy a surface that is not a complex root\n", This);
295 LeaveCriticalSection(&ddraw_cs);
296 return ref;
297 }
298 ddraw = This->ddraw;
299
300 /* If it's a texture, destroy the WineD3DTexture.
301 * WineD3D will destroy the IParent interfaces
302 * of the sublevels, which destroys the WineD3DSurfaces.
303 * Set the surfaces to NULL to avoid destroying them again later
304 */
305 if(This->wineD3DTexture)
306 {
307 IWineD3DBaseTexture_Release(This->wineD3DTexture);
308 }
309 /* If it's the RenderTarget, destroy the d3ddevice */
310 else if(This->wineD3DSwapChain)
311 {
312 if((ddraw->d3d_initialized) && (This == ddraw->d3d_target)) {
313 TRACE("(%p) Destroying the render target, uninitializing D3D\n", This);
314
315 /* Unset any index buffer, just to be sure */
316 IWineD3DDevice_SetIndices(ddraw->wineD3DDevice, NULL);
317 IWineD3DDevice_SetDepthStencilSurface(ddraw->wineD3DDevice, NULL);
318 IWineD3DDevice_SetVertexDeclaration(ddraw->wineD3DDevice, NULL);
319 for(i = 0; i < ddraw->numConvertedDecls; i++)
320 {
321 IWineD3DVertexDeclaration_Release(ddraw->decls[i].decl);
322 }
323 HeapFree(GetProcessHeap(), 0, ddraw->decls);
324 ddraw->numConvertedDecls = 0;
325
326 if(IWineD3DDevice_Uninit3D(ddraw->wineD3DDevice, D3D7CB_DestroyDepthStencilSurface, D3D7CB_DestroySwapChain) != D3D_OK)
327 {
328 /* Not good */
329 ERR("(%p) Failed to uninit 3D\n", This);
330 }
331 else
332 {
333 /* Free the d3d window if one was created */
334 if(ddraw->d3d_window != 0 && ddraw->d3d_window != ddraw->dest_window)
335 {
336 TRACE(" (%p) Destroying the hidden render window %p\n", This, ddraw->d3d_window);
337 DestroyWindow(ddraw->d3d_window);
338 ddraw->d3d_window = 0;
339 }
340 /* Unset the pointers */
341 }
342
343 This->wineD3DSwapChain = NULL; /* Uninit3D releases the swapchain */
344 ddraw->d3d_initialized = FALSE;
345 ddraw->d3d_target = NULL;
346 } else {
347 IWineD3DDevice_UninitGDI(ddraw->wineD3DDevice, D3D7CB_DestroySwapChain);
348 This->wineD3DSwapChain = NULL;
349 }
350
351 /* Reset to the default surface implementation type. This is needed if apps use
352 * non render target surfaces and expect blits to work after destroying the render
353 * target.
354 *
355 * TODO: Recreate existing offscreen surfaces
356 */
357 ddraw->ImplType = DefaultSurfaceType;
358
359 /* Write a trace because D3D unloading was the reason for many
360 * crashes during development.
361 */
362 TRACE("(%p) D3D unloaded\n", This);
363 }
364 else if(This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_PRIMARYSURFACE |
365 DDSCAPS_3DDEVICE |
366 DDSCAPS_TEXTURE ) )
367 {
368 /* It's a render target, but no swapchain was created.
369 * The IParent interfaces have to be released manually.
370 * The same applies for textures without an
371 * IWineD3DTexture object attached
372 */
373 IParent *Parent;
374
375 for(i = 0; i < MAX_COMPLEX_ATTACHED; i++)
376 {
377 if(This->complex_array[i])
378 {
379 /* Only the topmost level can have more than 1 surfaces in the complex
380 * attachment array(Cube texture roots), for all others there is only
381 * one
382 */
383 surf = This->complex_array[i];
384 while(surf)
385 {
386 IWineD3DSurface_GetParent(surf->WineD3DSurface,
387 (IUnknown **) &Parent);
388 IParent_Release(Parent); /* For the getParent */
389 IParent_Release(Parent); /* To release it */
390 surf = surf->complex_array[0];
391 }
392 }
393 }
394
395 /* Now the top-level surface */
396 IWineD3DSurface_GetParent(This->WineD3DSurface,
397 (IUnknown **) &Parent);
398 IParent_Release(Parent); /* For the getParent */
399 IParent_Release(Parent); /* To release it */
400 }
401
402 /* The refcount test shows that the palette is detached when the surface is destroyed */
403 IDirectDrawSurface7_SetPalette(ICOM_INTERFACE(This, IDirectDrawSurface7),
404 NULL);
405
406 /* Loop through all complex attached surfaces,
407 * and destroy them.
408 *
409 * Yet again, only the root can have more than one complexly attached surface, all the others
410 * have a total of one;
411 */
412 for(i = 0; i < MAX_COMPLEX_ATTACHED; i++)
413 {
414 if(!This->complex_array[i]) break;
415
416 surf = This->complex_array[i];
417 This->complex_array[i] = NULL;
418 while(surf)
419 {
420 IDirectDrawSurfaceImpl *destroy = surf;
421 surf = surf->complex_array[0]; /* Iterate through the "tree" */
422 IDirectDrawSurfaceImpl_Destroy(destroy); /* Destroy it */
423 }
424 }
425
426 /* Destroy the root surface.
427 */
428 IDirectDrawSurfaceImpl_Destroy(This);
429
430 /* Reduce the ddraw refcount */
431 if(ifaceToRelease) IUnknown_Release(ifaceToRelease);
432 LeaveCriticalSection(&ddraw_cs);
433 }
434
435 return ref;
436 }
437
438 /*****************************************************************************
439 * IDirectDrawSurface7::GetAttachedSurface
440 *
441 * Returns an attached surface with the requested caps. Surface attachment
442 * and complex surfaces are not clearly described by the MSDN or sdk,
443 * so this method is tricky and likely to contain problems.
444 * This implementation searches the complex list first, then the
445 * attachment chain.
446 *
447 * The chains are searched from This down to the last surface in the chain,
448 * not from the first element in the chain. The first surface found is
449 * returned. The MSDN says that this method fails if more than one surface
450 * matches the caps, but it is not sure if that is right. The attachment
451 * structure may not even allow two matching surfaces.
452 *
453 * The found surface is AddRef-ed before it is returned.
454 *
455 * Params:
456 * Caps: Pointer to a DDCAPS2 structure describing the caps asked for
457 * Surface: Address to store the found surface
458 *
459 * Returns:
460 * DD_OK on success
461 * DDERR_INVALIDPARAMS if Caps or Surface is NULL
462 * DDERR_NOTFOUND if no surface was found
463 *
464 *****************************************************************************/
465 static HRESULT WINAPI
466 IDirectDrawSurfaceImpl_GetAttachedSurface(IDirectDrawSurface7 *iface,
467 DDSCAPS2 *Caps,
468 IDirectDrawSurface7 **Surface)
469 {
470 ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirectDrawSurface7, iface);
471 IDirectDrawSurfaceImpl *surf;
472 DDSCAPS2 our_caps;
473 int i;
474
475 TRACE("(%p)->(%p,%p)\n", This, Caps, Surface);
476 EnterCriticalSection(&ddraw_cs);
477
478 our_caps = *Caps;
479
480 if(This->version < 7)
481 {
482 /* Earlier dx apps put garbage into these members, clear them */
483 our_caps.dwCaps2 = 0;
484 our_caps.dwCaps3 = 0;
485 our_caps.dwCaps4 = 0;
486 }
487
488 TRACE("(%p): Looking for caps: %x,%x,%x,%x\n", This, our_caps.dwCaps, our_caps.dwCaps2, our_caps.dwCaps3, our_caps.dwCaps4); /* FIXME: Better debugging */
489
490 for(i = 0; i < MAX_COMPLEX_ATTACHED; i++)
491 {
492 surf = This->complex_array[i];
493 if(!surf) break;
494
495 if (TRACE_ON(ddraw))
496 {
497 TRACE("Surface: (%p) caps: %x,%x,%x,%x\n", surf,
498 surf->surface_desc.ddsCaps.dwCaps,
499 surf->surface_desc.ddsCaps.dwCaps2,
500 surf->surface_desc.ddsCaps.dwCaps3,
501 surf->surface_desc.ddsCaps.dwCaps4);
502 }
503
504 if (((surf->surface_desc.ddsCaps.dwCaps & our_caps.dwCaps) == our_caps.dwCaps) &&
505 ((surf->surface_desc.ddsCaps.dwCaps2 & our_caps.dwCaps2) == our_caps.dwCaps2)) {
506
507 /* MSDN: "This method fails if more than one surface is attached
508 * that matches the capabilities requested."
509 *
510 * Not sure how to test this.
511 */
512
513 TRACE("(%p): Returning surface %p\n", This, surf);
514 TRACE("(%p): mipmapcount=%d\n", This, surf->mipmap_level);
515 *Surface = ICOM_INTERFACE(surf, IDirectDrawSurface7);
516 IDirectDrawSurface7_AddRef(*Surface);
517 LeaveCriticalSection(&ddraw_cs);
518 return DD_OK;
519 }
520 }
521
522 /* Next, look at the attachment chain */
523 surf = This;
524
525 while( (surf = surf->next_attached) )
526 {
527 if (TRACE_ON(ddraw))
528 {
529 TRACE("Surface: (%p) caps: %x,%x,%x,%x\n", surf,
530 surf->surface_desc.ddsCaps.dwCaps,
531 surf->surface_desc.ddsCaps.dwCaps2,
532 surf->surface_desc.ddsCaps.dwCaps3,
533 surf->surface_desc.ddsCaps.dwCaps4);
534 }
535
536 if (((surf->surface_desc.ddsCaps.dwCaps & our_caps.dwCaps) == our_caps.dwCaps) &&
537 ((surf->surface_desc.ddsCaps.dwCaps2 & our_caps.dwCaps2) == our_caps.dwCaps2)) {
538
539 TRACE("(%p): Returning surface %p\n", This, surf);
540 *Surface = ICOM_INTERFACE(surf, IDirectDrawSurface7);
541 IDirectDrawSurface7_AddRef(*Surface);
542 LeaveCriticalSection(&ddraw_cs);
543 return DD_OK;
544 }
545 }
546
547 TRACE("(%p) Didn't find a valid surface\n", This);
548 LeaveCriticalSection(&ddraw_cs);
549
550 *Surface = NULL;
551 return DDERR_NOTFOUND;
552 }
553
554 /*****************************************************************************
555 * IDirectDrawSurface7::Lock
556 *
557 * Locks the surface and returns a pointer to the surface's memory
558 *
559 * Params:
560 * Rect: Rectangle to lock. If NULL, the whole surface is locked
561 * DDSD: Pointer to a DDSURFACEDESC2 which shall receive the surface's desc.
562 * Flags: Locking flags, e.g Read only or write only
563 * h: An event handle that's not used and must be NULL
564 *
565 * Returns:
566 * DD_OK on success
567 * DDERR_INVALIDPARAMS if DDSD is NULL
568 * For more details, see IWineD3DSurface::LockRect
569 *
570 *****************************************************************************/
571 static HRESULT WINAPI
572 IDirectDrawSurfaceImpl_Lock(IDirectDrawSurface7 *iface,
573 RECT *Rect,
574 DDSURFACEDESC2 *DDSD,
575 DWORD Flags,
576 HANDLE h)
577 {
578 ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirectDrawSurface7, iface);
579 WINED3DLOCKED_RECT LockedRect;
580 HRESULT hr;
581 TRACE("(%p)->(%p,%p,%x,%p)\n", This, Rect, DDSD, Flags, h);
582
583 if(!DDSD)
584 return DDERR_INVALIDPARAMS;
585
586 /* This->surface_desc.dwWidth and dwHeight are changeable, thus lock */
587 EnterCriticalSection(&ddraw_cs);
588
589 /* Should I check for the handle to be NULL?
590 *
591 * The DDLOCK flags and the D3DLOCK flags are equal
592 * for the supported values. The others are ignored by WineD3D
593 */
594
595 if(DDSD->dwSize != sizeof(DDSURFACEDESC) &&
596 DDSD->dwSize != sizeof(DDSURFACEDESC2))
597 {
598 WARN("Invalid structure size %d, returning DDERR_INVALIDPARAMS\n", DDERR_INVALIDPARAMS);
599 LeaveCriticalSection(&ddraw_cs);
600 return DDERR_INVALIDPARAMS;
601 }
602
603 /* Windows zeroes this if the rect is invalid */
604 DDSD->lpSurface = 0;
605
606 if (Rect)
607 {
608 if ((Rect->left < 0)
609 || (Rect->top < 0)
610 || (Rect->left > Rect->right)
611 || (Rect->top > Rect->bottom)
612 || (Rect->right > This->surface_desc.dwWidth)
613 || (Rect->bottom > This->surface_desc.dwHeight))
614 {
615 WARN("Trying to lock an invalid rectangle, returning DDERR_INVALIDPARAMS\n");
616 LeaveCriticalSection(&ddraw_cs);
617 return DDERR_INVALIDPARAMS;
618 }
619 }
620
621 hr = IWineD3DSurface_LockRect(This->WineD3DSurface,
622 &LockedRect,
623 Rect,
624 Flags);
625 if(hr != D3D_OK)
626 {
627 LeaveCriticalSection(&ddraw_cs);
628 switch(hr)
629 {
630 /* D3D8 and D3D9 return the general D3DERR_INVALIDCALL error, but ddraw has a more
631 * specific error. But since IWineD3DSurface::LockRect returns that error in this
632 * only occasion, keep d3d8 and d3d9 free from the return value override. There are
633 * many different places where d3d8/9 would have to catch the DDERR_SURFACEBUSY, it
634 * is much easier to do it in one place in ddraw
635 */
636 case WINED3DERR_INVALIDCALL: return DDERR_SURFACEBUSY;
637 default: return hr;
638 }
639 }
640
641 /* Override the memory area. The pitch should be set already. Strangely windows
642 * does not set the LPSURFACE flag on locked surfaces !?!.
643 * DDSD->dwFlags |= DDSD_LPSURFACE;
644 */
645 This->surface_desc.lpSurface = LockedRect.pBits;
646 DD_STRUCT_COPY_BYSIZE(DDSD,&(This->surface_desc));
647
648 TRACE("locked surface returning description :\n");
649 if (TRACE_ON(ddraw)) DDRAW_dump_surface_desc(DDSD);
650
651 LeaveCriticalSection(&ddraw_cs);
652 return DD_OK;
653 }
654
655 /*****************************************************************************
656 * IDirectDrawSurface7::Unlock
657 *
658 * Unlocks an locked surface
659 *
660 * Params:
661 * Rect: Not used by this implementation
662 *
663 * Returns:
664 * D3D_OK on success
665 * For more details, see IWineD3DSurface::UnlockRect
666 *
667 *****************************************************************************/
668 static HRESULT WINAPI
669 IDirectDrawSurfaceImpl_Unlock(IDirectDrawSurface7 *iface,
670 RECT *pRect)
671 {
672 ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirectDrawSurface7, iface);
673 HRESULT hr;
674 TRACE("(%p)->(%p)\n", This, pRect);
675
676 EnterCriticalSection(&ddraw_cs);
677 hr = IWineD3DSurface_UnlockRect(This->WineD3DSurface);
678 if(SUCCEEDED(hr))
679 {
680 This->surface_desc.lpSurface = NULL;
681 }
682 LeaveCriticalSection(&ddraw_cs);
683 return hr;
684 }
685
686 /*****************************************************************************
687 * IDirectDrawSurface7::Flip
688 *
689 * Flips a surface with the DDSCAPS_FLIP flag. The flip is relayed to
690 * IWineD3DSurface::Flip. Because WineD3D doesn't handle attached surfaces,
691 * the flip target is passed to WineD3D, even if the app didn't specify one
692 *
693 * Params:
694 * DestOverride: Specifies the surface that will become the new front
695 * buffer. If NULL, the current back buffer is used
696 * Flags: some DirectDraw flags, see include/ddraw.h
697 *
698 * Returns:
699 * DD_OK on success
700 * DDERR_NOTFLIPPABLE if no flip target could be found
701 * DDERR_INVALIDOBJECT if the surface isn't a front buffer
702 * For more details, see IWineD3DSurface::Flip
703 *
704 *****************************************************************************/
705 static HRESULT WINAPI
706 IDirectDrawSurfaceImpl_Flip(IDirectDrawSurface7 *iface,
707 IDirectDrawSurface7 *DestOverride,
708 DWORD Flags)
709 {
710 ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirectDrawSurface7, iface);
711 IDirectDrawSurfaceImpl *Override = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7, DestOverride);
712 IDirectDrawSurface7 *Override7;
713 HRESULT hr;
714 TRACE("(%p)->(%p,%x)\n", This, DestOverride, Flags);
715
716 /* Flip has to be called from a front buffer
717 * What about overlay surfaces, AFAIK they can flip too?
718 */
719 if( !(This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER | DDSCAPS_OVERLAY)) )
720 return DDERR_INVALIDOBJECT; /* Unchecked */
721
722 EnterCriticalSection(&ddraw_cs);
723
724 /* WineD3D doesn't keep track of attached surface, so find the target */
725 if(!Override)
726 {
727 DDSCAPS2 Caps;
728
729 memset(&Caps, 0, sizeof(Caps));
730 Caps.dwCaps |= DDSCAPS_BACKBUFFER;
731 hr = IDirectDrawSurface7_GetAttachedSurface(iface, &Caps, &Override7);
732 if(hr != DD_OK)
733 {
734 ERR("Can't find a flip target\n");
735 LeaveCriticalSection(&ddraw_cs);
736 return DDERR_NOTFLIPPABLE; /* Unchecked */
737 }
738 Override = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7, Override7);
739
740 /* For the GetAttachedSurface */
741 IDirectDrawSurface7_Release(Override7);
742 }
743
744 hr = IWineD3DSurface_Flip(This->WineD3DSurface,
745 Override->WineD3DSurface,
746 Flags);
747 LeaveCriticalSection(&ddraw_cs);
748 return hr;
749 }
750
751 /*****************************************************************************
752 * IDirectDrawSurface7::Blt
753 *
754 * Performs a blit on the surface
755 *
756 * Params:
757 * DestRect: Destination rectangle, can be NULL
758 * SrcSurface: Source surface, can be NULL
759 * SrcRect: Source rectangle, can be NULL
760 * Flags: Blt flags
761 * DDBltFx: Some extended blt parameters, connected to the flags
762 *
763 * Returns:
764 * D3D_OK on success
765 * See IWineD3DSurface::Blt for more details
766 *
767 *****************************************************************************/
768 static HRESULT WINAPI
769 IDirectDrawSurfaceImpl_Blt(IDirectDrawSurface7 *iface,
770 RECT *DestRect,
771 IDirectDrawSurface7 *SrcSurface,
772 RECT *SrcRect,
773 DWORD Flags,
774 DDBLTFX *DDBltFx)
775 {
776 ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirectDrawSurface7, iface);
777 HRESULT hr;
778 IDirectDrawSurfaceImpl *Src = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7, SrcSurface);
779 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, Src, SrcRect, Flags, DDBltFx);
780
781 /* Check for validity of the flags here. WineD3D Has the software-opengl selection path and would have
782 * to check at 2 places, and sometimes do double checks. This also saves the call to wined3d :-)
783 */
784 if((Flags & DDBLT_KEYSRCOVERRIDE) && (!DDBltFx || Flags & DDBLT_KEYSRC)) {
785 WARN("Invalid source color key parameters, returning DDERR_INVALIDPARAMS\n");
786 return DDERR_INVALIDPARAMS;
787 }
788
789 if((Flags & DDBLT_KEYDESTOVERRIDE) && (!DDBltFx || Flags & DDBLT_KEYDEST)) {
790 WARN("Invalid destination color key parameters, returning DDERR_INVALIDPARAMS\n");
791 return DDERR_INVALIDPARAMS;
792 }
793
794 /* Sizes can change, therefore hold the lock when testing the rectangles */
795 EnterCriticalSection(&ddraw_cs);
796 if(DestRect)
797 {
798 if(DestRect->top >= DestRect->bottom || DestRect->left >= DestRect->right ||
799 DestRect->right > This->surface_desc.dwWidth ||
800 DestRect->bottom > This->surface_desc.dwHeight)
801 {
802 WARN("Destination rectangle is invalid, returning DDERR_INVALIDRECT\n");
803 LeaveCriticalSection(&ddraw_cs);
804 return DDERR_INVALIDRECT;
805 }
806 }
807 if(Src && SrcRect)
808 {
809 if(SrcRect->top >= SrcRect->bottom || SrcRect->left >=SrcRect->right ||
810 SrcRect->right > Src->surface_desc.dwWidth ||
811 SrcRect->bottom > Src->surface_desc.dwHeight)
812 {
813 WARN("Source rectangle is invalid, returning DDERR_INVALIDRECT\n");
814 LeaveCriticalSection(&ddraw_cs);
815 return DDERR_INVALIDRECT;
816 }
817 }
818
819 if(Flags & DDBLT_KEYSRC && (!Src || !(Src->surface_desc.dwFlags & DDSD_CKSRCBLT))) {
820 WARN("DDBLT_KEYDEST blit without color key in surface, returning DDERR_INVALIDPARAMS\n");
821 LeaveCriticalSection(&ddraw_cs);
822 return DDERR_INVALIDPARAMS;
823 }
824
825 /* TODO: Check if the DDBltFx contains any ddraw surface pointers. If it does, copy the struct,
826 * and replace the ddraw surfaces with the wined3d surfaces
827 * So far no blitting operations using surfaces in the bltfx struct are supported anyway.
828 */
829 hr = IWineD3DSurface_Blt(This->WineD3DSurface,
830 DestRect,
831 Src ? Src->WineD3DSurface : NULL,
832 SrcRect,
833 Flags,
834 (WINEDDBLTFX *) DDBltFx,
835 WINED3DTEXF_POINT);
836
837 LeaveCriticalSection(&ddraw_cs);
838 switch(hr)
839 {
840 case WINED3DERR_NOTAVAILABLE: return DDERR_UNSUPPORTED;
841 case WINED3DERR_WRONGTEXTUREFORMAT: return DDERR_INVALIDPIXELFORMAT;
842 default: return hr;
843 }
844 }
845
846 /*****************************************************************************
847 * IDirectDrawSurface7::AddAttachedSurface
848 *
849 * Attaches a surface to another surface. How the surface attachments work
850 * is not totally understood yet, and this method is prone to problems.
851 * he surface that is attached is AddRef-ed.
852 *
853 * Tests with complex surfaces suggest that the surface attachments form a
854 * tree, but no method to test this has been found yet.
855 *
856 * The attachment list consists of a first surface (first_attached) and
857 * for each surface a pointer to the next attached surface (next_attached).
858 * For the first surface, and a surface that has no attachments
859 * first_attached points to the surface itself. A surface that has
860 * no successors in the chain has next_attached set to NULL.
861 *
862 * Newly attached surfaces are attached right after the root surface.
863 * If a surface is attached to a complex surface compound, it's attached to
864 * the surface that the app requested, not the complex root. See
865 * GetAttachedSurface for a description how surfaces are found.
866 *
867 * This is how the current implementation works, and it was coded by looking
868 * at the needs of the applications.
869 *
870 * So far only Z-Buffer attachments are tested, and they are activated in
871 * WineD3D. Mipmaps could be tricky to activate in WineD3D.
872 * Back buffers should work in 2D mode, but they are not tested(They can be
873 * attached in older iface versions). Rendering to the front buffer and
874 * switching between that and double buffering is not yet implemented in
875 * WineD3D, so for 3D it might have unexpected results.
876 *
877 * IDirectDrawSurfaceImpl_AddAttachedSurface is the real thing,
878 * IDirectDrawSurface7Impl_AddAttachedSurface is a wrapper around it that
879 * performs additional checks. Version 7 of this interface is much more restrictive
880 * than its predecessors.
881 *
882 * Params:
883 * Attach: Surface to attach to iface
884 *
885 * Returns:
886 * DD_OK on success
887 * DDERR_CANNOTATTACHSURFACE if the surface can't be attached for some reason
888 *
889 *****************************************************************************/
890 HRESULT WINAPI
891 IDirectDrawSurfaceImpl_AddAttachedSurface(IDirectDrawSurfaceImpl *This,
892 IDirectDrawSurfaceImpl *Surf)
893 {
894 TRACE("(%p)->(%p)\n", This, Surf);
895
896 if(Surf == This)
897 return DDERR_CANNOTATTACHSURFACE; /* unchecked */
898
899 EnterCriticalSection(&ddraw_cs);
900
901 /* Check if the surface is already attached somewhere */
902 if( (Surf->next_attached != NULL) ||
903 (Surf->first_attached != Surf) )
904 {
905 /* TODO: Test for the structure of the manual attachment. Is it a chain or a list?
906 * What happens if one surface is attached to 2 different surfaces?
907 */
908 FIXME("(%p) The Surface %p is already attached somewhere else: next_attached = %p, first_attached = %p, can't handle by now\n", This, Surf, Surf->next_attached, Surf->first_attached);
909 LeaveCriticalSection(&ddraw_cs);
910 return DDERR_SURFACEALREADYATTACHED;
911 }
912
913 /* This inserts the new surface at the 2nd position in the chain, right after the root surface */
914 Surf->next_attached = This->next_attached;
915 Surf->first_attached = This->first_attached;
916 This->next_attached = Surf;
917
918 /* Check if the WineD3D depth stencil needs updating */
919 if(This->ddraw->d3ddevice)
920 {
921 IDirect3DDeviceImpl_UpdateDepthStencil(This->ddraw->d3ddevice);
922 }
923
924 /* MSDN:
925 * "This method increments the reference count of the surface being attached."
926 */
927 IDirectDrawSurface7_AddRef(ICOM_INTERFACE(Surf, IDirectDrawSurface7));
928 LeaveCriticalSection(&ddraw_cs);
929 return DD_OK;
930 }
931
932 static HRESULT WINAPI
933