- DDRAW_SYSTEMMEMORY is handled like OFFSCREENPLAIN for now
[wine] / dlls / x11drv / scroll.c
1 /*
2  * Scroll windows and DCs
3  *
4  * Copyright 1993  David W. Metcalfe
5  * Copyright 1995, 1996 Alex Korobka
6  * Copyright 2001 Alexandre Julliard
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24
25 #include "ts_xlib.h"
26 #include "ts_xutil.h"
27
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31
32 #include "x11drv.h"
33 #include "win.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(scroll);
37
38
39 /*************************************************************************
40  *             fix_caret
41  */
42 static BOOL fix_caret(HWND hWnd, LPRECT lprc, UINT flags)
43 {
44    HWND hCaret = CARET_GetHwnd();
45
46    if( hCaret )
47    {
48        RECT rc;
49        CARET_GetRect( &rc );
50        if( hCaret == hWnd ||
51           (flags & SW_SCROLLCHILDREN && IsChild(hWnd, hCaret)) )
52        {
53            POINT pt;
54            pt.x = rc.left;
55            pt.y = rc.top;
56            MapWindowPoints( hCaret, hWnd, (LPPOINT)&rc, 2 );
57            if( IntersectRect(lprc, lprc, &rc) )
58            {
59                HideCaret(0);
60                lprc->left = pt.x;
61                lprc->top = pt.y;
62                return TRUE;
63            }
64        }
65    }
66    return FALSE;
67 }
68
69
70 /*************************************************************************
71  *              ScrollDC   (X11DRV.@)
72  *
73  * Only the hrgnUpdate is returned in device coordinates.
74  * rcUpdate must be returned in logical coordinates to comply with win API.
75  * FIXME: the doc explicitly states the opposite, to be checked
76  */
77 BOOL X11DRV_ScrollDC( HDC hdc, INT dx, INT dy, const RECT *rc,
78                       const RECT *clipRect, HRGN hrgnUpdate, LPRECT rcUpdate )
79 {
80     RECT rect, rClip, rDst;
81
82     TRACE( "%04x %d,%d hrgnUpdate=%04x rcUpdate = %p\n", hdc, dx, dy, hrgnUpdate, rcUpdate );
83     if (clipRect) TRACE( "cliprc = (%d,%d,%d,%d)\n",
84                          clipRect->left, clipRect->top, clipRect->right, clipRect->bottom );
85     if (rc) TRACE( "rc = (%d,%d,%d,%d)\n", rc->left, rc->top, rc->right, rc->bottom );
86
87     /* compute device clipping region (in device coordinates) */
88
89     if (rc) rect = *rc;
90     else GetClipBox( hdc, &rect );
91
92     if (clipRect)
93     {
94         rClip = *clipRect;
95         IntersectRect( &rClip, &rect, &rClip );
96     }
97     else rClip = rect;
98
99     rDst = rClip;
100     OffsetRect( &rDst, dx,  dy );
101     IntersectRect( &rDst, &rDst, &rClip );
102
103     if (!IsRectEmpty(&rDst))
104     {
105         /* copy bits */
106         if (!BitBlt( hdc, rDst.left, rDst.top,
107                      rDst.right - rDst.left, rDst.bottom - rDst.top,
108                      hdc, rDst.left - dx, rDst.top - dy, SRCCOPY))
109             return FALSE;
110     }
111
112     /* compute update areas */
113
114     if (hrgnUpdate || rcUpdate)
115     {
116         HRGN hrgn = hrgnUpdate, hrgn2;
117
118         /* map everything to device coordinates */
119         LPtoDP( hdc, (LPPOINT)&rClip, 2 );
120         LPtoDP( hdc, (LPPOINT)&rDst, 2 );
121
122         hrgn2 = CreateRectRgnIndirect( &rDst );
123         if (hrgn) SetRectRgn( hrgn, rClip.left, rClip.top, rClip.right, rClip.bottom );
124         else hrgn = CreateRectRgn( rClip.left, rClip.top, rClip.right, rClip.bottom );
125         CombineRgn( hrgn, hrgn, hrgn2, RGN_DIFF );
126
127         if( rcUpdate )
128         {
129             GetRgnBox( hrgn, rcUpdate );
130
131             /* Put the rcUpdate in logical coordinate */
132             DPtoLP( hdc, (LPPOINT)rcUpdate, 2 );
133         }
134         if (!hrgnUpdate) DeleteObject( hrgn );
135         DeleteObject( hrgn2 );
136     }
137     return TRUE;
138 }
139
140
141 /*************************************************************************
142  *              ScrollWindowEx   (X11DRV.@)
143  *
144  * Note: contrary to what the doc says, pixels that are scrolled from the
145  *      outside of clipRect to the inside are NOT painted.
146  */
147 INT X11DRV_ScrollWindowEx( HWND hwnd, INT dx, INT dy,
148                            const RECT *rect, const RECT *clipRect,
149                            HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags )
150 {
151     INT  retVal = NULLREGION;
152     BOOL bCaret = FALSE, bOwnRgn = TRUE;
153     RECT rc, cliprc;
154
155     if (!WIN_IsWindowDrawable( hwnd, TRUE )) return ERROR;
156     hwnd = WIN_GetFullHandle( hwnd );
157
158     GetClientRect(hwnd, &rc);
159     if (rect) IntersectRect(&rc, &rc, rect);
160
161     if (clipRect) IntersectRect(&cliprc,&rc,clipRect);
162     else cliprc = rc;
163
164     if (!IsRectEmpty(&cliprc) && (dx || dy))
165     {
166         HDC   hDC;
167         BOOL  bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
168         HRGN  hrgnClip = CreateRectRgnIndirect(&cliprc);
169         HRGN  hrgnTemp;
170         RECT  caretrc;
171
172         TRACE( "%04x, %d,%d hrgnUpdate=%04x rcUpdate = %p rc=(%d,%d-%d,%d) %04x\n",
173                hwnd, dx, dy, hrgnUpdate, rcUpdate,
174                rc.left, rc.top, rc.right, rc.bottom, flags );
175         if (clipRect) TRACE( "cliprc = (%d,%d,%d,%d)\n",
176                              clipRect->left, clipRect->top,
177                              clipRect->right, clipRect->bottom );
178
179         caretrc = rc;
180         bCaret = fix_caret(hwnd, &caretrc, flags);
181
182         if( hrgnUpdate ) bOwnRgn = FALSE;
183         else if( bUpdate ) hrgnUpdate = CreateRectRgn( 0, 0, 0, 0 );
184
185         hDC = GetDCEx( hwnd, 0, DCX_CACHE | DCX_USESTYLE );
186         if (hDC)
187         {
188             HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
189             X11DRV_StartGraphicsExposures( hDC );
190             X11DRV_ScrollDC( hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate );
191             X11DRV_EndGraphicsExposures( hDC, hrgn );
192             ReleaseDC( hwnd, hDC );
193             if (bUpdate) CombineRgn( hrgnUpdate, hrgnUpdate, hrgn, RGN_OR );
194             else RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_ERASE );
195             DeleteObject( hrgn );
196         }
197
198         /* Take into account the fact that some damages may have occured during the scroll */
199         hrgnTemp = CreateRectRgn( 0, 0, 0, 0 );
200         if (GetUpdateRgn( hwnd, hrgnTemp, FALSE ) != NULLREGION)
201         {
202             OffsetRgn( hrgnTemp, dx, dy );
203             CombineRgn( hrgnTemp, hrgnTemp, hrgnClip, RGN_AND );
204             RedrawWindow( hwnd, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE );
205         }
206         DeleteObject( hrgnTemp );
207
208         if( flags & SW_SCROLLCHILDREN )
209         {
210             HWND *list = WIN_ListChildren( hwnd );
211
212             if (list)
213             {
214                 int i;
215                 RECT r, dummy;
216                 for (i = 0; list[i]; i++)
217                 {
218                     GetWindowRect( list[i], &r );
219                     MapWindowPoints( 0, hwnd, (POINT *)&r, 2 );
220                     if (!rect || IntersectRect(&dummy, &r, &rc))
221                         SetWindowPos( list[i], 0, r.left + dx, r.top  + dy, 0, 0,
222                                       SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
223                                       SWP_NOREDRAW | SWP_DEFERERASE );
224                 }
225                 HeapFree( GetProcessHeap(), 0, list );
226             }
227         }
228
229         if( flags & (SW_INVALIDATE | SW_ERASE) )
230             RedrawWindow( hwnd, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE |
231                           ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
232                           ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0 ) );
233
234         if( bCaret )
235         {
236             SetCaretPos( caretrc.left + dx, caretrc.top + dy );
237             ShowCaret(0);
238         }
239
240         if( bOwnRgn && hrgnUpdate ) DeleteObject( hrgnUpdate );
241         DeleteObject( hrgnClip );
242     }
243     return retVal;
244 }