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 DCE
*firstDCE
= 0;
31 static HDC defaultDCstate
= 0;
33 BOOL
DCHook(HDC
, WORD
, DWORD
, DWORD
);
35 /***********************************************************************
40 DCE
*DCE_AllocDCE( HWND32 hWnd
, DCE_TYPE type
)
43 if (!(dce
= HeapAlloc( SystemHeap
, 0, sizeof(DCE
) ))) return NULL
;
44 if (!(dce
->hDC
= CreateDC( "DISPLAY", NULL
, NULL
, NULL
)))
46 HeapFree( SystemHeap
, 0, dce
);
50 /* store DCE handle in DC hook data field */
52 SetDCHook(dce
->hDC
, GDI_GetDefDCHook(), (DWORD
)dce
);
54 dce
->hwndCurrent
= hWnd
;
59 if( type
!= DCE_CACHE_DC
)
61 dce
->DCXflags
= DCX_DCEBUSY
;
64 WND
* wnd
= WIN_FindWndPtr(hWnd
);
66 if( wnd
->dwStyle
& WS_CLIPCHILDREN
) dce
->DCXflags
|= DCX_CLIPCHILDREN
;
67 if( wnd
->dwStyle
& WS_CLIPSIBLINGS
) dce
->DCXflags
|= DCX_CLIPSIBLINGS
;
69 SetHookFlags(dce
->hDC
,DCHF_INVALIDATEVISRGN
);
71 else dce
->DCXflags
= DCX_CACHE
;
77 /***********************************************************************
80 void DCE_FreeDCE( DCE
*dce
)
82 DCE
**ppDCE
= &firstDCE
;
85 while (*ppDCE
&& (*ppDCE
!= dce
)) ppDCE
= &(*ppDCE
)->next
;
86 if (*ppDCE
== dce
) *ppDCE
= dce
->next
;
88 SetDCHook(dce
->hDC
,(SEGPTR
)NULL
,0L);
91 if( dce
->hClipRgn
&& !(dce
->DCXflags
& DCX_KEEPCLIPRGN
) )
92 DeleteObject(dce
->hClipRgn
);
93 HeapFree( SystemHeap
, 0, dce
);
97 /**********************************************************************
98 * WindowFromDC16 (USER32.580)
100 HWND16
WindowFromDC16( HDC16 hDC
)
102 return (HWND16
)WindowFromDC32( hDC
);
106 /**********************************************************************
107 * WindowFromDC32 (USER32.580)
109 HWND32
WindowFromDC32( HDC32 hDC
)
112 while (dce
&& (dce
->hDC
!= hDC
)) dce
= dce
->next
;
113 return dce
? dce
->hwndCurrent
: 0;
117 /***********************************************************************
120 * It is called from SetWindowPos - we have to invalidate all busy
121 * DCE's for windows whose client rect intersects with update rectangle
123 BOOL32
DCE_InvalidateDCE(WND
* wndScope
, RECT16
* pRectUpdate
)
128 if( !wndScope
) return 0;
130 dprintf_dc(stddeb
,"InvalidateDCE: scope hwnd = %04x, (%i,%i - %i,%i)\n",
131 wndScope
->hwndSelf
, pRectUpdate
->left
,pRectUpdate
->top
,
132 pRectUpdate
->right
,pRectUpdate
->bottom
);
135 for (dce
= firstDCE
; (dce
); dce
= dce
->next
)
137 if( dce
->DCXflags
& DCX_DCEBUSY
)
139 WND
* wndCurrent
, * wnd
;
141 wnd
= wndCurrent
= WIN_FindWndPtr(dce
->hwndCurrent
);
143 /* desktop is not critical (DC is not owned anyway) */
145 if( wnd
== WIN_GetDesktop() ) continue;
147 /* check if DCE window is within z-order scope */
149 for( ; wnd
; wnd
= wnd
->parent
)
150 if( wnd
== wndScope
)
152 RECT16 wndRect
= wndCurrent
->rectWindow
;
154 dprintf_dc(stddeb
,"\tgot hwnd %04x\n", wndCurrent
->hwndSelf
);
156 MapWindowPoints16(wndCurrent
->parent
->hwndSelf
, wndScope
->hwndSelf
,
157 (LPPOINT16
)&wndRect
, 2);
158 if( IntersectRect16(&wndRect
,&wndRect
,pRectUpdate
) )
160 SetHookFlags(dce
->hDC
, DCHF_INVALIDATEVISRGN
);
170 /***********************************************************************
178 for (i
= 0; i
< NB_DCE
; i
++)
180 if (!(dce
= DCE_AllocDCE( 0, DCE_CACHE_DC
))) return;
181 if (!defaultDCstate
) defaultDCstate
= GetDCState( dce
->hDC
);
186 /***********************************************************************
189 * Calc the visible rectangle of a window, i.e. the client or
190 * window area clipped by the client area of all ancestors.
191 * Return FALSE if the visible region is empty.
193 static BOOL
DCE_GetVisRect( WND
*wndPtr
, BOOL clientArea
, RECT16
*lprect
)
195 int xoffset
, yoffset
;
197 *lprect
= clientArea
? wndPtr
->rectClient
: wndPtr
->rectWindow
;
198 xoffset
= lprect
->left
;
199 yoffset
= lprect
->top
;
201 if (!(wndPtr
->dwStyle
& WS_VISIBLE
) || (wndPtr
->flags
& WIN_NO_REDRAW
))
203 SetRectEmpty16( lprect
); /* Clip everything */
207 while (wndPtr
->parent
)
209 wndPtr
= wndPtr
->parent
;
210 if (!(wndPtr
->dwStyle
& WS_VISIBLE
) ||
211 (wndPtr
->flags
& WIN_NO_REDRAW
) ||
212 (wndPtr
->dwStyle
& WS_ICONIC
))
214 SetRectEmpty16( lprect
); /* Clip everything */
217 xoffset
+= wndPtr
->rectClient
.left
;
218 yoffset
+= wndPtr
->rectClient
.top
;
219 OffsetRect16( lprect
, wndPtr
->rectClient
.left
,
220 wndPtr
->rectClient
.top
);
222 /* Warning!! we assume that IntersectRect() handles the case */
223 /* where the destination is the same as one of the sources. */
224 if (!IntersectRect16( lprect
, lprect
, &wndPtr
->rectClient
))
225 return FALSE
; /* Visible rectangle is empty */
227 OffsetRect16( lprect
, -xoffset
, -yoffset
);
232 /***********************************************************************
235 * Go through the linked list of windows from hwndStart to hwndEnd,
236 * removing from the given region the rectangle of each window offset
237 * by a given amount. The new region is returned, and the original one
238 * is destroyed. Used to implement DCX_CLIPSIBLINGS and
239 * DCX_CLIPCHILDREN styles.
241 static HRGN
DCE_ClipWindows( WND
*pWndStart
, WND
*pWndEnd
,
242 HRGN hrgn
, int xoffset
, int yoffset
)
246 if (!pWndStart
) return hrgn
;
247 if (!(hrgnNew
= CreateRectRgn( 0, 0, 0, 0 )))
249 DeleteObject( hrgn
);
252 for (; pWndStart
!= pWndEnd
; pWndStart
= pWndStart
->next
)
254 if (!(pWndStart
->dwStyle
& WS_VISIBLE
)) continue;
255 SetRectRgn( hrgnNew
, pWndStart
->rectWindow
.left
+ xoffset
,
256 pWndStart
->rectWindow
.top
+ yoffset
,
257 pWndStart
->rectWindow
.right
+ xoffset
,
258 pWndStart
->rectWindow
.bottom
+ yoffset
);
259 if (!CombineRgn( hrgn
, hrgn
, hrgnNew
, RGN_DIFF
)) break;
261 DeleteObject( hrgnNew
);
262 if (pWndStart
!= pWndEnd
) /* something went wrong */
264 DeleteObject( hrgn
);
271 /***********************************************************************
274 * Return the visible region of a window, i.e. the client or window area
275 * clipped by the client area of all ancestors, and then optionally
276 * by siblings and children.
278 HRGN
DCE_GetVisRgn( HWND hwnd
, WORD flags
)
282 int xoffset
, yoffset
;
283 WND
*wndPtr
= WIN_FindWndPtr( hwnd
);
285 /* Get visible rectangle and create a region with it.
286 * do we really need to calculate vis rgns for X windows?
287 * - yes, to clip child windows.
290 if (!wndPtr
|| !DCE_GetVisRect( wndPtr
, !(flags
& DCX_WINDOW
), &rect
))
292 return CreateRectRgn( 0, 0, 0, 0 ); /* Visible region is empty */
294 if (!(hrgn
= CreateRectRgnIndirect16( &rect
))) return 0;
296 /* Clip all children from the visible region */
298 if (flags
& DCX_CLIPCHILDREN
)
300 if (flags
& DCX_WINDOW
)
302 xoffset
= wndPtr
->rectClient
.left
- wndPtr
->rectWindow
.left
;
303 yoffset
= wndPtr
->rectClient
.top
- wndPtr
->rectWindow
.top
;
305 else xoffset
= yoffset
= 0;
306 hrgn
= DCE_ClipWindows( wndPtr
->child
, NULL
, hrgn
, xoffset
, yoffset
);
310 /* Clip siblings placed above this window */
312 if (flags
& DCX_WINDOW
)
314 xoffset
= -wndPtr
->rectWindow
.left
;
315 yoffset
= -wndPtr
->rectWindow
.top
;
319 xoffset
= -wndPtr
->rectClient
.left
;
320 yoffset
= -wndPtr
->rectClient
.top
;
322 if (flags
& DCX_CLIPSIBLINGS
)
324 hrgn
= DCE_ClipWindows( wndPtr
->parent
? wndPtr
->parent
->child
: NULL
,
325 wndPtr
, hrgn
, xoffset
, yoffset
);
329 /* Clip siblings of all ancestors that have the WS_CLIPSIBLINGS style */
331 while (wndPtr
->dwStyle
& WS_CHILD
)
333 wndPtr
= wndPtr
->parent
;
334 xoffset
-= wndPtr
->rectClient
.left
;
335 yoffset
-= wndPtr
->rectClient
.top
;
336 hrgn
= DCE_ClipWindows( wndPtr
->parent
->child
, wndPtr
,
337 hrgn
, xoffset
, yoffset
);
344 /***********************************************************************
347 * Set the drawable, origin and dimensions for the DC associated to
350 static void DCE_SetDrawable( WND
*wndPtr
, DC
*dc
, WORD flags
)
352 if (!wndPtr
) /* Get a DC for the whole screen */
356 dc
->u
.x
.drawable
= rootWindow
;
357 XSetSubwindowMode( display
, dc
->u
.x
.gc
, IncludeInferiors
);
361 if (flags
& DCX_WINDOW
)
363 dc
->w
.DCOrgX
= wndPtr
->rectWindow
.left
;
364 dc
->w
.DCOrgY
= wndPtr
->rectWindow
.top
;
368 dc
->w
.DCOrgX
= wndPtr
->rectClient
.left
;
369 dc
->w
.DCOrgY
= wndPtr
->rectClient
.top
;
371 while (!wndPtr
->window
)
373 wndPtr
= wndPtr
->parent
;
374 dc
->w
.DCOrgX
+= wndPtr
->rectClient
.left
;
375 dc
->w
.DCOrgY
+= wndPtr
->rectClient
.top
;
377 dc
->w
.DCOrgX
-= wndPtr
->rectWindow
.left
;
378 dc
->w
.DCOrgY
-= wndPtr
->rectWindow
.top
;
379 dc
->u
.x
.drawable
= wndPtr
->window
;
384 /***********************************************************************
385 * GetDCEx16 (USER.359)
387 HDC16
GetDCEx16( HWND16 hwnd
, HRGN16 hrgnClip
, DWORD flags
)
389 return (HDC16
)GetDCEx32( hwnd
, hrgnClip
, flags
);
393 /***********************************************************************
394 * GetDCEx32 (USER32.230)
396 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
398 HDC32
GetDCEx32( HWND32 hwnd
, HRGN32 hrgnClip
, DWORD flags
)
406 BOOL need_update
= TRUE
;
408 dprintf_dc(stddeb
,"GetDCEx: hwnd %04x, hrgnClip %04x, flags %08x\n", hwnd
, hrgnClip
, (unsigned)flags
);
410 if (!(wndPtr
= WIN_FindWndPtr( hwnd
))) return 0;
412 if (flags
& DCX_USESTYLE
)
414 flags
&= ~( DCX_CLIPCHILDREN
| DCX_CLIPSIBLINGS
| DCX_PARENTCLIP
);
416 if( wndPtr
->dwStyle
& WS_CLIPSIBLINGS
)
417 flags
|= DCX_CLIPSIBLINGS
;
419 if ( !(flags
& DCX_WINDOW
) )
421 if (!(wndPtr
->class->style
& (CS_OWNDC
| CS_CLASSDC
)))
424 if (wndPtr
->class->style
& CS_PARENTDC
) flags
|= DCX_PARENTCLIP
;
426 if (wndPtr
->dwStyle
& WS_CLIPCHILDREN
&&
427 !(wndPtr
->dwStyle
& WS_MINIMIZE
) ) flags
|= DCX_CLIPCHILDREN
;
429 else flags
|= DCX_CACHE
;
432 if( flags
& DCX_NOCLIPCHILDREN
)
435 flags
&= ~(DCX_PARENTCLIP
| DCX_CLIPCHILDREN
);
438 if (hwnd
== GetDesktopWindow32() || !(wndPtr
->dwStyle
& WS_CHILD
))
439 flags
&= ~DCX_PARENTCLIP
;
441 if (flags
& DCX_WINDOW
) flags
= (flags
& ~DCX_CLIPCHILDREN
) | DCX_CACHE
;
443 if( flags
& DCX_PARENTCLIP
)
446 if( !(flags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
)) )
447 if( (wndPtr
->dwStyle
& WS_VISIBLE
) && (wndPtr
->parent
->dwStyle
& WS_VISIBLE
) )
449 flags
&= ~DCX_CLIPCHILDREN
;
450 if( wndPtr
->parent
->dwStyle
& WS_CLIPSIBLINGS
)
451 flags
|= DCX_CLIPSIBLINGS
;
455 if (flags
& DCX_CACHE
)
457 for (dce
= firstDCE
; (dce
); dce
= dce
->next
)
459 if ((dce
->DCXflags
& DCX_CACHE
) && !(dce
->DCXflags
& DCX_DCEBUSY
)) break;
464 dce
= (wndPtr
->class->style
& CS_OWNDC
)?wndPtr
->dce
:wndPtr
->class->dce
;
465 if( dce
->hwndCurrent
== hwnd
)
467 dprintf_dc(stddeb
,"\tskipping hVisRgn update\n");
471 if( hrgnClip
&& dce
->hClipRgn
&& !(dce
->DCXflags
& DCX_KEEPCLIPRGN
))
473 fprintf(stdnimp
,"GetDCEx: hClipRgn collision!\n");
474 DeleteObject(dce
->hClipRgn
);
479 dcx_flags
= flags
& ( DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
| DCX_CACHE
| DCX_WINDOW
| DCX_WINDOWPAINT
);
482 dce
->hwndCurrent
= hwnd
;
484 dce
->DCXflags
= dcx_flags
| DCX_DCEBUSY
;
487 if (!(dc
= (DC
*) GDI_GetObjPtr( hdc
, DC_MAGIC
))) return 0;
489 DCE_SetDrawable( wndPtr
, dc
, flags
);
490 if( need_update
|| dc
->w
.flags
& DC_DIRTY
)
492 dprintf_dc(stddeb
,"updating hDC anyway\n");
494 if (flags
& DCX_PARENTCLIP
)
496 WND
*parentPtr
= wndPtr
->parent
;
497 dcx_flags
= flags
& ~(DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
499 if (parentPtr
->dwStyle
& WS_CLIPSIBLINGS
)
500 dcx_flags
|= DCX_CLIPSIBLINGS
;
501 hrgnVisible
= DCE_GetVisRgn( parentPtr
->hwndSelf
, dcx_flags
);
502 if (flags
& DCX_WINDOW
)
503 OffsetRgn( hrgnVisible
, -wndPtr
->rectWindow
.left
,
504 -wndPtr
->rectWindow
.top
);
505 else OffsetRgn( hrgnVisible
, -wndPtr
->rectClient
.left
,
506 -wndPtr
->rectClient
.top
);
508 /* optimize away GetVisRgn for desktop if it isn't there */
510 else if ((hwnd
== GetDesktopWindow32()) &&
511 (rootWindow
== DefaultRootWindow(display
)))
512 hrgnVisible
= CreateRectRgn( 0, 0, SYSMETRICS_CXSCREEN
,
513 SYSMETRICS_CYSCREEN
);
514 else hrgnVisible
= DCE_GetVisRgn( hwnd
, flags
);
516 if( wndPtr
->parent
&& wndPtr
->window
)
518 WND
* wnd
= wndPtr
->parent
->child
;
521 for( ; wnd
!= wndPtr
; wnd
= wnd
->next
)
522 if( wnd
->class->style
& CS_SAVEBITS
&&
523 wnd
->dwStyle
& WS_VISIBLE
&&
524 IntersectRect16(&rect
, &wndPtr
->rectClient
, &wnd
->rectClient
) )
525 wnd
->flags
|= WIN_SAVEUNDER_OVERRIDE
;
528 dc
->w
.flags
&= ~DC_DIRTY
;
529 SelectVisRgn( hdc
, hrgnVisible
);
531 else hrgnVisible
= CreateRectRgn(0,0,0,0);
533 if ((flags
& DCX_INTERSECTRGN
) || (flags
& DCX_EXCLUDERGN
))
535 dce
->DCXflags
|= flags
& (DCX_KEEPCLIPRGN
| DCX_INTERSECTRGN
| DCX_EXCLUDERGN
);
536 dce
->hClipRgn
= hrgnClip
;
538 dprintf_dc(stddeb
, "\tsaved VisRgn, clipRgn = %04x\n", hrgnClip
);
541 CombineRgn( hrgnVisible
, InquireVisRgn( hdc
), hrgnClip
,
542 (flags
& DCX_INTERSECTRGN
) ? RGN_AND
: RGN_DIFF
);
543 SelectVisRgn( hdc
, hrgnVisible
);
545 DeleteObject( hrgnVisible
);
547 dprintf_dc(stddeb
, "GetDCEx(%04x,%04x,0x%lx): returning %04x\n",
548 hwnd
, hrgnClip
, flags
, hdc
);
553 /***********************************************************************
556 HDC16
GetDC16( HWND16 hwnd
)
558 return (HDC16
)GetDC32( hwnd
);
562 /***********************************************************************
563 * GetDC32 (USER32.229)
565 HDC32
GetDC32( HWND32 hwnd
)
568 return GetDCEx32( GetDesktopWindow32(), 0, DCX_CACHE
| DCX_WINDOW
);
569 return GetDCEx32( hwnd
, 0, DCX_USESTYLE
);
573 /***********************************************************************
574 * GetWindowDC16 (USER.67)
576 HDC16
GetWindowDC16( HWND16 hwnd
)
578 if (!hwnd
) hwnd
= GetDesktopWindow16();
579 return GetDCEx16( hwnd
, 0, DCX_USESTYLE
| DCX_WINDOW
);
583 /***********************************************************************
584 * GetWindowDC32 (USER32.)
586 HDC32
GetWindowDC32( HWND32 hwnd
)
588 if (!hwnd
) hwnd
= GetDesktopWindow32();
589 return GetDCEx32( hwnd
, 0, DCX_USESTYLE
| DCX_WINDOW
);
593 /***********************************************************************
594 * ReleaseDC16 (USER.68)
596 INT16
ReleaseDC16( HWND16 hwnd
, HDC16 hdc
)
598 return (INT32
)ReleaseDC32( hwnd
, hdc
);
602 /***********************************************************************
603 * ReleaseDC32 (USER32.439)
605 INT32
ReleaseDC32( HWND32 hwnd
, HDC32 hdc
)
607 DCE
* dce
= firstDCE
;
609 dprintf_dc(stddeb
, "ReleaseDC: %04x %04x\n", hwnd
, hdc
);
611 while (dce
&& (dce
->hDC
!= hdc
)) dce
= dce
->next
;
613 if (!(dce
->DCXflags
& DCX_DCEBUSY
) ) return 0;
615 /* restore previous visible region */
617 if ( dce
->DCXflags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
) &&
618 (dce
->DCXflags
& DCX_CACHE
|| dce
->DCXflags
& DCX_WINDOWPAINT
) )
620 dprintf_dc(stddeb
,"\tcleaning up visrgn...\n");
621 dce
->DCXflags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
| DCX_WINDOWPAINT
);
623 if( dce
->DCXflags
& DCX_KEEPCLIPRGN
)
624 dce
->DCXflags
&= ~DCX_KEEPCLIPRGN
;
626 if( dce
->hClipRgn
> 1 )
627 DeleteObject( dce
->hClipRgn
);
630 RestoreVisRgn(dce
->hDC
);
633 if (dce
->DCXflags
& DCX_CACHE
)
635 SetDCState( dce
->hDC
, defaultDCstate
);
636 dce
->DCXflags
= DCX_CACHE
;
637 dce
->hwndCurrent
= 0;
642 /***********************************************************************
645 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
647 BOOL
DCHook(HDC hDC
, WORD code
, DWORD data
, DWORD lParam
)
650 DCE
*dce
= firstDCE
;;
652 dprintf_dc(stddeb
,"DCHook: hDC = %04x, %i\n", hDC
, code
);
654 while (dce
&& (dce
->hDC
!= hDC
)) dce
= dce
->next
;
659 case DCHC_INVALIDVISRGN
:
661 if( dce
->DCXflags
& DCX_DCEBUSY
)
663 SetHookFlags(hDC
, DCHF_VALIDATEVISRGN
);
664 hVisRgn
= DCE_GetVisRgn(dce
->hwndCurrent
, dce
->DCXflags
);
666 dprintf_dc(stddeb
,"\tapplying saved clipRgn\n");
668 /* clip this region with saved clipping region */
670 if ( (dce
->DCXflags
& DCX_INTERSECTRGN
&& dce
->hClipRgn
!= 1) ||
671 ( dce
->DCXflags
& DCX_EXCLUDERGN
&& dce
->hClipRgn
) )
674 if( (!dce
->hClipRgn
&& dce
->DCXflags
& DCX_INTERSECTRGN
) ||
675 (dce
->hClipRgn
== 1 && dce
->DCXflags
& DCX_EXCLUDERGN
) )
676 SetRectRgn(hVisRgn
,0,0,0,0);
678 CombineRgn(hVisRgn
, hVisRgn
, dce
->hClipRgn
,
679 (dce
->DCXflags
& DCX_EXCLUDERGN
)? RGN_DIFF
:RGN_AND
);
681 SelectVisRgn(hDC
, hVisRgn
);
682 DeleteObject(hVisRgn
);
685 dprintf_dc(stddeb
,"DCHook: DC is not in use!\n");
689 case DCHC_DELETEDC
: /* FIXME: ?? */
693 fprintf(stdnimp
,"DCHook: unknown code\n");