| /* |
| * Scroll windows and DCs |
| * |
| * Copyright 1993 David W. Metcalfe |
| * Copyright 1995, 1996 Alex Korobka |
| * Copyright 2001 Alexandre Julliard |
| * |
| * 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 <stdarg.h> |
| #include <X11/Xlib.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wingdi.h" |
| #include "winuser.h" |
| |
| #include "x11drv.h" |
| #include "wine/debug.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 %ld 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 (X11DRV.@) |
| */ |
| BOOL X11DRV_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; |
| INT code = X11DRV_START_EXPOSURES; |
| |
| 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); |
| /* enable X-exposure events */ |
| if (hrgnUpdate || lprcUpdate) |
| ExtEscape( hdc, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, 0, NULL ); |
| /* 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; |
| HRGN ExpRgn = 0; |
| |
| /* collect all the exposures */ |
| code = X11DRV_END_EXPOSURES; |
| ExtEscape( hdc, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, |
| sizeof(ExpRgn), (LPSTR)&ExpRgn ); |
| /* 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); |
| /* add the exposures to this */ |
| if( ExpRgn) { |
| if( TRACE_ON( scroll)) dump_region( "Expose region: ", ExpRgn); |
| CombineRgn( hrgn, hrgn, ExpRgn, RGN_OR); |
| DeleteObject( ExpRgn); |
| } |
| 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; |
| } |