Fixed a few more headers dependency issues.
[wine] / dlls / user / painting.c
1 /*
2  * Window painting functions
3  *
4  * Copyright 1993, 1994, 1995, 2001 Alexandre Julliard
5  * Copyright 1999 Alex Korobka
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 #include <stdarg.h>
22 #include <string.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "wine/winuser16.h"
28 #include "wine/server.h"
29 #include "win.h"
30 #include "dce.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(win);
34
35 /***********************************************************************
36  *           add_paint_count
37  *
38  * Add an increment (normally 1 or -1) to the current paint count of a window.
39  */
40 static void add_paint_count( HWND hwnd, int incr )
41 {
42     SERVER_START_REQ( inc_window_paint_count )
43     {
44         req->handle = hwnd;
45         req->incr   = incr;
46         wine_server_call( req );
47     }
48     SERVER_END_REQ;
49 }
50
51
52 /***********************************************************************
53  *           copy_rgn
54  *
55  * copy a region, doing the right thing if the source region is 0 or 1
56  */
57 static HRGN copy_rgn( HRGN hSrc )
58 {
59     if (hSrc > (HRGN)1)
60     {
61         HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
62         CombineRgn( hrgn, hSrc, 0, RGN_COPY );
63         return hrgn;
64     }
65     return hSrc;
66 }
67
68
69 /***********************************************************************
70  *           get_update_regions
71  *
72  * Return update regions for the whole window and for the client area.
73  */
74 static void get_update_regions( WND *win, HRGN *whole_rgn, HRGN *client_rgn )
75 {
76     if (win->hrgnUpdate > (HRGN)1)
77     {
78         RECT client, update;
79
80         /* check if update rgn overlaps with nonclient area */
81         GetRgnBox( win->hrgnUpdate, &update );
82         client = win->rectClient;
83         OffsetRect( &client, -win->rectWindow.left, -win->rectWindow.top );
84
85         if (update.left < client.left || update.top < client.top ||
86             update.right > client.right || update.bottom > client.bottom)
87         {
88             *whole_rgn = copy_rgn( win->hrgnUpdate );
89             *client_rgn = CreateRectRgnIndirect( &client );
90             if (CombineRgn( *client_rgn, *client_rgn, win->hrgnUpdate, RGN_AND ) == NULLREGION)
91             {
92                 DeleteObject( *client_rgn );
93                 *client_rgn = 0;
94             }
95         }
96         else
97         {
98             *whole_rgn = 0;
99             *client_rgn = copy_rgn( win->hrgnUpdate );
100         }
101     }
102     else
103     {
104         *client_rgn = *whole_rgn = win->hrgnUpdate;  /* 0 or 1 */
105     }
106 }
107
108
109 /***********************************************************************
110  *           begin_ncpaint
111  *
112  * Send a WM_NCPAINT message from inside BeginPaint().
113  * Returns update region cropped to client rectangle (and in client coords),
114  * and clears window update region and internal paint flag.
115  */
116 static HRGN begin_ncpaint( HWND hwnd )
117 {
118     HRGN whole_rgn, client_rgn;
119     WND *wnd = WIN_GetPtr( hwnd );
120
121     if (!wnd || wnd == WND_OTHER_PROCESS) return 0;
122
123     TRACE("hwnd %p [%p] ncf %i\n",
124           hwnd, wnd->hrgnUpdate, wnd->flags & WIN_NEEDS_NCPAINT);
125
126     get_update_regions( wnd, &whole_rgn, &client_rgn );
127
128     if (whole_rgn) /* NOTE: WM_NCPAINT allows wParam to be 1 */
129     {
130         WIN_ReleasePtr( wnd );
131         SendMessageA( hwnd, WM_NCPAINT, (WPARAM)whole_rgn, 0 );
132         if (whole_rgn > (HRGN)1) DeleteObject( whole_rgn );
133         /* make sure the window still exists before continuing */
134         if (!(wnd = WIN_GetPtr( hwnd )) || wnd == WND_OTHER_PROCESS)
135         {
136             if (client_rgn > (HRGN)1) DeleteObject( client_rgn );
137             return 0;
138         }
139     }
140
141     if (wnd->hrgnUpdate || (wnd->flags & WIN_INTERNAL_PAINT)) add_paint_count( hwnd, -1 );
142     if (wnd->hrgnUpdate > (HRGN)1) DeleteObject( wnd->hrgnUpdate );
143     wnd->hrgnUpdate = 0;
144     wnd->flags &= ~(WIN_INTERNAL_PAINT | WIN_NEEDS_NCPAINT | WIN_NEEDS_BEGINPAINT);
145     if (client_rgn > (HRGN)1) OffsetRgn( client_rgn, wnd->rectWindow.left - wnd->rectClient.left,
146                                          wnd->rectWindow.top - wnd->rectClient.top );
147     WIN_ReleasePtr( wnd );
148     return client_rgn;
149 }
150
151
152 /***********************************************************************
153  *              BeginPaint (USER32.@)
154  */
155 HDC WINAPI BeginPaint( HWND hwnd, PAINTSTRUCT *lps )
156 {
157     INT dcx_flags;
158     BOOL bIcon;
159     HRGN hrgnUpdate;
160     RECT clipRect, clientRect;
161     HWND full_handle;
162     WND *wndPtr;
163
164     if (!lps) return 0;
165
166     if (!(full_handle = WIN_IsCurrentThread( hwnd )))
167     {
168         if (IsWindow(hwnd))
169             FIXME( "window %p belongs to other thread\n", hwnd );
170         return 0;
171     }
172     hwnd = full_handle;
173
174     /* send WM_NCPAINT and retrieve update region */
175     hrgnUpdate = begin_ncpaint( hwnd );
176     if (!hrgnUpdate && !IsWindow( hwnd )) return 0;
177
178     HideCaret( hwnd );
179
180     bIcon = (IsIconic(hwnd) && GetClassLongA(hwnd, GCL_HICON));
181
182     dcx_flags = DCX_INTERSECTRGN | DCX_WINDOWPAINT | DCX_USESTYLE;
183     if (bIcon) dcx_flags |= DCX_WINDOW;
184
185     if (GetClassLongA( hwnd, GCL_STYLE ) & CS_PARENTDC)
186     {
187         /* Don't clip the output to the update region for CS_PARENTDC window */
188         if (hrgnUpdate > (HRGN)1) DeleteObject( hrgnUpdate );
189         hrgnUpdate = 0;
190         dcx_flags &= ~DCX_INTERSECTRGN;
191     }
192     else
193     {
194         if (!hrgnUpdate)  /* empty region, clip everything */
195         {
196             hrgnUpdate = CreateRectRgn( 0, 0, 0, 0 );
197         }
198         else if (hrgnUpdate == (HRGN)1)  /* whole client area, don't clip */
199         {
200             hrgnUpdate = 0;
201             dcx_flags &= ~DCX_INTERSECTRGN;
202         }
203     }
204     lps->hdc = GetDCEx( hwnd, hrgnUpdate, dcx_flags );
205     /* ReleaseDC() in EndPaint() will delete the region */
206
207     if (!lps->hdc)
208     {
209         WARN("GetDCEx() failed in BeginPaint(), hwnd=%p\n", hwnd);
210         DeleteObject( hrgnUpdate );
211         return 0;
212     }
213
214     /* It is possible that the clip box is bigger than the window itself,
215        if CS_PARENTDC flag is set. Windows never return a paint rect bigger
216        than the window itself, so we need to intersect the cliprect with
217        the window  */
218     GetClientRect( hwnd, &clientRect );
219
220     GetClipBox( lps->hdc, &clipRect );
221     LPtoDP(lps->hdc, (LPPOINT)&clipRect, 2);      /* GetClipBox returns LP */
222
223     IntersectRect(&lps->rcPaint, &clientRect, &clipRect);
224     DPtoLP(lps->hdc, (LPPOINT)&lps->rcPaint, 2);  /* we must return LP */
225
226     TRACE("hdc = %p box = (%ld,%ld - %ld,%ld)\n",
227           lps->hdc, lps->rcPaint.left, lps->rcPaint.top, lps->rcPaint.right, lps->rcPaint.bottom );
228
229     if (!(wndPtr = WIN_GetPtr( hwnd )) || wndPtr == WND_OTHER_PROCESS) return 0;
230     lps->fErase = (wndPtr->flags & WIN_NEEDS_ERASEBKGND) != 0;
231     wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
232     WIN_ReleasePtr( wndPtr );
233
234     if (lps->fErase)
235         lps->fErase = !SendMessageA( hwnd, bIcon ? WM_ICONERASEBKGND : WM_ERASEBKGND,
236                                      (WPARAM)lps->hdc, 0 );
237
238     TRACE("hdc = %p box = (%ld,%ld - %ld,%ld), fErase = %d\n",
239           lps->hdc, lps->rcPaint.left, lps->rcPaint.top, lps->rcPaint.right, lps->rcPaint.bottom,
240           lps->fErase);
241
242     return lps->hdc;
243 }
244
245
246 /***********************************************************************
247  *              EndPaint (USER32.@)
248  */
249 BOOL WINAPI EndPaint( HWND hwnd, const PAINTSTRUCT *lps )
250 {
251     if (!lps) return FALSE;
252
253     ReleaseDC( hwnd, lps->hdc );
254     ShowCaret( hwnd );
255     return TRUE;
256 }