Added ReleaseDC function to the USER driver interface.
[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 <string.h>
22
23 #include "windef.h"
24 #include "wingdi.h"
25 #include "wine/winuser16.h"
26 #include "wine/server.h"
27 #include "win.h"
28 #include "dce.h"
29 #include "wine/debug.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(win);
32
33 /***********************************************************************
34  *           add_paint_count
35  *
36  * Add an increment (normally 1 or -1) to the current paint count of a window.
37  */
38 static void add_paint_count( HWND hwnd, int incr )
39 {
40     SERVER_START_REQ( inc_window_paint_count )
41     {
42         req->handle = hwnd;
43         req->incr   = incr;
44         wine_server_call( req );
45     }
46     SERVER_END_REQ;
47 }
48
49
50 /***********************************************************************
51  *           copy_rgn
52  *
53  * copy a region, doing the right thing if the source region is 0 or 1
54  */
55 static HRGN copy_rgn( HRGN hSrc )
56 {
57     if (hSrc > 1)
58     {
59         HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
60         CombineRgn( hrgn, hSrc, 0, RGN_COPY );
61         return hrgn;
62     }
63     return hSrc;
64 }
65
66
67 /***********************************************************************
68  *           get_update_regions
69  *
70  * Return update regions for the whole window and for the client area.
71  */
72 static void get_update_regions( WND *win, HRGN *whole_rgn, HRGN *client_rgn )
73 {
74     if (win->hrgnUpdate > 1)
75     {
76         RECT client, update;
77
78         /* check if update rgn overlaps with nonclient area */
79         GetRgnBox( win->hrgnUpdate, &update );
80         client = win->rectClient;
81         OffsetRect( &client, -win->rectWindow.left, -win->rectWindow.top );
82
83         if (update.left < client.left || update.top < client.top ||
84             update.right > client.right || update.bottom > client.bottom)
85         {
86             *whole_rgn = copy_rgn( win->hrgnUpdate );
87             *client_rgn = CreateRectRgnIndirect( &client );
88             if (CombineRgn( *client_rgn, *client_rgn, win->hrgnUpdate, RGN_AND ) == NULLREGION)
89             {
90                 DeleteObject( *client_rgn );
91                 *client_rgn = 0;
92             }
93         }
94         else
95         {
96             *whole_rgn = 0;
97             *client_rgn = copy_rgn( win->hrgnUpdate );
98         }
99     }
100     else
101     {
102         *client_rgn = *whole_rgn = win->hrgnUpdate;  /* 0 or 1 */
103     }
104 }
105
106
107 /***********************************************************************
108  *           begin_ncpaint
109  *
110  * Send a WM_NCPAINT message from inside BeginPaint().
111  * Returns update region cropped to client rectangle (and in client coords),
112  * and clears window update region and internal paint flag.
113  */
114 static HRGN begin_ncpaint( HWND hwnd )
115 {
116     HRGN whole_rgn, client_rgn;
117     WND *wnd = WIN_GetPtr( hwnd );
118
119     if (!wnd || wnd == WND_OTHER_PROCESS) return 0;
120
121     TRACE("hwnd %04x [%04x] ncf %i\n",
122           hwnd, wnd->hrgnUpdate, wnd->flags & WIN_NEEDS_NCPAINT);
123
124     get_update_regions( wnd, &whole_rgn, &client_rgn );
125
126     if (whole_rgn) /* NOTE: WM_NCPAINT allows wParam to be 1 */
127     {
128         WIN_ReleasePtr( wnd );
129         SendMessageA( hwnd, WM_NCPAINT, whole_rgn, 0 );
130         if (whole_rgn > 1) DeleteObject( whole_rgn );
131         /* make sure the window still exists before continuing */
132         if (!(wnd = WIN_GetPtr( hwnd )) || wnd == WND_OTHER_PROCESS)
133         {
134             if (client_rgn > 1) DeleteObject( client_rgn );
135             return 0;
136         }
137     }
138
139     if (wnd->hrgnUpdate || (wnd->flags & WIN_INTERNAL_PAINT)) add_paint_count( hwnd, -1 );
140     if (wnd->hrgnUpdate > 1) DeleteObject( wnd->hrgnUpdate );
141     wnd->hrgnUpdate = 0;
142     wnd->flags &= ~(WIN_INTERNAL_PAINT | WIN_NEEDS_NCPAINT | WIN_NEEDS_BEGINPAINT);
143     if (client_rgn > 1) OffsetRgn( client_rgn, wnd->rectWindow.left - wnd->rectClient.left,
144                                    wnd->rectWindow.top - wnd->rectClient.top );
145     WIN_ReleasePtr( wnd );
146     return client_rgn;
147 }
148
149
150 /***********************************************************************
151  *              BeginPaint (USER32.@)
152  */
153 HDC WINAPI BeginPaint( HWND hwnd, PAINTSTRUCT *lps )
154 {
155     INT dcx_flags;
156     BOOL bIcon;
157     HRGN hrgnUpdate;
158     RECT clipRect, clientRect;
159     HWND full_handle;
160     WND *wndPtr;
161
162     if (!(full_handle = WIN_IsCurrentThread( hwnd )))
163     {
164         if (IsWindow(hwnd))
165             FIXME( "window %x belongs to other thread\n", hwnd );
166         return 0;
167     }
168     hwnd = full_handle;
169
170     /* send WM_NCPAINT and retrieve update region */
171     hrgnUpdate = begin_ncpaint( hwnd );
172     if (!hrgnUpdate && !IsWindow( hwnd )) return 0;
173
174     HideCaret( hwnd );
175
176     bIcon = (IsIconic(hwnd) && GetClassLongA(hwnd, GCL_HICON));
177
178     dcx_flags = DCX_INTERSECTRGN | DCX_WINDOWPAINT | DCX_USESTYLE;
179     if (bIcon) dcx_flags |= DCX_WINDOW;
180
181     if (GetClassLongA( hwnd, GCL_STYLE ) & CS_PARENTDC)
182     {
183         /* Don't clip the output to the update region for CS_PARENTDC window */
184         if (hrgnUpdate > 1) DeleteObject( hrgnUpdate );
185         hrgnUpdate = 0;
186         dcx_flags &= ~DCX_INTERSECTRGN;
187     }
188     else
189     {
190         if (!hrgnUpdate)  /* empty region, clip everything */
191         {
192             hrgnUpdate = CreateRectRgn( 0, 0, 0, 0 );
193         }
194         else if (hrgnUpdate == 1)  /* whole client area, don't clip */
195         {
196             hrgnUpdate = 0;
197             dcx_flags &= ~DCX_INTERSECTRGN;
198         }
199     }
200     lps->hdc = GetDCEx( hwnd, hrgnUpdate, dcx_flags );
201     /* ReleaseDC() in EndPaint() will delete the region */
202
203     if (!lps->hdc)
204     {
205         WARN("GetDCEx() failed in BeginPaint(), hwnd=%04x\n", hwnd);
206         DeleteObject( hrgnUpdate );
207         return 0;
208     }
209
210     /* It is possible that the clip box is bigger than the window itself,
211        if CS_PARENTDC flag is set. Windows never return a paint rect bigger
212        than the window itself, so we need to intersect the cliprect with
213        the window  */
214     GetClientRect( hwnd, &clientRect );
215
216     GetClipBox( lps->hdc, &clipRect );
217     LPtoDP(lps->hdc, (LPPOINT)&clipRect, 2);      /* GetClipBox returns LP */
218
219     IntersectRect(&lps->rcPaint, &clientRect, &clipRect);
220     DPtoLP(lps->hdc, (LPPOINT)&lps->rcPaint, 2);  /* we must return LP */
221
222     TRACE("hdc = %x box = (%i,%i - %i,%i)\n",
223           lps->hdc, lps->rcPaint.left, lps->rcPaint.top, lps->rcPaint.right, lps->rcPaint.bottom );
224
225     if (!(wndPtr = WIN_GetPtr( hwnd )) || wndPtr == WND_OTHER_PROCESS) return 0;
226     lps->fErase = !(wndPtr->flags & WIN_NEEDS_ERASEBKGND);
227     wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
228     WIN_ReleasePtr( wndPtr );
229
230     if (!lps->fErase)
231         lps->fErase = !SendMessageA( hwnd, bIcon ? WM_ICONERASEBKGND : WM_ERASEBKGND,
232                                      (WPARAM)lps->hdc, 0 );
233     return lps->hdc;
234 }
235
236
237 /***********************************************************************
238  *              EndPaint (USER32.@)
239  */
240 BOOL WINAPI EndPaint( HWND hwnd, const PAINTSTRUCT *lps )
241 {
242     ReleaseDC( hwnd, lps->hdc );
243     ShowCaret( hwnd );
244     return TRUE;
245 }