Release 960506
[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->parent)
112     {
113         wndPtr = wndPtr->parent;
114         if (!(wndPtr->dwStyle & WS_VISIBLE) ||
115             (wndPtr->flags & WIN_NO_REDRAW) ||
116             (wndPtr->dwStyle & WS_ICONIC))
117         {
118             SetRectEmpty( lprect );  /* Clip everything */
119             return FALSE;
120         }
121         xoffset += wndPtr->rectClient.left;
122         yoffset += wndPtr->rectClient.top;
123         OffsetRect( lprect, wndPtr->rectClient.left,
124                     wndPtr->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, &wndPtr->rectClient ))
129             return FALSE;  /* Visible rectangle is empty */
130     }
131     OffsetRect( lprect, -xoffset, -yoffset );
132     return TRUE;
133 }
134
135
136 /***********************************************************************
137  *           DCE_ClipWindows
138  *
139  * Go through the linked list of windows from hwndStart to hwndEnd,
140  * removing from the given region the rectangle of each window offset
141  * by a given amount.  The new region is returned, and the original one
142  * is destroyed.  Used to implement DCX_CLIPSIBLINGS and
143  * DCX_CLIPCHILDREN styles.
144  */
145 static HRGN DCE_ClipWindows( WND *pWndStart, WND *pWndEnd,
146                              HRGN hrgn, int xoffset, int yoffset )
147 {
148     HRGN hrgnNew;
149
150     if (!pWndStart) return hrgn;
151     if (!(hrgnNew = CreateRectRgn( 0, 0, 0, 0 )))
152     {
153         DeleteObject( hrgn );
154         return 0;
155     }
156     for (; pWndStart != pWndEnd; pWndStart = pWndStart->next)
157     {
158         if (!(pWndStart->dwStyle & WS_VISIBLE)) continue;
159         SetRectRgn( hrgnNew, pWndStart->rectWindow.left + xoffset,
160                     pWndStart->rectWindow.top + yoffset,
161                     pWndStart->rectWindow.right + xoffset,
162                     pWndStart->rectWindow.bottom + yoffset );
163         if (!CombineRgn( hrgn, hrgn, hrgnNew, RGN_DIFF )) break;
164     }
165     DeleteObject( hrgnNew );
166     if (pWndStart != pWndEnd)  /* something went wrong */
167     {
168         DeleteObject( hrgn );
169         return 0;
170     }
171     return hrgn;
172 }
173
174
175 /***********************************************************************
176  *           DCE_GetVisRgn
177  *
178  * Return the visible region of a window, i.e. the client or window area
179  * clipped by the client area of all ancestors, and then optionally
180  * by siblings and children.
181  */
182 HRGN DCE_GetVisRgn( HWND hwnd, WORD flags )
183 {
184     RECT rect;
185     HRGN hrgn;
186     int xoffset, yoffset;
187     WND *wndPtr = WIN_FindWndPtr( hwnd );
188
189       /* Get visible rectangle and create a region with it */
190
191     if (!DCE_GetVisRect( wndPtr, !(flags & DCX_WINDOW), &rect ))
192     {
193         return CreateRectRgn( 0, 0, 0, 0 );  /* Visible region is empty */
194     }
195     if (!(hrgn = CreateRectRgnIndirect( &rect ))) return 0;
196
197       /* Clip all children from the visible region */
198
199     if (flags & DCX_CLIPCHILDREN)
200     {
201         if (flags & DCX_WINDOW)
202         {
203             xoffset = wndPtr->rectClient.left - wndPtr->rectWindow.left;
204             yoffset = wndPtr->rectClient.top - wndPtr->rectWindow.top;
205         }
206         else xoffset = yoffset = 0;
207         hrgn = DCE_ClipWindows( wndPtr->child, NULL, hrgn, xoffset, yoffset );
208         if (!hrgn) return 0;
209     }
210
211       /* Clip siblings placed above this window */
212
213     if (flags & DCX_WINDOW)
214     {
215         xoffset = -wndPtr->rectWindow.left;
216         yoffset = -wndPtr->rectWindow.top;
217     }
218     else
219     {
220         xoffset = -wndPtr->rectClient.left;
221         yoffset = -wndPtr->rectClient.top;
222     }
223     if (flags & DCX_CLIPSIBLINGS)
224     {
225         hrgn = DCE_ClipWindows( wndPtr->parent ? wndPtr->parent->child : NULL,
226                                 wndPtr, hrgn, xoffset, yoffset );
227         if (!hrgn) return 0;
228     }
229
230       /* Clip siblings of all ancestors that have the WS_CLIPSIBLINGS style */
231
232     while (wndPtr->dwStyle & WS_CHILD)
233     {
234         wndPtr = wndPtr->parent;
235         xoffset -= wndPtr->rectClient.left;
236         yoffset -= wndPtr->rectClient.top;
237         hrgn = DCE_ClipWindows( wndPtr->parent->child, wndPtr,
238                                 hrgn, xoffset, yoffset );
239         if (!hrgn) return 0;
240     }
241     return hrgn;
242 }
243
244
245 /***********************************************************************
246  *           DCE_SetDrawable
247  *
248  * Set the drawable, origin and dimensions for the DC associated to
249  * a given window.
250  */
251 static void DCE_SetDrawable( WND *wndPtr, DC *dc, WORD flags )
252 {
253     if (!wndPtr)  /* Get a DC for the whole screen */
254     {
255         dc->w.DCOrgX = 0;
256         dc->w.DCOrgY = 0;
257         dc->u.x.drawable = rootWindow;
258         XSetSubwindowMode( display, dc->u.x.gc, IncludeInferiors );
259     }
260     else
261     {
262         if (flags & DCX_WINDOW)
263         {
264             dc->w.DCOrgX  = wndPtr->rectWindow.left;
265             dc->w.DCOrgY  = wndPtr->rectWindow.top;
266         }
267         else
268         {
269             dc->w.DCOrgX  = wndPtr->rectClient.left;
270             dc->w.DCOrgY  = wndPtr->rectClient.top;
271         }
272         while (!wndPtr->window)
273         {
274             wndPtr = wndPtr->parent;
275             dc->w.DCOrgX += wndPtr->rectClient.left;
276             dc->w.DCOrgY += wndPtr->rectClient.top;
277         }
278         dc->w.DCOrgX -= wndPtr->rectWindow.left;
279         dc->w.DCOrgY -= wndPtr->rectWindow.top;
280         dc->u.x.drawable = wndPtr->window;
281     }
282 }
283
284
285 /***********************************************************************
286  *           GetDCEx    (USER.359)
287  */
288 /* Unimplemented flags: DCX_LOCKWINDOWUPDATE
289  */
290 HDC GetDCEx( HWND hwnd, HRGN hrgnClip, DWORD flags )
291 {
292     HANDLE hdce;
293     HRGN hrgnVisible;
294     HDC hdc = 0;
295     DCE * dce;
296     DC * dc;
297     WND * wndPtr;
298     
299     if (hwnd)
300     {
301         if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
302     }
303     else wndPtr = NULL;
304
305     if (flags & DCX_USESTYLE)
306     {
307         /* Set the flags according to the window style. */
308         /* Not sure if this is the real meaning of the DCX_USESTYLE flag... */
309         flags &= ~(DCX_CACHE | DCX_CLIPCHILDREN |
310                    DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
311         if (wndPtr)
312         {
313             if (!(wndPtr->class->style & (CS_OWNDC | CS_CLASSDC)))
314                 flags |= DCX_CACHE;
315             if (wndPtr->class->style & CS_PARENTDC) flags |= DCX_PARENTCLIP;
316             if (wndPtr->dwStyle & WS_CLIPCHILDREN) flags |= DCX_CLIPCHILDREN;
317             if (wndPtr->dwStyle & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
318         }
319         else flags |= DCX_CACHE;
320     }
321
322       /* Can only use PARENTCLIP on child windows */
323     if (!wndPtr || !(wndPtr->dwStyle & WS_CHILD)) flags &= ~DCX_PARENTCLIP;
324
325       /* Whole window DC implies using cache DC and not clipping children */
326     if (flags & DCX_WINDOW) flags = (flags & ~DCX_CLIPCHILDREN) | DCX_CACHE;
327
328     if (flags & DCX_CACHE)
329     {
330         for (hdce = firstDCE; (hdce); hdce = dce->hNext)
331         {
332             if (!(dce = (DCE *) USER_HEAP_LIN_ADDR( hdce ))) return 0;
333             if ((dce->type == DCE_CACHE_DC) && (!dce->inUse)) break;
334         }
335     }
336     else hdce = wndPtr->hdce;
337
338     if (!hdce) return 0;
339     dce = (DCE *) USER_HEAP_LIN_ADDR( hdce );
340     dce->hwndCurrent = hwnd;
341     dce->inUse       = TRUE;
342     hdc = dce->hdc;
343     
344       /* Initialize DC */
345     
346     if (!(dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC ))) return 0;
347
348     DCE_SetDrawable( wndPtr, dc, flags );
349     if (hwnd)
350     {
351         if (flags & DCX_PARENTCLIP)  /* Get a VisRgn for the parent */
352         {
353             WND *parentPtr = wndPtr->parent;
354             DWORD newflags = flags & ~(DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN |
355                                        DCX_WINDOW);
356             if (parentPtr->dwStyle & WS_CLIPSIBLINGS)
357                 newflags |= DCX_CLIPSIBLINGS;
358             hrgnVisible = DCE_GetVisRgn( parentPtr->hwndSelf, newflags );
359             if (flags & DCX_WINDOW)
360                 OffsetRgn( hrgnVisible, -wndPtr->rectWindow.left,
361                                         -wndPtr->rectWindow.top );
362             else OffsetRgn( hrgnVisible, -wndPtr->rectClient.left,
363                                          -wndPtr->rectClient.top );
364         }
365         else hrgnVisible = DCE_GetVisRgn( hwnd, flags );
366     }
367     else  /* Get a VisRgn for the whole screen */
368     {
369         hrgnVisible = CreateRectRgn( 0, 0, SYSMETRICS_CXSCREEN,
370                                      SYSMETRICS_CYSCREEN);
371     }
372
373       /* Intersect VisRgn with the given region */
374
375     if ((flags & DCX_INTERSECTRGN) || (flags & DCX_EXCLUDERGN))
376     {
377         CombineRgn( hrgnVisible, hrgnVisible, hrgnClip,
378                     (flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF );
379     }
380     SelectVisRgn( hdc, hrgnVisible );
381     DeleteObject( hrgnVisible );
382
383     dprintf_dc(stddeb, "GetDCEx(%04x,%04x,0x%lx): returning %04x\n", 
384                hwnd, hrgnClip, flags, hdc);
385     return hdc;
386 }
387
388
389 /***********************************************************************
390  *           GetDC    (USER.66)
391  */
392 HDC GetDC( HWND hwnd )
393 {
394     return GetDCEx( hwnd, 0, DCX_USESTYLE );
395 }
396
397
398 /***********************************************************************
399  *           GetWindowDC    (USER.67)
400  */
401 HDC GetWindowDC( HWND hwnd )
402 {
403     int flags = DCX_CACHE | DCX_WINDOW;
404     if (hwnd)
405     {
406         WND * wndPtr;
407         if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
408 /*      if (wndPtr->dwStyle & WS_CLIPCHILDREN) flags |= DCX_CLIPCHILDREN; */
409         if (wndPtr->dwStyle & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
410     }
411     return GetDCEx( hwnd, 0, flags );
412 }
413
414
415 /***********************************************************************
416  *           ReleaseDC    (USER.68)
417  */
418 int ReleaseDC( HWND hwnd, HDC hdc )
419 {
420     HANDLE hdce;
421     DCE * dce = NULL;
422     
423     dprintf_dc(stddeb, "ReleaseDC: %04x %04x\n", hwnd, hdc );
424         
425     for (hdce = firstDCE; (hdce); hdce = dce->hNext)
426     {
427         if (!(dce = (DCE *) USER_HEAP_LIN_ADDR( hdce ))) return 0;
428         if (dce->inUse && (dce->hdc == hdc)) break;
429     }
430     if (!hdce) return 0;
431
432     if (dce->type == DCE_CACHE_DC)
433     {
434         SetDCState( dce->hdc, defaultDCstate );
435         dce->inUse = FALSE;
436     }
437     return 1;
438 }