4 * Copyright 1993 Alexandre Julliard
8 * Note: Visible regions of CS_OWNDC/CS_CLASSDC window DCs
9 * have to be updated dynamically.
13 * DCX_DCEBUSY - dce structure is in use
14 * DCX_KEEPCLIPRGN - do not delete clipping region in ReleaseDC
15 * DCX_WINDOWPAINT - BeginPaint specific flag
23 #include "sysmetrics.h"
25 /* #define DEBUG_DC */
28 #define NB_DCE 5 /* Number of DCEs created at startup */
30 static HANDLE firstDCE = 0;
31 static HDC defaultDCstate = 0;
33 BOOL DCHook(HDC, WORD, DWORD, DWORD);
35 /***********************************************************************
40 HANDLE DCE_AllocDCE( HWND hWnd, DCE_TYPE type )
43 HANDLE handle = USER_HEAP_ALLOC( sizeof(DCE) );
44 if (!handle) return 0;
45 dce = (DCE *) USER_HEAP_LIN_ADDR( handle );
46 if (!(dce->hDC = CreateDC( "DISPLAY", NULL, NULL, NULL )))
48 USER_HEAP_FREE( handle );
52 /* store DCE handle in DC hook data field */
54 SetDCHook(dce->hDC, GDI_GetDefDCHook(), MAKELONG(handle,DC_MAGIC));
56 dce->hwndCurrent = hWnd;
57 dce->hNext = firstDCE;
61 if( type != DCE_CACHE_DC )
63 dce->DCXflags = DCX_DCEBUSY;
66 WND* wnd = WIN_FindWndPtr(hWnd);
68 if( wnd->dwStyle & WS_CLIPCHILDREN ) dce->DCXflags |= DCX_CLIPCHILDREN;
69 if( wnd->dwStyle & WS_CLIPSIBLINGS ) dce->DCXflags |= DCX_CLIPSIBLINGS;
71 SetHookFlags(dce->hDC,DCHF_INVALIDATEVISRGN);
73 else dce->DCXflags = DCX_CACHE;
79 /***********************************************************************
82 void DCE_FreeDCE( HANDLE hdce )
85 HANDLE *handle = &firstDCE;
87 if (!(dce = (DCE *) USER_HEAP_LIN_ADDR( hdce ))) return;
88 while (*handle && (*handle != hdce))
90 DCE * prev = (DCE *) USER_HEAP_LIN_ADDR( *handle );
91 handle = &prev->hNext;
93 if (*handle == hdce) *handle = dce->hNext;
95 SetDCHook(dce->hDC,(SEGPTR)NULL,0L);
98 if( dce->hClipRgn && !(dce->DCXflags & DCX_KEEPCLIPRGN) )
99 DeleteObject(dce->hClipRgn);
100 USER_HEAP_FREE( hdce );
103 /***********************************************************************
106 HANDLE DCE_FindDCE(HDC hDC)
108 HANDLE hdce = firstDCE;
113 dce = (DCE *) USER_HEAP_LIN_ADDR(hdce);
114 if( dce->hDC == hDC ) break;
120 /**********************************************************************
121 * WindowFromDC (USER.117) (USER32.580)
123 HWND16 WindowFromDC( HDC32 hDC )
125 HANDLE16 hdce = DCE_FindDCE(hDC);
129 DCE* dce = (DCE *) USER_HEAP_LIN_ADDR(hdce);
130 return dce->hwndCurrent;
136 /***********************************************************************
139 * It is called from SetWindowPos - we have to invalidate all busy
140 * DCE's for windows whose client rect intersects with update rectangle
142 BOOL DCE_InvalidateDCE(WND* wndScope, RECT16* pRectUpdate)
147 if( !wndScope ) return 0;
149 dprintf_dc(stddeb,"InvalidateDCE: scope hwnd = %04x, (%i,%i - %i,%i)\n",
150 wndScope->hwndSelf, pRectUpdate->left,pRectUpdate->top,
151 pRectUpdate->right,pRectUpdate->bottom);
154 for( hdce = firstDCE; (hdce); hdce=dce->hNext)
156 dce = (DCE*)USER_HEAP_LIN_ADDR(hdce);
158 if( dce->DCXflags & DCX_DCEBUSY )
160 WND * wndCurrent, * wnd;
162 wnd = wndCurrent = WIN_FindWndPtr(dce->hwndCurrent);
164 /* desktop is not critical (DC is not owned anyway) */
166 if( wnd == WIN_GetDesktop() ) continue;
168 /* check if DCE window is within z-order scope */
170 for( ; wnd ; wnd = wnd->parent )
171 if( wnd == wndScope )
173 RECT16 wndRect = wndCurrent->rectWindow;
175 dprintf_dc(stddeb,"\tgot hwnd %04x\n", wndCurrent->hwndSelf);
177 MapWindowPoints16(wndCurrent->parent->hwndSelf, wndScope->hwndSelf,
178 (LPPOINT16)&wndRect, 2);
179 if( IntersectRect16(&wndRect,&wndRect,pRectUpdate) )
180 SetHookFlags(dce->hDC, DCHF_INVALIDATEVISRGN);
188 /***********************************************************************
197 for (i = 0; i < NB_DCE; i++)
199 if (!(handle = DCE_AllocDCE( 0, DCE_CACHE_DC ))) return;
200 dce = (DCE *) USER_HEAP_LIN_ADDR( handle );
201 if (!defaultDCstate) defaultDCstate = GetDCState( dce->hDC );
206 /***********************************************************************
209 * Calc the visible rectangle of a window, i.e. the client or
210 * window area clipped by the client area of all ancestors.
211 * Return FALSE if the visible region is empty.
213 static BOOL DCE_GetVisRect( WND *wndPtr, BOOL clientArea, RECT16 *lprect )
215 int xoffset, yoffset;
217 *lprect = clientArea ? wndPtr->rectClient : wndPtr->rectWindow;
218 xoffset = lprect->left;
219 yoffset = lprect->top;
221 if (!(wndPtr->dwStyle & WS_VISIBLE) || (wndPtr->flags & WIN_NO_REDRAW))
223 SetRectEmpty16( lprect ); /* Clip everything */
227 while (wndPtr->parent)
229 wndPtr = wndPtr->parent;
230 if (!(wndPtr->dwStyle & WS_VISIBLE) ||
231 (wndPtr->flags & WIN_NO_REDRAW) ||
232 (wndPtr->dwStyle & WS_ICONIC))
234 SetRectEmpty16( lprect ); /* Clip everything */
237 xoffset += wndPtr->rectClient.left;
238 yoffset += wndPtr->rectClient.top;
239 OffsetRect16( lprect, wndPtr->rectClient.left,
240 wndPtr->rectClient.top );
242 /* Warning!! we assume that IntersectRect() handles the case */
243 /* where the destination is the same as one of the sources. */
244 if (!IntersectRect16( lprect, lprect, &wndPtr->rectClient ))
245 return FALSE; /* Visible rectangle is empty */
247 OffsetRect16( lprect, -xoffset, -yoffset );
252 /***********************************************************************
255 * Go through the linked list of windows from hwndStart to hwndEnd,
256 * removing from the given region the rectangle of each window offset
257 * by a given amount. The new region is returned, and the original one
258 * is destroyed. Used to implement DCX_CLIPSIBLINGS and
259 * DCX_CLIPCHILDREN styles.
261 static HRGN DCE_ClipWindows( WND *pWndStart, WND *pWndEnd,
262 HRGN hrgn, int xoffset, int yoffset )
266 if (!pWndStart) return hrgn;
267 if (!(hrgnNew = CreateRectRgn( 0, 0, 0, 0 )))
269 DeleteObject( hrgn );
272 for (; pWndStart != pWndEnd; pWndStart = pWndStart->next)
274 if (!(pWndStart->dwStyle & WS_VISIBLE)) continue;
275 SetRectRgn( hrgnNew, pWndStart->rectWindow.left + xoffset,
276 pWndStart->rectWindow.top + yoffset,
277 pWndStart->rectWindow.right + xoffset,
278 pWndStart->rectWindow.bottom + yoffset );
279 if (!CombineRgn( hrgn, hrgn, hrgnNew, RGN_DIFF )) break;
281 DeleteObject( hrgnNew );
282 if (pWndStart != pWndEnd) /* something went wrong */
284 DeleteObject( hrgn );
291 /***********************************************************************
294 * Return the visible region of a window, i.e. the client or window area
295 * clipped by the client area of all ancestors, and then optionally
296 * by siblings and children.
298 HRGN DCE_GetVisRgn( HWND hwnd, WORD flags )
302 int xoffset, yoffset;
303 WND *wndPtr = WIN_FindWndPtr( hwnd );
305 /* Get visible rectangle and create a region with it
306 * FIXME: do we really need to calculate vis rgns for X windows?
309 if (!wndPtr || !DCE_GetVisRect( wndPtr, !(flags & DCX_WINDOW), &rect ))
311 return CreateRectRgn( 0, 0, 0, 0 ); /* Visible region is empty */
313 if (!(hrgn = CreateRectRgnIndirect16( &rect ))) return 0;
315 /* Clip all children from the visible region */
317 if (flags & DCX_CLIPCHILDREN)
319 if (flags & DCX_WINDOW)
321 xoffset = wndPtr->rectClient.left - wndPtr->rectWindow.left;
322 yoffset = wndPtr->rectClient.top - wndPtr->rectWindow.top;
324 else xoffset = yoffset = 0;
325 hrgn = DCE_ClipWindows( wndPtr->child, NULL, hrgn, xoffset, yoffset );
329 /* Clip siblings placed above this window */
331 if (flags & DCX_WINDOW)
333 xoffset = -wndPtr->rectWindow.left;
334 yoffset = -wndPtr->rectWindow.top;
338 xoffset = -wndPtr->rectClient.left;
339 yoffset = -wndPtr->rectClient.top;
341 if (flags & DCX_CLIPSIBLINGS)
343 hrgn = DCE_ClipWindows( wndPtr->parent ? wndPtr->parent->child : NULL,
344 wndPtr, hrgn, xoffset, yoffset );
348 /* Clip siblings of all ancestors that have the WS_CLIPSIBLINGS style */
350 while (wndPtr->dwStyle & WS_CHILD)
352 wndPtr = wndPtr->parent;
353 xoffset -= wndPtr->rectClient.left;
354 yoffset -= wndPtr->rectClient.top;
355 hrgn = DCE_ClipWindows( wndPtr->parent->child, wndPtr,
356 hrgn, xoffset, yoffset );
363 /***********************************************************************
366 * Set the drawable, origin and dimensions for the DC associated to
369 static void DCE_SetDrawable( WND *wndPtr, DC *dc, WORD flags )
371 if (!wndPtr) /* Get a DC for the whole screen */
375 dc->u.x.drawable = rootWindow;
376 XSetSubwindowMode( display, dc->u.x.gc, IncludeInferiors );
380 if (flags & DCX_WINDOW)
382 dc->w.DCOrgX = wndPtr->rectWindow.left;
383 dc->w.DCOrgY = wndPtr->rectWindow.top;
387 dc->w.DCOrgX = wndPtr->rectClient.left;
388 dc->w.DCOrgY = wndPtr->rectClient.top;
390 while (!wndPtr->window)
392 wndPtr = wndPtr->parent;
393 dc->w.DCOrgX += wndPtr->rectClient.left;
394 dc->w.DCOrgY += wndPtr->rectClient.top;
396 dc->w.DCOrgX -= wndPtr->rectWindow.left;
397 dc->w.DCOrgY -= wndPtr->rectWindow.top;
398 dc->u.x.drawable = wndPtr->window;
402 /***********************************************************************
405 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
407 HDC GetDCEx( HWND hwnd, HRGN hrgnClip, DWORD flags )
416 BOOL need_update = TRUE;
418 dprintf_dc(stddeb,"GetDCEx: hwnd %04x, hrgnClip %04x, flags %08x\n", hwnd, hrgnClip, (unsigned)flags);
420 if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
422 if (flags & DCX_USESTYLE)
424 flags &= ~( DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
426 if( wndPtr->dwStyle & WS_CLIPSIBLINGS )
427 flags |= DCX_CLIPSIBLINGS;
429 if ( !(flags & DCX_WINDOW) )
431 if (!(wndPtr->class->style & (CS_OWNDC | CS_CLASSDC)))
434 if (wndPtr->class->style & CS_PARENTDC) flags |= DCX_PARENTCLIP;
436 if (wndPtr->dwStyle & WS_CLIPCHILDREN &&
437 !(wndPtr->dwStyle & WS_MINIMIZE) ) flags |= DCX_CLIPCHILDREN;
439 else flags |= DCX_CACHE;
442 if( flags & DCX_NOCLIPCHILDREN )
445 flags &= ~(DCX_PARENTCLIP | DCX_CLIPCHILDREN);
448 if (hwnd==GetDesktopWindow() || !(wndPtr->dwStyle & WS_CHILD)) flags &= ~DCX_PARENTCLIP;
450 if (flags & DCX_WINDOW) flags = (flags & ~DCX_CLIPCHILDREN) | DCX_CACHE;
452 if( flags & DCX_PARENTCLIP )
455 if( !(flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) )
456 if( (wndPtr->dwStyle & WS_VISIBLE) && (wndPtr->parent->dwStyle & WS_VISIBLE) )
458 flags &= ~DCX_CLIPCHILDREN;
459 if( wndPtr->parent->dwStyle & WS_CLIPSIBLINGS )
460 flags |= DCX_CLIPSIBLINGS;
464 if (flags & DCX_CACHE)
466 for (hdce = firstDCE; (hdce); hdce = dce->hNext)
468 if (!(dce = (DCE *) USER_HEAP_LIN_ADDR( hdce ))) return 0;
469 if ((dce->DCXflags & DCX_CACHE) && !(dce->DCXflags & DCX_DCEBUSY)) break;
474 hdce = (wndPtr->class->style & CS_OWNDC)?wndPtr->hdce:wndPtr->class->hdce;
475 dce = (DCE *) USER_HEAP_LIN_ADDR( hdce );
477 if( dce->hwndCurrent == hwnd )
479 dprintf_dc(stddeb,"\tskipping hVisRgn update\n");
483 if( hrgnClip && dce->hClipRgn && !(dce->DCXflags & DCX_KEEPCLIPRGN))
485 fprintf(stdnimp,"GetDCEx: hClipRgn collision!\n");
486 DeleteObject(dce->hClipRgn);
491 dcx_flags = flags & ( DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_CACHE | DCX_WINDOW | DCX_WINDOWPAINT);
494 dce = (DCE *) USER_HEAP_LIN_ADDR( hdce );
495 dce->hwndCurrent = hwnd;
497 dce->DCXflags = dcx_flags | DCX_DCEBUSY;
500 if (!(dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC ))) return 0;
502 DCE_SetDrawable( wndPtr, dc, flags );
503 if( need_update || dc->w.flags & DC_DIRTY )
505 dprintf_dc(stddeb,"updating hDC anyway\n");
507 if (flags & DCX_PARENTCLIP)
509 WND *parentPtr = wndPtr->parent;
510 dcx_flags = flags & ~(DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN |
512 if (parentPtr->dwStyle & WS_CLIPSIBLINGS)
513 dcx_flags |= DCX_CLIPSIBLINGS;
514 hrgnVisible = DCE_GetVisRgn( parentPtr->hwndSelf, dcx_flags );
515 if (flags & DCX_WINDOW)
516 OffsetRgn( hrgnVisible, -wndPtr->rectWindow.left,
517 -wndPtr->rectWindow.top );
518 else OffsetRgn( hrgnVisible, -wndPtr->rectClient.left,
519 -wndPtr->rectClient.top );
521 /* optimize away GetVisRgn for desktop if it isn't there */
523 else if ((hwnd == GetDesktopWindow()) &&
524 (rootWindow == DefaultRootWindow(display)))
525 hrgnVisible = CreateRectRgn( 0, 0, SYSMETRICS_CXSCREEN,
526 SYSMETRICS_CYSCREEN);
527 else hrgnVisible = DCE_GetVisRgn( hwnd, flags );
529 dc->w.flags &= ~DC_DIRTY;
531 SelectVisRgn( hdc, hrgnVisible );
533 else hrgnVisible = CreateRectRgn(0,0,0,0);
535 if ((flags & DCX_INTERSECTRGN) || (flags & DCX_EXCLUDERGN))
537 dce->DCXflags |= flags & (DCX_KEEPCLIPRGN | DCX_INTERSECTRGN | DCX_EXCLUDERGN);
538 dce->hClipRgn = hrgnClip;
540 dprintf_dc(stddeb, "\tsaved VisRgn, clipRgn = %04x\n", hrgnClip);
543 CombineRgn( hrgnVisible, InquireVisRgn( hdc ), hrgnClip,
544 (flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF );
545 SelectVisRgn( hdc, hrgnVisible );
547 DeleteObject( hrgnVisible );
549 dprintf_dc(stddeb, "GetDCEx(%04x,%04x,0x%lx): returning %04x\n",
550 hwnd, hrgnClip, flags, hdc);
554 /***********************************************************************
557 HDC GetDC( HWND hwnd )
559 if( !hwnd ) return GetDCEx( GetDesktopWindow(), 0, DCX_CACHE | DCX_WINDOW );
561 return GetDCEx( hwnd, 0, DCX_USESTYLE );
565 /***********************************************************************
566 * GetWindowDC (USER.67)
568 HDC GetWindowDC( HWND hwnd )
573 if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
575 else hwnd = GetDesktopWindow();
577 return GetDCEx( hwnd, 0, DCX_USESTYLE | DCX_WINDOW );
581 /***********************************************************************
582 * ReleaseDC (USER.68)
584 int ReleaseDC( HWND hwnd, HDC hdc )
589 dprintf_dc(stddeb, "ReleaseDC: %04x %04x\n", hwnd, hdc );
591 for (hdce = firstDCE; (hdce); hdce = dce->hNext)
593 if (!(dce = (DCE *) USER_HEAP_LIN_ADDR( hdce ))) return 0;
594 if (dce->hDC == hdc) break;
597 if (!(dce->DCXflags & DCX_DCEBUSY) ) return 0;
599 /* restore previous visible region */
601 if ( dce->DCXflags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN) &&
602 (dce->DCXflags & DCX_CACHE || dce->DCXflags & DCX_WINDOWPAINT) )
604 dprintf_dc(stddeb,"\tcleaning up visrgn...\n");
605 dce->DCXflags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN | DCX_WINDOWPAINT);
607 if( dce->DCXflags & DCX_KEEPCLIPRGN )
608 dce->DCXflags &= ~DCX_KEEPCLIPRGN;
610 if( dce->hClipRgn > 1 )
611 DeleteObject( dce->hClipRgn );
614 RestoreVisRgn(dce->hDC);
617 if (dce->DCXflags & DCX_CACHE)
619 SetDCState( dce->hDC, defaultDCstate );
620 dce->DCXflags = DCX_CACHE;
621 dce->hwndCurrent = 0;
626 /***********************************************************************
629 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
631 BOOL DCHook(HDC hDC, WORD code, DWORD data, DWORD lParam)
636 dprintf_dc(stddeb,"DCHook: hDC = %04x, %i\n", hDC, code);
638 if( HIWORD(data) == DC_MAGIC )
639 hdce = (HANDLE)LOWORD(data);
641 hdce = DCE_FindDCE(hDC);
643 if( !hdce ) return 0;
647 case DCHC_INVALIDVISRGN:
649 DCE* dce = (DCE*) USER_HEAP_LIN_ADDR(hdce);
651 if( dce->DCXflags & DCX_DCEBUSY )
653 SetHookFlags(hDC, DCHF_VALIDATEVISRGN);
654 hVisRgn = DCE_GetVisRgn(dce->hwndCurrent, dce->DCXflags);
656 dprintf_dc(stddeb,"\tapplying saved clipRgn\n");
658 /* clip this region with saved clipping region */
660 if ( (dce->DCXflags & DCX_INTERSECTRGN && dce->hClipRgn != 1) ||
661 ( dce->DCXflags & DCX_EXCLUDERGN && dce->hClipRgn) )
664 if( (!dce->hClipRgn && dce->DCXflags & DCX_INTERSECTRGN) ||
665 (dce->hClipRgn == 1 && dce->DCXflags & DCX_EXCLUDERGN) )
666 SetRectRgn(hVisRgn,0,0,0,0);
668 CombineRgn(hVisRgn, hVisRgn, dce->hClipRgn,
669 (dce->DCXflags & DCX_EXCLUDERGN)? RGN_DIFF:RGN_AND);
671 SelectVisRgn(hDC, hVisRgn);
672 DeleteObject(hVisRgn);
675 dprintf_dc(stddeb,"DCHook: DC is not in use!\n");
679 case DCHC_DELETEDC: /* FIXME: ?? */
683 fprintf(stdnimp,"DCHook: unknown code\n");