1 /* User-based primary surface driver
3 * Copyright 2000-2001 TransGaming Technologies Inc.
13 #include "debugtools.h"
14 #include "ddraw_private.h"
15 #include "dsurface/main.h"
16 #include "dsurface/dib.h"
17 #include "dsurface/user.h"
18 #include "dsurface/wndproc.h"
20 DEFAULT_DEBUG_CHANNEL(ddraw);
22 /* if you use OWN_WINDOW, don't use SYNC_UPDATE, or you may get trouble */
23 /* #define SYNC_UPDATE */
27 static void User_create_own_window(IDirectDrawSurfaceImpl* This);
28 static void User_destroy_own_window(IDirectDrawSurfaceImpl* This);
31 static DWORD CALLBACK User_update_thread(LPVOID);
33 static void User_copy_to_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc);
34 static void User_copy_from_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc);
36 static HWND get_display_window(IDirectDrawSurfaceImpl* This, LPPOINT pt);
38 static ICOM_VTABLE(IDirectDrawSurface7) User_IDirectDrawSurface7_VTable;
41 User_DirectDrawSurface_Construct(IDirectDrawSurfaceImpl* This,
43 const DDSURFACEDESC2* pDDSD)
45 USER_PRIV_VAR(priv, This);
48 TRACE("(%p,%p,%p)\n",This,pDD,pDDSD);
49 hr = DIB_DirectDrawSurface_Construct(This, pDD, pDDSD);
50 if (FAILED(hr)) return hr;
52 ICOM_INIT_INTERFACE(This, IDirectDrawSurface7,
53 User_IDirectDrawSurface7_VTable);
55 This->final_release = User_DirectDrawSurface_final_release;
56 This->duplicate_surface = User_DirectDrawSurface_duplicate_surface;
58 This->lock_update = User_DirectDrawSurface_lock_update;
59 This->unlock_update = User_DirectDrawSurface_unlock_update;
61 This->flip_data = User_DirectDrawSurface_flip_data;
62 This->flip_update = User_DirectDrawSurface_flip_update;
64 This->get_dc = User_DirectDrawSurface_get_dc;
65 This->release_dc = User_DirectDrawSurface_release_dc;
67 This->set_palette = User_DirectDrawSurface_set_palette;
68 This->update_palette = User_DirectDrawSurface_update_palette;
70 This->get_gamma_ramp = User_DirectDrawSurface_get_gamma_ramp;
71 This->set_gamma_ramp = User_DirectDrawSurface_set_gamma_ramp;
73 This->get_display_window = User_DirectDrawSurface_get_display_window;
75 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
78 DirectDrawSurface_RegisterClass();
81 priv->user.update_event = CreateEventA(NULL, FALSE, FALSE, NULL);
82 priv->user.update_thread = CreateThread(NULL, 0, User_update_thread, This, 0, NULL);
84 if (This->ddraw_owner->cooperative_level & DDSCL_FULLSCREEN) {
85 /* wait for window creation (or update thread destruction) */
86 while (WaitForMultipleObjects(1, &priv->user.update_thread, FALSE, 10) == WAIT_TIMEOUT)
87 if (This->more.lpDDRAWReserved) break;
88 if (!This->more.lpDDRAWReserved) {
89 ERR("window creation failed\n");
95 User_create_own_window(This);
98 if (!This->more.lpDDRAWReserved)
99 This->more.lpDDRAWReserved = (LPVOID)pDD->window;
102 return DIB_DirectDrawSurface_alloc_dc(This, &priv->user.cached_dc);
106 User_DirectDrawSurface_Create(IDirectDrawImpl *pDD,
107 const DDSURFACEDESC2 *pDDSD,
108 LPDIRECTDRAWSURFACE7 *ppSurf,
111 IDirectDrawSurfaceImpl* This;
113 assert(pUnkOuter == NULL);
115 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
116 sizeof(*This) + sizeof(User_DirectDrawSurfaceImpl));
117 if (This == NULL) return E_OUTOFMEMORY;
119 This->private = (User_DirectDrawSurfaceImpl*)(This+1);
121 hr = User_DirectDrawSurface_Construct(This, pDD, pDDSD);
123 HeapFree(GetProcessHeap(), 0, This);
125 *ppSurf = ICOM_INTERFACE(This, IDirectDrawSurface7);
130 void User_DirectDrawSurface_final_release(IDirectDrawSurfaceImpl* This)
132 USER_PRIV_VAR(priv, This);
134 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
137 HANDLE event = priv->user.update_event;
138 priv->user.update_event = 0;
140 TRACE("waiting for update thread to terminate...\n");
142 /* dispatch any waiting sendmessages */
145 PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE);
147 /* to avoid deadlocks, allow SendMessages from update thread
148 * through while we wait for it */
149 while (MsgWaitForMultipleObjects(1, &priv->user.update_thread, FALSE,
150 INFINITE, QS_SENDMESSAGE) == WAIT_OBJECT_0+1)
153 PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE);
156 WaitForSingleObject(priv->user.update_thread,INFINITE);
158 TRACE("update thread terminated\n");
159 CloseHandle(priv->user.update_thread);
162 User_destroy_own_window(This);
165 This->more.lpDDRAWReserved = 0;
167 DirectDrawSurface_UnregisterClass();
170 DIB_DirectDrawSurface_free_dc(This, priv->user.cached_dc);
171 DIB_DirectDrawSurface_final_release(This);
174 void User_DirectDrawSurface_lock_update(IDirectDrawSurfaceImpl* This,
175 LPCRECT pRect, DWORD dwFlags)
177 if (!(dwFlags & DDLOCK_WRITEONLY))
178 User_copy_from_screen(This, pRect);
181 This->lastlockrect = *pRect;
183 This->lastlockrect.left = This->lastlockrect.right = 0;
187 void User_DirectDrawSurface_unlock_update(IDirectDrawSurfaceImpl* This,
191 User_copy_to_screen(This, pRect);
193 USER_PRIV_VAR(priv, This);
194 SetEvent(priv->user.update_event);
198 void User_DirectDrawSurface_set_palette(IDirectDrawSurfaceImpl* This,
199 IDirectDrawPaletteImpl* pal)
201 USER_PRIV_VAR(priv, This);
204 FIXME("selecting null palette\n");
205 /* I don't think selecting GDI object 0 will work, we should select
206 * the original palette, returned by the first SelectPalette */
207 SelectPalette(priv->user.cached_dc, 0, FALSE);
211 SelectPalette(priv->user.cached_dc, pal->hpal, FALSE);
213 DIB_DirectDrawSurface_set_palette(This, pal);
216 void User_DirectDrawSurface_update_palette(IDirectDrawSurfaceImpl* This,
217 IDirectDrawPaletteImpl* pal,
218 DWORD dwStart, DWORD dwCount,
219 LPPALETTEENTRY palent)
221 USER_PRIV_VAR(priv, This);
223 DIB_DirectDrawSurface_update_palette(This, pal, dwStart, dwCount, palent);
224 /* FIXME: realize palette on display window */
227 /* with async updates, it's probably cool to force an update */
228 SetEvent(priv->user.update_event);
232 HRESULT User_DirectDrawSurface_duplicate_surface(IDirectDrawSurfaceImpl* This,
233 LPDIRECTDRAWSURFACE7* ppDup)
235 return User_DirectDrawSurface_Create(This->ddraw_owner,
236 &This->surface_desc, ppDup, NULL);
239 BOOL User_DirectDrawSurface_flip_data(IDirectDrawSurfaceImpl* front,
240 IDirectDrawSurfaceImpl* back,
243 USER_PRIV_VAR(front_priv, front);
244 USER_PRIV_VAR(back_priv, back);
248 tmp = front_priv->user.cached_dc;
249 front_priv->user.cached_dc = back_priv->user.cached_dc;
250 back_priv->user.cached_dc = tmp;
253 return DIB_DirectDrawSurface_flip_data(front, back, dwFlags);
256 void User_DirectDrawSurface_flip_update(IDirectDrawSurfaceImpl* This, DWORD dwFlags)
259 This->lastlockrect.left = This->lastlockrect.right = 0;
260 assert(This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE);
261 User_copy_to_screen(This,NULL);
263 USER_PRIV_VAR(priv, This);
264 This->lastlockrect.left = This->lastlockrect.right = 0;
265 assert(This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE);
266 SetEvent(priv->user.update_event);
270 HRESULT User_DirectDrawSurface_get_dc(IDirectDrawSurfaceImpl* This, HDC* phDC)
272 USER_PRIV_VAR(priv, This);
274 *phDC = priv->user.cached_dc;
278 HRESULT User_DirectDrawSurface_release_dc(IDirectDrawSurfaceImpl* This,
284 HRESULT User_DirectDrawSurface_get_gamma_ramp(IDirectDrawSurfaceImpl* This,
286 LPDDGAMMARAMP lpGammaRamp)
288 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
294 hDisplayWnd = get_display_window(This, &offset);
295 hDisplayDC = GetDCEx(hDisplayWnd, 0, DCX_CLIPSIBLINGS|DCX_CACHE);
296 hr = GetDeviceGammaRamp(hDisplayDC, lpGammaRamp) ? DD_OK : DDERR_UNSUPPORTED;
297 ReleaseDC(hDisplayWnd, hDisplayDC);
300 return Main_DirectDrawSurface_get_gamma_ramp(This, dwFlags, lpGammaRamp);
303 HRESULT User_DirectDrawSurface_set_gamma_ramp(IDirectDrawSurfaceImpl* This,
305 LPDDGAMMARAMP lpGammaRamp)
307 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
313 hDisplayWnd = get_display_window(This, &offset);
314 hDisplayDC = GetDCEx(hDisplayWnd, 0, DCX_CLIPSIBLINGS|DCX_CACHE);
315 hr = SetDeviceGammaRamp(hDisplayDC, lpGammaRamp) ? DD_OK : DDERR_UNSUPPORTED;
316 ReleaseDC(hDisplayWnd, hDisplayDC);
319 return Main_DirectDrawSurface_set_gamma_ramp(This, dwFlags, lpGammaRamp);
322 /* Returns the window that hosts the display.
323 * *pt is set to the upper left position of the window relative to the
324 * upper left corner of the surface. */
325 static HWND get_display_window(IDirectDrawSurfaceImpl* This, LPPOINT pt)
327 memset(pt, 0, sizeof(*pt));
329 if (This->ddraw_owner->cooperative_level & DDSCL_FULLSCREEN)
332 USER_PRIV_VAR(priv, This);
334 SetWindowPos(priv->user.window, HWND_TOP, 0, 0, 0, 0,
335 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|
336 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE);
338 return priv->user.window;
340 return This->ddraw_owner->window;
345 if (This->clipper != NULL)
347 /* looks silly, but since we don't have the clipper locked */
348 HWND hWnd = This->clipper->hWnd;
352 ClientToScreen(hWnd, pt);
357 static BOOL warn = 0;
358 if (!warn++) FIXME("clipper clip lists not supported\n");
360 return GetDesktopWindow();
365 static BOOL warn = 0;
366 if (!warn++) WARN("hosting on root\n");
368 return GetDesktopWindow();
373 HWND User_DirectDrawSurface_get_display_window(IDirectDrawSurfaceImpl* This)
376 return get_display_window(This, &offset);
380 static void User_create_own_window(IDirectDrawSurfaceImpl* This)
382 USER_PRIV_VAR(priv, This);
384 if (This->ddraw_owner->cooperative_level & DDSCL_FULLSCREEN)
386 priv->user.window = CreateWindowExA(WS_EX_TOPMOST |
389 "WINE_DDRAW", "DirectDraw",
392 This->surface_desc.dwWidth,
393 This->surface_desc.dwHeight,
396 This->more.lpDDRAWReserved = (LPVOID)priv->user.window;
397 SetWindowPos(priv->user.window, HWND_TOP, 0, 0, 0, 0,
398 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|
399 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE|SWP_SHOWWINDOW);
400 UpdateWindow(priv->user.window);
404 static void User_destroy_own_window(IDirectDrawSurfaceImpl* This)
406 USER_PRIV_VAR(priv, This);
408 if (priv->user.window)
410 SetWindowPos(priv->user.window, 0, 0, 0, 0, 0,
411 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|SWP_NOZORDER|
412 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE|SWP_HIDEWINDOW);
413 This->more.lpDDRAWReserved = NULL;
414 DestroyWindow(priv->user.window);
415 priv->user.window = 0;
421 static DWORD CALLBACK User_update_thread(LPVOID arg)
423 IDirectDrawSurfaceImpl *This = (IDirectDrawSurfaceImpl *)arg;
424 USER_PRIV_VAR(priv, This);
425 volatile DWORD *pActive = (volatile DWORD *)&priv->user.update_event;
426 HANDLE event = *pActive;
429 User_create_own_window(This);
432 /* the point of this is that many games lock the primary surface
433 * multiple times per frame; this thread will then simply copy as
434 * often as it can without keeping the main thread waiting for
435 * each unlock, thus keeping the frame rate high */
438 DWORD ret = MsgWaitForMultipleObjects(1, &event, FALSE, INFINITE, QS_ALLINPUT);
441 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
443 switch (msg.message) {
445 DispatchMessageA(&msg);
448 /* since we risk getting keyboard messages posted to us,
449 * repost posted messages to cooperative window */
450 PostMessageA(This->ddraw_owner->window, msg.message, msg.wParam, msg.lParam);
454 DWORD ret = WaitForSingleObject(event, INFINITE);
456 if (ret == WAIT_OBJECT_0)
459 User_copy_to_screen(This, NULL);
463 else if (ret != WAIT_OBJECT_0+1) break;
467 User_destroy_own_window(This);
474 static void User_copy_to_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc)
476 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
484 if (FAILED(This->get_dc(This, &hSurfaceDC)))
487 hDisplayWnd = get_display_window(This, &offset);
488 hDisplayDC = GetDCEx(hDisplayWnd, 0, DCX_CLIPSIBLINGS|DCX_CACHE);
490 /* FIXME: this doesn't work... if users really want to run
491 * X in 8bpp, then we need to call directly into display.drv
492 * (or Wine's equivalent), and force a private colormap
493 * without default entries. */
495 SelectPalette(hDisplayDC, This->palette->hpal, FALSE);
496 RealizePalette(hDisplayDC); /* sends messages => deadlocks */
500 drawrect.right = This->surface_desc.dwWidth;
502 drawrect.bottom = This->surface_desc.dwHeight;
506 HWND hwnd = This->clipper->hWnd;
507 if (hwnd && GetWindowRect(hwnd,&xrc)) {
508 /* Do not forget to honor the offset within the clip window. */
509 /* translate the surface to 0.0 of the clip window */
510 OffsetRect(&drawrect,offset.x,offset.y);
511 IntersectRect(&drawrect,&drawrect,&xrc);
512 /* translate it back to its original position */
513 OffsetRect(&drawrect,-offset.x,-offset.y);
517 IntersectRect(&drawrect,&drawrect,rc);
519 /* Only use this if the caller did not pass a rectangle, since
520 * due to double locking this could be the wrong one ... */
521 if (This->lastlockrect.left != This->lastlockrect.right)
522 IntersectRect(&drawrect,&drawrect,&This->lastlockrect);
525 drawrect.left+offset.x, drawrect.top+offset.y,
526 drawrect.right-drawrect.left, drawrect.bottom-drawrect.top,
528 drawrect.left, drawrect.top,
531 ReleaseDC(hDisplayWnd, hDisplayDC);
535 static void User_copy_from_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc)
537 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
540 HWND hDisplayWnd = get_display_window(This, &offset);
541 HDC hDisplayDC = GetDC(hDisplayWnd);
545 drawrect.right = This->surface_desc.dwWidth;
547 drawrect.bottom = This->surface_desc.dwHeight;
549 IntersectRect(&drawrect,&drawrect,rc);
552 drawrect.left, drawrect.top,
553 drawrect.right-drawrect.left, drawrect.bottom-drawrect.top,
555 drawrect.left+offset.x, drawrect.top+offset.y,
558 ReleaseDC(hDisplayWnd, hDisplayDC);
562 static ICOM_VTABLE(IDirectDrawSurface7) User_IDirectDrawSurface7_VTable =
564 Main_DirectDrawSurface_QueryInterface,
565 Main_DirectDrawSurface_AddRef,
566 Main_DirectDrawSurface_Release,
567 Main_DirectDrawSurface_AddAttachedSurface,
568 Main_DirectDrawSurface_AddOverlayDirtyRect,
569 DIB_DirectDrawSurface_Blt,
570 Main_DirectDrawSurface_BltBatch,
571 DIB_DirectDrawSurface_BltFast,
572 Main_DirectDrawSurface_DeleteAttachedSurface,
573 Main_DirectDrawSurface_EnumAttachedSurfaces,
574 Main_DirectDrawSurface_EnumOverlayZOrders,
575 Main_DirectDrawSurface_Flip,
576 Main_DirectDrawSurface_GetAttachedSurface,
577 Main_DirectDrawSurface_GetBltStatus,
578 Main_DirectDrawSurface_GetCaps,
579 Main_DirectDrawSurface_GetClipper,
580 Main_DirectDrawSurface_GetColorKey,
581 Main_DirectDrawSurface_GetDC,
582 Main_DirectDrawSurface_GetFlipStatus,
583 Main_DirectDrawSurface_GetOverlayPosition,
584 Main_DirectDrawSurface_GetPalette,
585 Main_DirectDrawSurface_GetPixelFormat,
586 Main_DirectDrawSurface_GetSurfaceDesc,
587 Main_DirectDrawSurface_Initialize,
588 Main_DirectDrawSurface_IsLost,
589 Main_DirectDrawSurface_Lock,
590 Main_DirectDrawSurface_ReleaseDC,
591 DIB_DirectDrawSurface_Restore,
592 Main_DirectDrawSurface_SetClipper,
593 Main_DirectDrawSurface_SetColorKey,
594 Main_DirectDrawSurface_SetOverlayPosition,
595 Main_DirectDrawSurface_SetPalette,
596 Main_DirectDrawSurface_Unlock,
597 Main_DirectDrawSurface_UpdateOverlay,
598 Main_DirectDrawSurface_UpdateOverlayDisplay,
599 Main_DirectDrawSurface_UpdateOverlayZOrder,
600 Main_DirectDrawSurface_GetDDInterface,
601 Main_DirectDrawSurface_PageLock,
602 Main_DirectDrawSurface_PageUnlock,
603 DIB_DirectDrawSurface_SetSurfaceDesc,
604 Main_DirectDrawSurface_SetPrivateData,
605 Main_DirectDrawSurface_GetPrivateData,
606 Main_DirectDrawSurface_FreePrivateData,
607 Main_DirectDrawSurface_GetUniquenessValue,
608 Main_DirectDrawSurface_ChangeUniquenessValue,
609 Main_DirectDrawSurface_SetPriority,
610 Main_DirectDrawSurface_GetPriority,
611 Main_DirectDrawSurface_SetLOD,
612 Main_DirectDrawSurface_GetLOD