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 assert(This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE);
260 User_copy_to_screen(This,NULL);
262 USER_PRIV_VAR(priv, This);
263 assert(This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE);
264 SetEvent(priv->user.update_event);
268 HRESULT User_DirectDrawSurface_get_dc(IDirectDrawSurfaceImpl* This, HDC* phDC)
270 USER_PRIV_VAR(priv, This);
272 *phDC = priv->user.cached_dc;
276 HRESULT User_DirectDrawSurface_release_dc(IDirectDrawSurfaceImpl* This,
282 HRESULT User_DirectDrawSurface_get_gamma_ramp(IDirectDrawSurfaceImpl* This,
284 LPDDGAMMARAMP lpGammaRamp)
286 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
292 hDisplayWnd = get_display_window(This, &offset);
293 hDisplayDC = GetDCEx(hDisplayWnd, 0, DCX_CLIPSIBLINGS|DCX_CACHE);
294 hr = GetDeviceGammaRamp(hDisplayDC, lpGammaRamp) ? DD_OK : DDERR_UNSUPPORTED;
295 ReleaseDC(hDisplayWnd, hDisplayDC);
298 return Main_DirectDrawSurface_get_gamma_ramp(This, dwFlags, lpGammaRamp);
301 HRESULT User_DirectDrawSurface_set_gamma_ramp(IDirectDrawSurfaceImpl* This,
303 LPDDGAMMARAMP lpGammaRamp)
305 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
311 hDisplayWnd = get_display_window(This, &offset);
312 hDisplayDC = GetDCEx(hDisplayWnd, 0, DCX_CLIPSIBLINGS|DCX_CACHE);
313 hr = SetDeviceGammaRamp(hDisplayDC, lpGammaRamp) ? DD_OK : DDERR_UNSUPPORTED;
314 ReleaseDC(hDisplayWnd, hDisplayDC);
317 return Main_DirectDrawSurface_set_gamma_ramp(This, dwFlags, lpGammaRamp);
320 /* Returns the window that hosts the display.
321 * *pt is set to the upper left position of the window relative to the
322 * upper left corner of the surface. */
323 static HWND get_display_window(IDirectDrawSurfaceImpl* This, LPPOINT pt)
325 memset(pt, 0, sizeof(*pt));
327 if (This->ddraw_owner->cooperative_level & DDSCL_FULLSCREEN)
330 USER_PRIV_VAR(priv, This);
332 SetWindowPos(priv->user.window, HWND_TOP, 0, 0, 0, 0,
333 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|
334 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE);
336 return priv->user.window;
338 return This->ddraw_owner->window;
343 if (This->clipper != NULL)
345 /* looks silly, but since we don't have the clipper locked */
346 HWND hWnd = This->clipper->hWnd;
350 ClientToScreen(hWnd, pt);
355 static BOOL warn = 0;
356 if (!warn++) FIXME("clipper clip lists not supported\n");
358 return GetDesktopWindow();
363 static BOOL warn = 0;
364 if (!warn++) WARN("hosting on root\n");
366 return GetDesktopWindow();
371 HWND User_DirectDrawSurface_get_display_window(IDirectDrawSurfaceImpl* This)
374 return get_display_window(This, &offset);
378 static void User_create_own_window(IDirectDrawSurfaceImpl* This)
380 USER_PRIV_VAR(priv, This);
382 if (This->ddraw_owner->cooperative_level & DDSCL_FULLSCREEN)
384 priv->user.window = CreateWindowExA(WS_EX_TOPMOST |
387 "WINE_DDRAW", "DirectDraw",
390 This->surface_desc.dwWidth,
391 This->surface_desc.dwHeight,
394 This->more.lpDDRAWReserved = (LPVOID)priv->user.window;
395 SetWindowPos(priv->user.window, HWND_TOP, 0, 0, 0, 0,
396 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|
397 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE|SWP_SHOWWINDOW);
398 UpdateWindow(priv->user.window);
402 static void User_destroy_own_window(IDirectDrawSurfaceImpl* This)
404 USER_PRIV_VAR(priv, This);
406 if (priv->user.window)
408 SetWindowPos(priv->user.window, 0, 0, 0, 0, 0,
409 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|SWP_NOZORDER|
410 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE|SWP_HIDEWINDOW);
411 This->more.lpDDRAWReserved = NULL;
412 DestroyWindow(priv->user.window);
413 priv->user.window = 0;
419 static DWORD CALLBACK User_update_thread(LPVOID arg)
421 IDirectDrawSurfaceImpl *This = (IDirectDrawSurfaceImpl *)arg;
422 USER_PRIV_VAR(priv, This);
423 volatile DWORD *pActive = (volatile DWORD *)&priv->user.update_event;
424 HANDLE event = *pActive;
427 User_create_own_window(This);
430 /* the point of this is that many games lock the primary surface
431 * multiple times per frame; this thread will then simply copy as
432 * often as it can without keeping the main thread waiting for
433 * each unlock, thus keeping the frame rate high */
436 DWORD ret = MsgWaitForMultipleObjects(1, &event, FALSE, INFINITE, QS_ALLINPUT);
439 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
441 switch (msg.message) {
443 DispatchMessageA(&msg);
446 /* since we risk getting keyboard messages posted to us,
447 * repost posted messages to cooperative window */
448 PostMessageA(This->ddraw_owner->window, msg.message, msg.wParam, msg.lParam);
452 DWORD ret = WaitForSingleObject(event, INFINITE);
454 if (ret == WAIT_OBJECT_0)
457 User_copy_to_screen(This, NULL);
461 else if (ret != WAIT_OBJECT_0+1) break;
465 User_destroy_own_window(This);
472 static void User_copy_to_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc)
474 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
482 if (FAILED(This->get_dc(This, &hSurfaceDC)))
485 hDisplayWnd = get_display_window(This, &offset);
486 hDisplayDC = GetDCEx(hDisplayWnd, 0, DCX_CLIPSIBLINGS|DCX_CACHE);
488 /* FIXME: this doesn't work... if users really want to run
489 * X in 8bpp, then we need to call directly into display.drv
490 * (or Wine's equivalent), and force a private colormap
491 * without default entries. */
493 SelectPalette(hDisplayDC, This->palette->hpal, FALSE);
494 RealizePalette(hDisplayDC); /* sends messages => deadlocks */
498 drawrect.right = This->surface_desc.dwWidth;
500 drawrect.bottom = This->surface_desc.dwHeight;
504 HWND hwnd = This->clipper->hWnd;
505 if (hwnd && GetWindowRect(hwnd,&xrc)) {
506 /* Do not forget to honor the offset within the clip window. */
507 /* translate the surface to 0.0 of the clip window */
508 OffsetRect(&drawrect,offset.x,offset.y);
509 IntersectRect(&drawrect,&drawrect,&xrc);
510 /* translate it back to its original position */
511 OffsetRect(&drawrect,-offset.x,-offset.y);
515 IntersectRect(&drawrect,&drawrect,rc);
517 /* Only use this if the caller did not pass a rectangle, since
518 * due to double locking this could be the wrong one ... */
519 if (This->lastlockrect.left != This->lastlockrect.right)
520 IntersectRect(&drawrect,&drawrect,&This->lastlockrect);
523 drawrect.left+offset.x, drawrect.top+offset.y,
524 drawrect.right-drawrect.left, drawrect.bottom-drawrect.top,
526 drawrect.left, drawrect.top,
529 ReleaseDC(hDisplayWnd, hDisplayDC);
533 static void User_copy_from_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc)
535 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
538 HWND hDisplayWnd = get_display_window(This, &offset);
539 HDC hDisplayDC = GetDC(hDisplayWnd);
543 drawrect.right = This->surface_desc.dwWidth;
545 drawrect.bottom = This->surface_desc.dwHeight;
547 IntersectRect(&drawrect,&drawrect,rc);
550 drawrect.left, drawrect.top,
551 drawrect.right-drawrect.left, drawrect.bottom-drawrect.top,
553 drawrect.left+offset.x, drawrect.top+offset.y,
556 ReleaseDC(hDisplayWnd, hDisplayDC);
560 static ICOM_VTABLE(IDirectDrawSurface7) User_IDirectDrawSurface7_VTable =
562 Main_DirectDrawSurface_QueryInterface,
563 Main_DirectDrawSurface_AddRef,
564 Main_DirectDrawSurface_Release,
565 Main_DirectDrawSurface_AddAttachedSurface,
566 Main_DirectDrawSurface_AddOverlayDirtyRect,
567 DIB_DirectDrawSurface_Blt,
568 Main_DirectDrawSurface_BltBatch,
569 DIB_DirectDrawSurface_BltFast,
570 Main_DirectDrawSurface_DeleteAttachedSurface,
571 Main_DirectDrawSurface_EnumAttachedSurfaces,
572 Main_DirectDrawSurface_EnumOverlayZOrders,
573 Main_DirectDrawSurface_Flip,
574 Main_DirectDrawSurface_GetAttachedSurface,
575 Main_DirectDrawSurface_GetBltStatus,
576 Main_DirectDrawSurface_GetCaps,
577 Main_DirectDrawSurface_GetClipper,
578 Main_DirectDrawSurface_GetColorKey,
579 Main_DirectDrawSurface_GetDC,
580 Main_DirectDrawSurface_GetFlipStatus,
581 Main_DirectDrawSurface_GetOverlayPosition,
582 Main_DirectDrawSurface_GetPalette,
583 Main_DirectDrawSurface_GetPixelFormat,
584 Main_DirectDrawSurface_GetSurfaceDesc,
585 Main_DirectDrawSurface_Initialize,
586 Main_DirectDrawSurface_IsLost,
587 Main_DirectDrawSurface_Lock,
588 Main_DirectDrawSurface_ReleaseDC,
589 DIB_DirectDrawSurface_Restore,
590 Main_DirectDrawSurface_SetClipper,
591 Main_DirectDrawSurface_SetColorKey,
592 Main_DirectDrawSurface_SetOverlayPosition,
593 Main_DirectDrawSurface_SetPalette,
594 Main_DirectDrawSurface_Unlock,
595 Main_DirectDrawSurface_UpdateOverlay,
596 Main_DirectDrawSurface_UpdateOverlayDisplay,
597 Main_DirectDrawSurface_UpdateOverlayZOrder,
598 Main_DirectDrawSurface_GetDDInterface,
599 Main_DirectDrawSurface_PageLock,
600 Main_DirectDrawSurface_PageUnlock,
601 DIB_DirectDrawSurface_SetSurfaceDesc,
602 Main_DirectDrawSurface_SetPrivateData,
603 Main_DirectDrawSurface_GetPrivateData,
604 Main_DirectDrawSurface_FreePrivateData,
605 Main_DirectDrawSurface_GetUniquenessValue,
606 Main_DirectDrawSurface_ChangeUniquenessValue,
607 Main_DirectDrawSurface_SetPriority,
608 Main_DirectDrawSurface_GetPriority,
609 Main_DirectDrawSurface_SetLOD,
610 Main_DirectDrawSurface_GetLOD