Release 950319
[wine] / windows / dce.c
1 /*
2  * USER DCE functions
3  *
4  * Copyright 1993 Alexandre Julliard
5  *
6 static char Copyright[] = "Copyright  Alexandre Julliard, 1993";
7 */
8
9 #include "dce.h"
10 #include "class.h"
11 #include "win.h"
12 #include "gdi.h"
13 #include "user.h"
14 #include "sysmetrics.h"
15 #include "stddebug.h"
16 /* #define DEBUG_DC */
17 #include "debug.h"
18
19 #define NB_DCE    5  /* Number of DCEs created at startup */
20
21 static HANDLE firstDCE = 0;
22 static HDC defaultDCstate = 0;
23
24
25 /***********************************************************************
26  *           DCE_AllocDCE
27  *
28  * Allocate a new DCE.
29  */
30 HANDLE DCE_AllocDCE( DCE_TYPE type )
31 {
32     DCE * dce;
33     HANDLE handle = USER_HEAP_ALLOC( sizeof(DCE) );
34     if (!handle) return 0;
35     dce = (DCE *) USER_HEAP_LIN_ADDR( handle );
36     if (!(dce->hdc = CreateDC( "DISPLAY", NULL, NULL, NULL )))
37     {
38         USER_HEAP_FREE( handle );
39         return 0;
40     }
41     dce->hwndCurrent = 0;
42     dce->type  = type;
43     dce->inUse = (type != DCE_CACHE_DC);
44     dce->xOrigin = 0;
45     dce->yOrigin = 0;
46     dce->hNext = firstDCE;
47     firstDCE = handle;
48     return handle;
49 }
50
51
52 /***********************************************************************
53  *           DCE_FreeDCE
54  */
55 void DCE_FreeDCE( HANDLE hdce )
56 {
57     DCE * dce;
58     HANDLE *handle = &firstDCE;
59
60     if (!(dce = (DCE *) USER_HEAP_LIN_ADDR( hdce ))) return;
61     while (*handle && (*handle != hdce))
62     {
63         DCE * prev = (DCE *) USER_HEAP_LIN_ADDR( *handle );     
64         handle = &prev->hNext;
65     }
66     if (*handle == hdce) *handle = dce->hNext;
67     DeleteDC( dce->hdc );
68     USER_HEAP_FREE( hdce );
69 }
70
71
72 /***********************************************************************
73  *           DCE_Init
74  */
75 void DCE_Init()
76 {
77     int i;
78     HANDLE handle;
79     DCE * dce;
80         
81     for (i = 0; i < NB_DCE; i++)
82     {
83         if (!(handle = DCE_AllocDCE( DCE_CACHE_DC ))) return;
84         dce = (DCE *) USER_HEAP_LIN_ADDR( handle );     
85         if (!defaultDCstate) defaultDCstate = GetDCState( dce->hdc );
86     }
87 }
88
89
90 /***********************************************************************
91  *           DCE_GetVisRect
92  *
93  * Calc the visible rectangle of a window, i.e. the client or
94  * window area clipped by the client area of all ancestors.
95  * Return FALSE if the visible region is empty.
96  */
97 static BOOL DCE_GetVisRect( WND *wndPtr, BOOL clientArea, RECT *lprect )
98 {
99     int xoffset, yoffset;
100
101     *lprect = clientArea ? wndPtr->rectClient : wndPtr->rectWindow;
102     xoffset = lprect->left;
103     yoffset = lprect->top;
104
105     if (!(wndPtr->dwStyle & WS_VISIBLE) || (wndPtr->flags & WIN_NO_REDRAW))
106     {
107         SetRectEmpty( lprect );  /* Clip everything */
108         return FALSE;
109     }
110
111     while (wndPtr->hwndParent)
112     {
113         WND *parentPtr = WIN_FindWndPtr( wndPtr->hwndParent );
114         if (!(parentPtr->dwStyle & WS_VISIBLE) ||
115             (parentPtr->flags & WIN_NO_REDRAW) ||
116             (parentPtr->dwStyle & WS_ICONIC))
117         {
118             SetRectEmpty( lprect );  /* Clip everything */
119             return FALSE;
120         }
121         xoffset += parentPtr->rectClient.left;
122         yoffset += parentPtr->rectClient.top;
123         OffsetRect( lprect, parentPtr->rectClient.left,
124                     parentPtr->rectClient.top );
125
126           /* Warning!! we assume that IntersectRect() handles the case */
127           /* where the destination is the same as one of the sources.  */
128         if (!IntersectRect( lprect, lprect, &parentPtr->rectClient ))
129             return FALSE;  /* Visible rectangle is empty */
130         wndPtr = parentPtr;
131     }
132     OffsetRect( lprect, -xoffset, -yoffset );
133     return TRUE;
134 }
135
136
137 /***********************************************************************
138  *           DCE_ClipWindows
139  *
140  * Go through the linked list of windows from hwndStart to hwndEnd,
141  * removing from the given region the rectangle of each window offset
142  * by a given amount.  The new region is returned, and the original one
143  * is destroyed.  Used to implement DCX_CLIPSIBLINGS and
144  * DCX_CLIPCHILDREN styles.
145  */
146 static HRGN DCE_ClipWindows( HWND hwndStart, HWND hwndEnd,
147                              HRGN hrgn, int xoffset, int yoffset )
148 {
149     HRGN hrgnTmp, hrgnNew;
150     WND *wndPtr;
151
152     if (!hwndStart) return hrgn;
153     for (; hwndStart != hwndEnd; hwndStart = wndPtr->hwndNext)
154     {
155         hrgnTmp = hrgnNew = 0;
156         wndPtr = WIN_FindWndPtr( hwndStart );
157         if (!(wndPtr->dwStyle & WS_VISIBLE)) continue;
158         if (!(hrgnTmp = CreateRectRgn( 0, 0, 0, 0 ))) break;
159         if (!(hrgnNew = CreateRectRgn( wndPtr->rectWindow.left + xoffset,
160                                        wndPtr->rectWindow.top + yoffset,
161                                        wndPtr->rectWindow.right + xoffset,
162                                        wndPtr->rectWindow.bottom + yoffset )))
163             break;
164         if (!CombineRgn( hrgnTmp, hrgn, hrgnNew, RGN_DIFF )) break;
165         DeleteObject( hrgn );
166         DeleteObject( hrgnNew );
167         hrgn = hrgnTmp;
168     }
169     if (hwndStart != hwndEnd)  /* something went wrong */
170     {
171         if (hrgnTmp) DeleteObject( hrgnTmp );
172         if (hrgnNew) DeleteObject( hrgnNew );
173         if (hrgn) DeleteObject( hrgn );
174         return 0;
175     }
176     return hrgn;
177 }
178
179
180 /***********************************************************************
181  *           DCE_GetVisRgn
182  *
183  * Return the visible region of a window, i.e. the client or window area
184  * clipped by the client area of all ancestors, and then optionally
185  * by siblings and children.
186  */
187 static HRGN DCE_GetVisRgn( HWND hwnd, WORD flags )
188 {
189     RECT rect;
190     HRGN hrgn;
191     int xoffset, yoffset;
192     WND *wndPtr = WIN_FindWndPtr( hwnd );
193
194       /* Get visible rectangle and create a region with it */
195
196     if (!DCE_GetVisRect( wndPtr, !(flags & DCX_WINDOW), &rect ))
197     {
198         return CreateRectRgn( 0, 0, 0, 0 );  /* Visible region is empty */
199     }
200     if (!(hrgn = CreateRectRgnIndirect( &rect ))) return 0;
201
202       /* Clip all children from the visible region */
203
204     if (flags & DCX_CLIPCHILDREN)
205     {
206         if (flags & DCX_WINDOW)
207         {
208             xoffset = wndPtr->rectClient.left - wndPtr->rectWindow.left;
209             yoffset = wndPtr->rectClient.top - wndPtr->rectWindow.top;
210         }
211         else xoffset = yoffset = 0;
212         hrgn = DCE_ClipWindows( wndPtr->hwndChild, 0, hrgn, xoffset, yoffset );
213         if (!hrgn) return 0;
214     }
215
216       /* Clip siblings placed above this window */
217
218     if (flags & DCX_WINDOW)
219     {
220         xoffset = -wndPtr->rectWindow.left;
221         yoffset = -wndPtr->rectWindow.top;
222     }
223     else
224     {
225         xoffset = -wndPtr->rectClient.left;
226         yoffset = -wndPtr->rectClient.top;
227     }
228     if (flags & DCX_CLIPSIBLINGS)
229     {
230         hrgn = DCE_ClipWindows( GetWindow( wndPtr->hwndParent, GW_CHILD ),
231                                 hwnd, hrgn, xoffset, yoffset );
232         if (!hrgn) return 0;
233     }
234
235       /* Clip siblings of all ancestors that have the WS_CLIPSIBLINGS style */
236
237     while (wndPtr->dwStyle & WS_CHILD)
238     {
239         hwnd = wndPtr->hwndParent;
240         wndPtr = WIN_FindWndPtr( hwnd );
241         xoffset -= wndPtr->rectClient.left;
242         yoffset -= wndPtr->rectClient.top;
243         hrgn = DCE_ClipWindows( GetWindow( wndPtr->hwndParent, GW_CHILD ),
244                                 hwnd, hrgn, xoffset, yoffset );
245         if (!hrgn) return 0;
246     }
247     return hrgn;
248 }
249
250
251 /***********************************************************************
252  *           DCE_SetDrawable
253  *
254  * Set the drawable, origin and dimensions for the DC associated to
255  * a given window.
256  */
257 static void DCE_SetDrawable( WND *wndPtr, DC *dc, WORD flags )
258 {
259     if (!wndPtr)  /* Get a DC for the whole screen */
260     {
261         dc->w.DCOrgX = 0;
262         dc->w.DCOrgY = 0;
263         dc->u.x.drawable = rootWindow;
264         XSetSubwindowMode( display, dc->u.x.gc, IncludeInferiors );
265     }
266     else
267     {
268         if (flags & DCX_WINDOW)
269         {
270             dc->w.DCOrgX  = wndPtr->rectWindow.left;
271             dc->w.DCOrgY  = wndPtr->rectWindow.top;
272         }
273         else
274         {
275             dc->w.DCOrgX  = wndPtr->rectClient.left;
276             dc->w.DCOrgY  = wndPtr->rectClient.top;
277         }
278         while (!wndPtr->window)
279         {
280             wndPtr = WIN_FindWndPtr( wndPtr->hwndParent );
281             dc->w.DCOrgX += wndPtr->rectClient.left;
282             dc->w.DCOrgY += wndPtr->rectClient.top;
283         }
284         dc->w.DCOrgX -= wndPtr->rectWindow.left;
285         dc->w.DCOrgY -= wndPtr->rectWindow.top;
286         dc->u.x.drawable = wndPtr->window;
287     }
288 }
289
290
291 /***********************************************************************
292  *           GetDCEx    (USER.359)
293  */
294 /* Unimplemented flags: DCX_LOCKWINDOWUPDATE
295  */
296 HDC GetDCEx( HWND hwnd, HRGN hrgnClip, DWORD flags )
297 {
298     HANDLE hdce;
299     HRGN hrgnVisible;
300     HDC hdc = 0;
301     DCE * dce;
302     DC * dc;
303     WND * wndPtr;
304     
305     if (hwnd)
306     {
307         if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
308     }
309     else wndPtr = NULL;
310
311     if (flags & DCX_USESTYLE)
312     {
313         /* Set the flags according to the window style. */
314         /* Not sure if this is the real meaning of the DCX_USESTYLE flag... */
315         flags &= ~(DCX_CACHE | DCX_CLIPCHILDREN |
316                    DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
317         if (wndPtr)
318         {
319             if (!(WIN_CLASS_STYLE(wndPtr) & (CS_OWNDC | CS_CLASSDC)))
320                 flags |= DCX_CACHE;
321             if (WIN_CLASS_STYLE(wndPtr) & CS_PARENTDC) flags |= DCX_PARENTCLIP;
322             if (wndPtr->dwStyle & WS_CLIPCHILDREN) flags |= DCX_CLIPCHILDREN;
323             if (wndPtr->dwStyle & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
324         }
325         else flags |= DCX_CACHE;
326     }
327
328       /* Can only use PARENTCLIP on child windows */
329     if (!wndPtr || !(wndPtr->dwStyle & WS_CHILD)) flags &= ~DCX_PARENTCLIP;
330
331       /* Whole window DC implies using cache DC and not clipping children */
332     if (flags & DCX_WINDOW) flags = (flags & ~DCX_CLIPCHILDREN) | DCX_CACHE;
333
334     if (flags & DCX_CACHE)
335     {
336         for (hdce = firstDCE; (hdce); hdce = dce->hNext)
337         {
338             if (!(dce = (DCE *) USER_HEAP_LIN_ADDR( hdce ))) return 0;
339             if ((dce->type == DCE_CACHE_DC) && (!dce->inUse)) break;
340         }
341     }
342     else hdce = wndPtr->hdce;
343
344     if (!hdce) return 0;
345     dce = (DCE *) USER_HEAP_LIN_ADDR( hdce );
346     dce->hwndCurrent = hwnd;
347     dce->inUse       = TRUE;
348     hdc = dce->hdc;
349     
350       /* Initialize DC */
351     
352     if (!(dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC ))) return 0;
353
354     DCE_SetDrawable( wndPtr, dc, flags );
355     if (hwnd)
356     {
357         if (flags & DCX_PARENTCLIP)  /* Get a VisRgn for the parent */
358         {
359             WND *parentPtr = WIN_FindWndPtr( wndPtr->hwndParent );
360             DWORD newflags = flags & ~(DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN |
361                                        DCX_WINDOW);
362             if (parentPtr->dwStyle & WS_CLIPSIBLINGS)
363                 newflags |= DCX_CLIPSIBLINGS;
364             hrgnVisible = DCE_GetVisRgn( wndPtr->hwndParent, newflags );
365             if (flags & DCX_WINDOW)
366                 OffsetRgn( hrgnVisible, -wndPtr->rectWindow.left,
367                                         -wndPtr->rectWindow.top );
368             else OffsetRgn( hrgnVisible, -wndPtr->rectClient.left,
369                                          -wndPtr->rectClient.top );
370         }
371         else hrgnVisible = DCE_GetVisRgn( hwnd, flags );
372     }
373     else  /* Get a VisRgn for the whole screen */
374     {
375         hrgnVisible = CreateRectRgn( 0, 0, SYSMETRICS_CXSCREEN,
376                                      SYSMETRICS_CYSCREEN);
377     }
378
379       /* Intersect VisRgn with the given region */
380
381     if ((flags & DCX_INTERSECTRGN) || (flags & DCX_EXCLUDERGN))
382     {
383         HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
384         if (hrgn)
385         {
386             CombineRgn( hrgn, hrgnVisible, hrgnClip,
387                        (flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF );
388             DeleteObject( hrgnVisible );
389             hrgnVisible = hrgn;
390         }
391     }
392     SelectVisRgn( hdc, hrgnVisible );
393     DeleteObject( hrgnVisible );
394
395     dprintf_dc(stddeb, "GetDCEx(%d,%d,0x%lx): returning %d\n", 
396                hwnd, hrgnClip, flags, hdc);
397     return hdc;
398 }
399
400
401 /***********************************************************************
402  *           GetDC    (USER.66)
403  */
404 HDC GetDC( HWND hwnd )
405 {
406     return GetDCEx( hwnd, 0, DCX_USESTYLE );
407 }
408
409
410 /***********************************************************************
411  *           GetWindowDC    (USER.67)
412  */
413 HDC GetWindowDC( HWND hwnd )
414 {
415     int flags = DCX_CACHE | DCX_WINDOW;
416     if (hwnd)
417     {
418         WND * wndPtr;
419         if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
420 /*      if (wndPtr->dwStyle & WS_CLIPCHILDREN) flags |= DCX_CLIPCHILDREN; */
421         if (wndPtr->dwStyle & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
422     }
423     return GetDCEx( hwnd, 0, flags );
424 }
425
426
427 /***********************************************************************
428  *           ReleaseDC    (USER.68)
429  */
430 int ReleaseDC( HWND hwnd, HDC hdc )
431 {
432     HANDLE hdce;
433     DCE * dce = NULL;
434     
435     dprintf_dc(stddeb, "ReleaseDC: %d %d\n", hwnd, hdc );
436         
437     for (hdce = firstDCE; (hdce); hdce = dce->hNext)
438     {
439         if (!(dce = (DCE *) USER_HEAP_LIN_ADDR( hdce ))) return 0;
440         if (dce->inUse && (dce->hdc == hdc)) break;
441     }
442     if (!hdce) return 0;
443
444     if (dce->type == DCE_CACHE_DC)
445     {
446         SetDCState( dce->hdc, defaultDCstate );
447         dce->inUse = FALSE;
448     }
449     return 1;
450 }