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