winemac: Implement WGL_EXT_swap_control.
[wine] / dlls / winemac.drv / scroll.c
1 /*
2  * MACDRV window/DC scrolling
3  *
4  * Copyright 1993  David W. Metcalfe
5  * Copyright 1995, 1996 Alex Korobka
6  * Copyright 2001 Alexandre Julliard
7  * Copyright 2011, 2013 Ken Thomases for CodeWeavers Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25
26 #include "macdrv.h"
27 #include "winuser.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(scroll);
30
31
32 static void dump_region(const char *p, HRGN hrgn)
33 {
34     DWORD i, size;
35     RGNDATA *data = NULL;
36     RECT *rect;
37
38     if (!hrgn)
39     {
40         TRACE("%s null region\n", p);
41         return;
42     }
43     if (!(size = GetRegionData(hrgn, 0, NULL)))
44         return;
45     if (!(data = HeapAlloc(GetProcessHeap(), 0, size))) return;
46     GetRegionData(hrgn, size, data);
47     TRACE("%s %d rects:", p, data->rdh.nCount);
48     for (i = 0, rect = (RECT *)data->Buffer; i<20 && i < data->rdh.nCount; i++, rect++)
49         TRACE(" %s", wine_dbgstr_rect(rect));
50     TRACE("\n");
51     HeapFree(GetProcessHeap(), 0, data);
52 }
53
54
55 /*************************************************************************
56  *              ScrollDC   (MACDRV.@)
57  */
58 BOOL CDECL macdrv_ScrollDC(HDC hdc, INT dx, INT dy, const RECT *lprcScroll,
59                            const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
60 {
61     RECT rcSrc, rcClip, offset;
62     INT dxdev, dydev, res;
63     HRGN DstRgn, clipRgn, visrgn;
64
65     TRACE("dx,dy %d,%d rcScroll %s rcClip %s hrgnUpdate %p lprcUpdate %p\n",
66           dx, dy, wine_dbgstr_rect(lprcScroll), wine_dbgstr_rect(lprcClip),
67           hrgnUpdate, lprcUpdate);
68
69     /* get the visible region */
70     visrgn = CreateRectRgn(0, 0, 0, 0);
71     GetRandomRgn(hdc, visrgn, SYSRGN);
72     if (!(GetVersion() & 0x80000000))
73     {
74         /* Window NT/2k/XP */
75         POINT org;
76         GetDCOrgEx(hdc, &org);
77         OffsetRgn(visrgn, -org.x, -org.y);
78     }
79
80     /* intersect with the clipping Region if the DC has one */
81     clipRgn = CreateRectRgn(0, 0, 0, 0);
82     if (GetClipRgn(hdc, clipRgn) != 1)
83     {
84         DeleteObject(clipRgn);
85         clipRgn = NULL;
86     }
87     else
88         CombineRgn(visrgn, visrgn, clipRgn, RGN_AND);
89
90     /* only those pixels in the scroll rectangle that remain in the clipping
91      * rect are scrolled. */
92     if (lprcClip)
93         rcClip = *lprcClip;
94     else
95         GetClipBox(hdc, &rcClip);
96     rcSrc = rcClip;
97     OffsetRect(&rcClip, -dx, -dy);
98     IntersectRect(&rcSrc, &rcSrc, &rcClip);
99
100     /* if an scroll rectangle is specified, only the pixels within that
101      * rectangle are scrolled */
102     if (lprcScroll)
103         IntersectRect(&rcSrc, &rcSrc, lprcScroll);
104
105     /* now convert to device coordinates */
106     LPtoDP(hdc, (LPPOINT)&rcSrc, 2);
107     TRACE("source rect: %s\n", wine_dbgstr_rect(&rcSrc));
108
109     /* also dx and dy */
110     SetRect(&offset, 0, 0, dx, dy);
111     LPtoDP(hdc, (LPPOINT)&offset, 2);
112     dxdev = offset.right - offset.left;
113     dydev = offset.bottom - offset.top;
114
115     /* now intersect with the visible region to get the pixels that will
116      * actually scroll */
117     DstRgn = CreateRectRgnIndirect(&rcSrc);
118     res = CombineRgn(DstRgn, DstRgn, visrgn, RGN_AND);
119
120     /* and translate, giving the destination region */
121     OffsetRgn(DstRgn, dxdev, dydev);
122     if (TRACE_ON(scroll)) dump_region("Destination scroll region: ", DstRgn);
123
124     /* if there are any, do it */
125     if (res > NULLREGION)
126     {
127         RECT rect ;
128         /* clip to the destination region, so we can BitBlt with a simple
129          * bounding rectangle */
130         if (clipRgn)
131             ExtSelectClipRgn(hdc, DstRgn, RGN_AND);
132         else
133             SelectClipRgn(hdc, DstRgn);
134         GetRgnBox(DstRgn, &rect);
135         DPtoLP(hdc, (LPPOINT)&rect, 2);
136         TRACE("destination rect: %s\n", wine_dbgstr_rect(&rect));
137
138         BitBlt(hdc, rect.left, rect.top,
139                rect.right - rect.left, rect.bottom - rect.top,
140                hdc, rect.left - dx, rect.top - dy, SRCCOPY);
141     }
142
143     /* compute the update areas.  This is the combined clip rectangle
144      * minus the scrolled region, and intersected with the visible
145      * region. */
146     if (hrgnUpdate || lprcUpdate)
147     {
148         HRGN hrgn = hrgnUpdate;
149
150         /* Intersect clip and scroll rectangles, allowing NULL values */
151         if (lprcScroll)
152             if (lprcClip)
153                 IntersectRect(&rcClip, lprcClip, lprcScroll);
154             else
155                 rcClip = *lprcScroll;
156         else
157             if (lprcClip)
158                 rcClip = *lprcClip;
159             else
160                 GetClipBox(hdc, &rcClip);
161
162         /* Convert the combined clip rectangle to device coordinates */
163         LPtoDP(hdc, (LPPOINT)&rcClip, 2);
164         if (hrgn)
165             SetRectRgn(hrgn, rcClip.left, rcClip.top, rcClip.right, rcClip.bottom);
166         else
167             hrgn = CreateRectRgnIndirect(&rcClip);
168         CombineRgn(hrgn, hrgn, visrgn, RGN_AND);
169         CombineRgn(hrgn, hrgn, DstRgn, RGN_DIFF);
170         if (TRACE_ON(scroll)) dump_region("Update region: ", hrgn);
171         if (lprcUpdate)
172         {
173             GetRgnBox(hrgn, lprcUpdate);
174             /* Put the lprcUpdate in logical coordinates */
175             DPtoLP(hdc, (LPPOINT)lprcUpdate, 2);
176             TRACE("returning lprcUpdate %s\n", wine_dbgstr_rect(lprcUpdate));
177         }
178         if (!hrgnUpdate)
179             DeleteObject(hrgn);
180     }
181
182     /* restore original clipping region */
183     SelectClipRgn(hdc, clipRgn);
184     DeleteObject(visrgn);
185     DeleteObject(DstRgn);
186     if (clipRgn) DeleteObject(clipRgn);
187     return TRUE;
188 }