blob: e8e7537cd45d4fa169c94edfe559dd82e8d719ff [file] [log] [blame]
/*
* MACDRV window/DC scrolling
*
* Copyright 1993 David W. Metcalfe
* Copyright 1995, 1996 Alex Korobka
* Copyright 2001 Alexandre Julliard
* Copyright 2011, 2013 Ken Thomases for CodeWeavers Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include "macdrv.h"
#include "winuser.h"
WINE_DEFAULT_DEBUG_CHANNEL(scroll);
static void dump_region(const char *p, HRGN hrgn)
{
DWORD i, size;
RGNDATA *data = NULL;
RECT *rect;
if (!hrgn)
{
TRACE("%s null region\n", p);
return;
}
if (!(size = GetRegionData(hrgn, 0, NULL)))
return;
if (!(data = HeapAlloc(GetProcessHeap(), 0, size))) return;
GetRegionData(hrgn, size, data);
TRACE("%s %d rects:", p, data->rdh.nCount);
for (i = 0, rect = (RECT *)data->Buffer; i<20 && i < data->rdh.nCount; i++, rect++)
TRACE(" %s", wine_dbgstr_rect(rect));
TRACE("\n");
HeapFree(GetProcessHeap(), 0, data);
}
/*************************************************************************
* ScrollDC (MACDRV.@)
*/
BOOL CDECL macdrv_ScrollDC(HDC hdc, INT dx, INT dy, const RECT *lprcScroll,
const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
{
RECT rcSrc, rcClip, offset;
INT dxdev, dydev, res;
HRGN DstRgn, clipRgn, visrgn;
TRACE("dx,dy %d,%d rcScroll %s rcClip %s hrgnUpdate %p lprcUpdate %p\n",
dx, dy, wine_dbgstr_rect(lprcScroll), wine_dbgstr_rect(lprcClip),
hrgnUpdate, lprcUpdate);
/* get the visible region */
visrgn = CreateRectRgn(0, 0, 0, 0);
GetRandomRgn(hdc, visrgn, SYSRGN);
if (!(GetVersion() & 0x80000000))
{
/* Window NT/2k/XP */
POINT org;
GetDCOrgEx(hdc, &org);
OffsetRgn(visrgn, -org.x, -org.y);
}
/* intersect with the clipping Region if the DC has one */
clipRgn = CreateRectRgn(0, 0, 0, 0);
if (GetClipRgn(hdc, clipRgn) != 1)
{
DeleteObject(clipRgn);
clipRgn = NULL;
}
else
CombineRgn(visrgn, visrgn, clipRgn, RGN_AND);
/* only those pixels in the scroll rectangle that remain in the clipping
* rect are scrolled. */
if (lprcClip)
rcClip = *lprcClip;
else
GetClipBox(hdc, &rcClip);
rcSrc = rcClip;
OffsetRect(&rcClip, -dx, -dy);
IntersectRect(&rcSrc, &rcSrc, &rcClip);
/* if an scroll rectangle is specified, only the pixels within that
* rectangle are scrolled */
if (lprcScroll)
IntersectRect(&rcSrc, &rcSrc, lprcScroll);
/* now convert to device coordinates */
LPtoDP(hdc, (LPPOINT)&rcSrc, 2);
TRACE("source rect: %s\n", wine_dbgstr_rect(&rcSrc));
/* also dx and dy */
SetRect(&offset, 0, 0, dx, dy);
LPtoDP(hdc, (LPPOINT)&offset, 2);
dxdev = offset.right - offset.left;
dydev = offset.bottom - offset.top;
/* now intersect with the visible region to get the pixels that will
* actually scroll */
DstRgn = CreateRectRgnIndirect(&rcSrc);
res = CombineRgn(DstRgn, DstRgn, visrgn, RGN_AND);
/* and translate, giving the destination region */
OffsetRgn(DstRgn, dxdev, dydev);
if (TRACE_ON(scroll)) dump_region("Destination scroll region: ", DstRgn);
/* if there are any, do it */
if (res > NULLREGION)
{
RECT rect ;
/* clip to the destination region, so we can BitBlt with a simple
* bounding rectangle */
if (clipRgn)
ExtSelectClipRgn(hdc, DstRgn, RGN_AND);
else
SelectClipRgn(hdc, DstRgn);
GetRgnBox(DstRgn, &rect);
DPtoLP(hdc, (LPPOINT)&rect, 2);
TRACE("destination rect: %s\n", wine_dbgstr_rect(&rect));
BitBlt(hdc, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
hdc, rect.left - dx, rect.top - dy, SRCCOPY);
}
/* compute the update areas. This is the combined clip rectangle
* minus the scrolled region, and intersected with the visible
* region. */
if (hrgnUpdate || lprcUpdate)
{
HRGN hrgn = hrgnUpdate;
/* Intersect clip and scroll rectangles, allowing NULL values */
if (lprcScroll)
if (lprcClip)
IntersectRect(&rcClip, lprcClip, lprcScroll);
else
rcClip = *lprcScroll;
else
if (lprcClip)
rcClip = *lprcClip;
else
GetClipBox(hdc, &rcClip);
/* Convert the combined clip rectangle to device coordinates */
LPtoDP(hdc, (LPPOINT)&rcClip, 2);
if (hrgn)
SetRectRgn(hrgn, rcClip.left, rcClip.top, rcClip.right, rcClip.bottom);
else
hrgn = CreateRectRgnIndirect(&rcClip);
CombineRgn(hrgn, hrgn, visrgn, RGN_AND);
CombineRgn(hrgn, hrgn, DstRgn, RGN_DIFF);
if (TRACE_ON(scroll)) dump_region("Update region: ", hrgn);
if (lprcUpdate)
{
GetRgnBox(hrgn, lprcUpdate);
/* Put the lprcUpdate in logical coordinates */
DPtoLP(hdc, (LPPOINT)lprcUpdate, 2);
TRACE("returning lprcUpdate %s\n", wine_dbgstr_rect(lprcUpdate));
}
if (!hrgnUpdate)
DeleteObject(hrgn);
}
/* restore original clipping region */
SelectClipRgn(hdc, clipRgn);
DeleteObject(visrgn);
DeleteObject(DstRgn);
if (clipRgn) DeleteObject(clipRgn);
return TRUE;
}