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
24 #include "sysmetrics.h"
26 /* #define DEBUG_DC */
29 #define NB_DCE 5 /* Number of DCEs created at startup */
31 static HANDLE firstDCE
= 0;
32 static HDC defaultDCstate
= 0;
34 BOOL
DCHook(HDC
, WORD
, DWORD
, DWORD
);
36 /***********************************************************************
41 HANDLE
DCE_AllocDCE( HWND hWnd
, DCE_TYPE type
)
44 HANDLE handle
= USER_HEAP_ALLOC( sizeof(DCE
) );
45 if (!handle
) return 0;
46 dce
= (DCE
*) USER_HEAP_LIN_ADDR( handle
);
47 if (!(dce
->hDC
= CreateDC( "DISPLAY", NULL
, NULL
, NULL
)))
49 USER_HEAP_FREE( handle
);
53 /* store DCE handle in DC hook data field */
55 SetDCHook(dce
->hDC
, GDI_GetDefDCHook(), MAKELONG(handle
,DC_MAGIC
));
57 dce
->hwndCurrent
= hWnd
;
58 dce
->hNext
= firstDCE
;
62 if( type
!= DCE_CACHE_DC
)
64 dce
->DCXflags
= DCX_DCEBUSY
;
67 WND
* wnd
= WIN_FindWndPtr(hWnd
);
69 if( wnd
->dwStyle
& WS_CLIPCHILDREN
) dce
->DCXflags
|= DCX_CLIPCHILDREN
;
70 if( wnd
->dwStyle
& WS_CLIPSIBLINGS
) dce
->DCXflags
|= DCX_CLIPSIBLINGS
;
72 SetHookFlags(dce
->hDC
,DCHF_INVALIDATEVISRGN
);
74 else dce
->DCXflags
= DCX_CACHE
;
80 /***********************************************************************
83 void DCE_FreeDCE( HANDLE hdce
)
86 HANDLE
*handle
= &firstDCE
;
88 if (!(dce
= (DCE
*) USER_HEAP_LIN_ADDR( hdce
))) return;
89 while (*handle
&& (*handle
!= hdce
))
91 DCE
* prev
= (DCE
*) USER_HEAP_LIN_ADDR( *handle
);
92 handle
= &prev
->hNext
;
94 if (*handle
== hdce
) *handle
= dce
->hNext
;
96 SetDCHook(dce
->hDC
,(SEGPTR
)NULL
,0L);
99 if( dce
->hClipRgn
&& !(dce
->DCXflags
& DCX_KEEPCLIPRGN
) )
100 DeleteObject(dce
->hClipRgn
);
101 USER_HEAP_FREE( hdce
);
104 /***********************************************************************
107 HANDLE
DCE_FindDCE(HDC hDC
)
109 HANDLE hdce
= firstDCE
;
114 dce
= (DCE
*) USER_HEAP_LIN_ADDR(hdce
);
115 if( dce
->hDC
== hDC
) break;
121 /***********************************************************************
124 * It is called from SetWindowPos - we have to invalidate all busy
125 * DCE's for windows whose client rect intersects with update rectangle
127 BOOL
DCE_InvalidateDCE(WND
* wndScope
, RECT16
* pRectUpdate
)
132 if( !wndScope
) return 0;
134 dprintf_dc(stddeb
,"InvalidateDCE: scope hwnd = %04x, (%i,%i - %i,%i)\n",
135 wndScope
->hwndSelf
, pRectUpdate
->left
,pRectUpdate
->top
,
136 pRectUpdate
->right
,pRectUpdate
->bottom
);
139 for( hdce
= firstDCE
; (hdce
); hdce
=dce
->hNext
)
141 dce
= (DCE
*)USER_HEAP_LIN_ADDR(hdce
);
143 if( dce
->DCXflags
& DCX_DCEBUSY
)
145 WND
* wndCurrent
, * wnd
;
147 wnd
= wndCurrent
= WIN_FindWndPtr(dce
->hwndCurrent
);
149 /* desktop is not critical (DC is not owned anyway) */
151 if( wnd
== WIN_GetDesktop() ) continue;
153 /* check if DCE window is within z-order scope */
155 for( ; wnd
; wnd
= wnd
->parent
)
156 if( wnd
== wndScope
)
158 RECT16 wndRect
= wndCurrent
->rectWindow
;
160 dprintf_dc(stddeb
,"\tgot hwnd %04x\n", wndCurrent
->hwndSelf
);
162 if( wndCurrent
->parent
!= wndScope
)
163 MapWindowPoints16(wndCurrent
->parent
->hwndSelf
, wndScope
->hwndSelf
,
164 (LPPOINT16
)&wndRect
, 2);
165 if( IntersectRect16(&wndRect
,&wndRect
,pRectUpdate
) )
166 SetHookFlags(dce
->hDC
, DCHF_INVALIDATEVISRGN
);
174 /***********************************************************************
183 for (i
= 0; i
< NB_DCE
; i
++)
185 if (!(handle
= DCE_AllocDCE( 0, DCE_CACHE_DC
))) return;
186 dce
= (DCE
*) USER_HEAP_LIN_ADDR( handle
);
187 if (!defaultDCstate
) defaultDCstate
= GetDCState( dce
->hDC
);
192 /***********************************************************************
195 * Calc the visible rectangle of a window, i.e. the client or
196 * window area clipped by the client area of all ancestors.
197 * Return FALSE if the visible region is empty.
199 static BOOL
DCE_GetVisRect( WND
*wndPtr
, BOOL clientArea
, RECT16
*lprect
)
201 int xoffset
, yoffset
;
203 *lprect
= clientArea
? wndPtr
->rectClient
: wndPtr
->rectWindow
;
204 xoffset
= lprect
->left
;
205 yoffset
= lprect
->top
;
207 if (!(wndPtr
->dwStyle
& WS_VISIBLE
) || (wndPtr
->flags
& WIN_NO_REDRAW
))
209 SetRectEmpty16( lprect
); /* Clip everything */
213 while (wndPtr
->parent
)
215 wndPtr
= wndPtr
->parent
;
216 if (!(wndPtr
->dwStyle
& WS_VISIBLE
) ||
217 (wndPtr
->flags
& WIN_NO_REDRAW
) ||
218 (wndPtr
->dwStyle
& WS_ICONIC
))
220 SetRectEmpty16( lprect
); /* Clip everything */
223 xoffset
+= wndPtr
->rectClient
.left
;
224 yoffset
+= wndPtr
->rectClient
.top
;
225 OffsetRect16( lprect
, wndPtr
->rectClient
.left
,
226 wndPtr
->rectClient
.top
);
228 /* Warning!! we assume that IntersectRect() handles the case */
229 /* where the destination is the same as one of the sources. */
230 if (!IntersectRect16( lprect
, lprect
, &wndPtr
->rectClient
))
231 return FALSE
; /* Visible rectangle is empty */
233 OffsetRect16( lprect
, -xoffset
, -yoffset
);
238 /***********************************************************************
241 * Go through the linked list of windows from hwndStart to hwndEnd,
242 * removing from the given region the rectangle of each window offset
243 * by a given amount. The new region is returned, and the original one
244 * is destroyed. Used to implement DCX_CLIPSIBLINGS and
245 * DCX_CLIPCHILDREN styles.
247 static HRGN
DCE_ClipWindows( WND
*pWndStart
, WND
*pWndEnd
,
248 HRGN hrgn
, int xoffset
, int yoffset
)
252 if (!pWndStart
) return hrgn
;
253 if (!(hrgnNew
= CreateRectRgn( 0, 0, 0, 0 )))
255 DeleteObject( hrgn
);
258 for (; pWndStart
!= pWndEnd
; pWndStart
= pWndStart
->next
)
260 if (!(pWndStart
->dwStyle
& WS_VISIBLE
)) continue;
261 SetRectRgn( hrgnNew
, pWndStart
->rectWindow
.left
+ xoffset
,
262 pWndStart
->rectWindow
.top
+ yoffset
,
263 pWndStart
->rectWindow
.right
+ xoffset
,
264 pWndStart
->rectWindow
.bottom
+ yoffset
);
265 if (!CombineRgn( hrgn
, hrgn
, hrgnNew
, RGN_DIFF
)) break;
267 DeleteObject( hrgnNew
);
268 if (pWndStart
!= pWndEnd
) /* something went wrong */
270 DeleteObject( hrgn
);
277 /***********************************************************************
280 * Return the visible region of a window, i.e. the client or window area
281 * clipped by the client area of all ancestors, and then optionally
282 * by siblings and children.
284 HRGN
DCE_GetVisRgn( HWND hwnd
, WORD flags
)
288 int xoffset
, yoffset
;
289 WND
*wndPtr
= WIN_FindWndPtr( hwnd
);
291 /* Get visible rectangle and create a region with it
292 * FIXME: do we really need to calculate vis rgns for X windows?
295 if (!wndPtr
|| !DCE_GetVisRect( wndPtr
, !(flags
& DCX_WINDOW
), &rect
))
297 return CreateRectRgn( 0, 0, 0, 0 ); /* Visible region is empty */
299 if (!(hrgn
= CreateRectRgnIndirect16( &rect
))) return 0;
301 /* Clip all children from the visible region */
303 if (flags
& DCX_CLIPCHILDREN
)
305 if (flags
& DCX_WINDOW
)
307 xoffset
= wndPtr
->rectClient
.left
- wndPtr
->rectWindow
.left
;
308 yoffset
= wndPtr
->rectClient
.top
- wndPtr
->rectWindow
.top
;
310 else xoffset
= yoffset
= 0;
311 hrgn
= DCE_ClipWindows( wndPtr
->child
, NULL
, hrgn
, xoffset
, yoffset
);
315 /* Clip siblings placed above this window */
317 if (flags
& DCX_WINDOW
)
319 xoffset
= -wndPtr
->rectWindow
.left
;
320 yoffset
= -wndPtr
->rectWindow
.top
;
324 xoffset
= -wndPtr
->rectClient
.left
;
325 yoffset
= -wndPtr
->rectClient
.top
;
327 if (flags
& DCX_CLIPSIBLINGS
)
329 hrgn
= DCE_ClipWindows( wndPtr
->parent
? wndPtr
->parent
->child
: NULL
,
330 wndPtr
, hrgn
, xoffset
, yoffset
);
334 /* Clip siblings of all ancestors that have the WS_CLIPSIBLINGS style */
336 while (wndPtr
->dwStyle
& WS_CHILD
)
338 wndPtr
= wndPtr
->parent
;
339 xoffset
-= wndPtr
->rectClient
.left
;
340 yoffset
-= wndPtr
->rectClient
.top
;
341 hrgn
= DCE_ClipWindows( wndPtr
->parent
->child
, wndPtr
,
342 hrgn
, xoffset
, yoffset
);
349 /***********************************************************************
352 * Set the drawable, origin and dimensions for the DC associated to
355 static void DCE_SetDrawable( WND
*wndPtr
, DC
*dc
, WORD flags
)
357 if (!wndPtr
) /* Get a DC for the whole screen */
361 dc
->u
.x
.drawable
= rootWindow
;
362 XSetSubwindowMode( display
, dc
->u
.x
.gc
, IncludeInferiors
);
366 if (flags
& DCX_WINDOW
)
368 dc
->w
.DCOrgX
= wndPtr
->rectWindow
.left
;
369 dc
->w
.DCOrgY
= wndPtr
->rectWindow
.top
;
373 dc
->w
.DCOrgX
= wndPtr
->rectClient
.left
;
374 dc
->w
.DCOrgY
= wndPtr
->rectClient
.top
;
376 while (!wndPtr
->window
)
378 wndPtr
= wndPtr
->parent
;
379 dc
->w
.DCOrgX
+= wndPtr
->rectClient
.left
;
380 dc
->w
.DCOrgY
+= wndPtr
->rectClient
.top
;
382 dc
->w
.DCOrgX
-= wndPtr
->rectWindow
.left
;
383 dc
->w
.DCOrgY
-= wndPtr
->rectWindow
.top
;
384 dc
->u
.x
.drawable
= wndPtr
->window
;
388 /***********************************************************************
391 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
393 HDC
GetDCEx( HWND hwnd
, HRGN hrgnClip
, DWORD flags
)
402 BOOL need_update
= TRUE
;
404 dprintf_dc(stddeb
,"GetDCEx: hwnd %04x, hrgnClip %04x, flags %08x\n", hwnd
, hrgnClip
, (unsigned)flags
);
406 if (!(wndPtr
= WIN_FindWndPtr( hwnd
))) return 0;
408 if (flags
& DCX_USESTYLE
)
410 flags
&= ~( DCX_CLIPCHILDREN
| DCX_CLIPSIBLINGS
| DCX_PARENTCLIP
);
412 if( wndPtr
->dwStyle
& WS_CLIPSIBLINGS
)
413 flags
|= DCX_CLIPSIBLINGS
;
415 if ( !(flags
& DCX_WINDOW
) )
417 if (!(wndPtr
->class->style
& (CS_OWNDC
| CS_CLASSDC
)))
420 if (wndPtr
->class->style
& CS_PARENTDC
) flags
|= DCX_PARENTCLIP
;
422 if (wndPtr
->dwStyle
& WS_CLIPCHILDREN
&&
423 !(wndPtr
->dwStyle
& WS_MINIMIZE
) ) flags
|= DCX_CLIPCHILDREN
;
425 else flags
|= DCX_CACHE
;
428 if( flags
& DCX_NOCLIPCHILDREN
)
431 flags
&= ~(DCX_PARENTCLIP
| DCX_CLIPCHILDREN
);
434 if (hwnd
==GetDesktopWindow() || !(wndPtr
->dwStyle
& WS_CHILD
)) flags
&= ~DCX_PARENTCLIP
;
436 if (flags
& DCX_WINDOW
) flags
= (flags
& ~DCX_CLIPCHILDREN
) | DCX_CACHE
;
438 if( flags
& DCX_PARENTCLIP
)
441 if( !(flags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
)) )
442 if( (wndPtr
->dwStyle
& WS_VISIBLE
) && (wndPtr
->parent
->dwStyle
& WS_VISIBLE
) )
444 flags
&= ~DCX_CLIPCHILDREN
;
445 if( wndPtr
->parent
->dwStyle
& WS_CLIPSIBLINGS
)
446 flags
|= DCX_CLIPSIBLINGS
;
450 if (flags
& DCX_CACHE
)
452 for (hdce
= firstDCE
; (hdce
); hdce
= dce
->hNext
)
454 if (!(dce
= (DCE
*) USER_HEAP_LIN_ADDR( hdce
))) return 0;
455 if ((dce
->DCXflags
& DCX_CACHE
) && !(dce
->DCXflags
& DCX_DCEBUSY
)) break;
460 hdce
= (wndPtr
->class->style
& CS_OWNDC
)?wndPtr
->hdce
:wndPtr
->class->hdce
;
461 dce
= (DCE
*) USER_HEAP_LIN_ADDR( hdce
);
463 if( dce
->hwndCurrent
== hwnd
)
465 dprintf_dc(stddeb
,"\tskipping hVisRgn update\n");
469 if( hrgnClip
&& dce
->hClipRgn
&& !(dce
->DCXflags
& DCX_KEEPCLIPRGN
))
471 fprintf(stdnimp
,"GetDCEx: hClipRgn collision!\n");
472 DeleteObject(dce
->hClipRgn
);
477 dcx_flags
= flags
& ( DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
| DCX_CACHE
| DCX_WINDOW
| DCX_WINDOWPAINT
);
480 dce
= (DCE
*) USER_HEAP_LIN_ADDR( hdce
);
481 dce
->hwndCurrent
= hwnd
;
483 dce
->DCXflags
= dcx_flags
| DCX_DCEBUSY
;
486 if (!(dc
= (DC
*) GDI_GetObjPtr( hdc
, DC_MAGIC
))) return 0;
488 DCE_SetDrawable( wndPtr
, dc
, flags
);
489 if( need_update
|| dc
->w
.flags
& DC_DIRTY
)
491 dprintf_dc(stddeb
,"updating hDC anyway\n");
493 if (flags
& DCX_PARENTCLIP
)
495 WND
*parentPtr
= wndPtr
->parent
;
496 dcx_flags
= flags
& ~(DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
498 if (parentPtr
->dwStyle
& WS_CLIPSIBLINGS
)
499 dcx_flags
|= DCX_CLIPSIBLINGS
;
500 hrgnVisible
= DCE_GetVisRgn( parentPtr
->hwndSelf
, dcx_flags
);
501 if (flags
& DCX_WINDOW
)
502 OffsetRgn( hrgnVisible
, -wndPtr
->rectWindow
.left
,
503 -wndPtr
->rectWindow
.top
);
504 else OffsetRgn( hrgnVisible
, -wndPtr
->rectClient
.left
,
505 -wndPtr
->rectClient
.top
);
507 /* optimize away GetVisRgn for desktop if it isn't there */
509 else if( hwnd
==GetDesktopWindow() && !Options
.desktopGeometry
)
510 hrgnVisible
= CreateRectRgn( 0, 0, SYSMETRICS_CXSCREEN
,
511 SYSMETRICS_CYSCREEN
);
512 else hrgnVisible
= DCE_GetVisRgn( hwnd
, flags
);
514 dc
->w
.flags
&= ~DC_DIRTY
;
516 SelectVisRgn( hdc
, hrgnVisible
);
518 else hrgnVisible
= CreateRectRgn(0,0,0,0);
520 if ((flags
& DCX_INTERSECTRGN
) || (flags
& DCX_EXCLUDERGN
))
522 dce
->DCXflags
|= flags
& (DCX_KEEPCLIPRGN
| DCX_INTERSECTRGN
| DCX_EXCLUDERGN
);
523 dce
->hClipRgn
= hrgnClip
;
525 dprintf_dc(stddeb
, "\tsaved VisRgn, clipRgn = %04x\n", hrgnClip
);
528 CombineRgn( hrgnVisible
, InquireVisRgn( hdc
), hrgnClip
,
529 (flags
& DCX_INTERSECTRGN
) ? RGN_AND
: RGN_DIFF
);
530 SelectVisRgn( hdc
, hrgnVisible
);
532 DeleteObject( hrgnVisible
);
534 dprintf_dc(stddeb
, "GetDCEx(%04x,%04x,0x%lx): returning %04x\n",
535 hwnd
, hrgnClip
, flags
, hdc
);
539 /***********************************************************************
542 HDC
GetDC( HWND hwnd
)
544 if( !hwnd
) return GetDCEx( GetDesktopWindow(), 0, DCX_CACHE
| DCX_WINDOW
);
546 return GetDCEx( hwnd
, 0, DCX_USESTYLE
);
550 /***********************************************************************
551 * GetWindowDC (USER.67)
553 HDC
GetWindowDC( HWND hwnd
)
558 if (!(wndPtr
= WIN_FindWndPtr( hwnd
))) return 0;
560 else hwnd
= GetDesktopWindow();
562 return GetDCEx( hwnd
, 0, DCX_USESTYLE
| DCX_WINDOW
);
566 /***********************************************************************
567 * ReleaseDC (USER.68)
569 int ReleaseDC( HWND hwnd
, HDC hdc
)
574 dprintf_dc(stddeb
, "ReleaseDC: %04x %04x\n", hwnd
, hdc
);
576 for (hdce
= firstDCE
; (hdce
); hdce
= dce
->hNext
)
578 if (!(dce
= (DCE
*) USER_HEAP_LIN_ADDR( hdce
))) return 0;
579 if (dce
->hDC
== hdc
) break;
582 if (!(dce
->DCXflags
& DCX_DCEBUSY
) ) return 0;
584 /* restore previous visible region */
586 if ( dce
->DCXflags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
) &&
587 (dce
->DCXflags
& DCX_CACHE
|| dce
->DCXflags
& DCX_WINDOWPAINT
) )
589 dprintf_dc(stddeb
,"\tcleaning up visrgn...\n");
590 dce
->DCXflags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
| DCX_WINDOWPAINT
);
592 if( dce
->DCXflags
& DCX_KEEPCLIPRGN
)
593 dce
->DCXflags
&= ~DCX_KEEPCLIPRGN
;
595 if( dce
->hClipRgn
> 1 )
596 DeleteObject( dce
->hClipRgn
);
599 RestoreVisRgn(dce
->hDC
);
602 if (dce
->DCXflags
& DCX_CACHE
)
604 SetDCState( dce
->hDC
, defaultDCstate
);
605 dce
->DCXflags
= DCX_CACHE
;
610 /***********************************************************************
613 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
615 BOOL
DCHook(HDC hDC
, WORD code
, DWORD data
, DWORD lParam
)
620 dprintf_dc(stddeb
,"DCHook: hDC = %04x, %i\n", hDC
, code
);
622 if( HIWORD(data
) == DC_MAGIC
)
623 hdce
= (HANDLE
)LOWORD(data
);
625 hdce
= DCE_FindDCE(hDC
);
627 if( !hdce
) return 0;
631 case DCHC_INVALIDVISRGN
:
633 DCE
* dce
= (DCE
*) USER_HEAP_LIN_ADDR(hdce
);
635 if( dce
->DCXflags
& DCX_DCEBUSY
)
637 SetHookFlags(hDC
, DCHF_VALIDATEVISRGN
);
638 hVisRgn
= DCE_GetVisRgn(dce
->hwndCurrent
, dce
->DCXflags
);
640 dprintf_dc(stddeb
,"\tapplying saved clipRgn\n");
642 /* clip this region with saved clipping region */
644 if ( (dce
->DCXflags
& DCX_INTERSECTRGN
&& dce
->hClipRgn
!= 1) ||
645 ( dce
->DCXflags
& DCX_EXCLUDERGN
&& dce
->hClipRgn
) )
648 if( (!dce
->hClipRgn
&& dce
->DCXflags
& DCX_INTERSECTRGN
) ||
649 (dce
->hClipRgn
== 1 && dce
->DCXflags
& DCX_EXCLUDERGN
) )
650 SetRectRgn(hVisRgn
,0,0,0,0);
652 CombineRgn(hVisRgn
, hVisRgn
, dce
->hClipRgn
,
653 (dce
->DCXflags
& DCX_EXCLUDERGN
)? RGN_DIFF
:RGN_AND
);
655 SelectVisRgn(hDC
, hVisRgn
);
656 DeleteObject(hVisRgn
);
659 dprintf_dc(stddeb
,"DCHook: DC is not in use!\n");
663 case DCHC_DELETEDC
: /* FIXME: ?? */
667 fprintf(stdnimp
,"DCHook: unknown code\n");