/* * tkUnixDraw.c -- * * This file contains X specific drawing routines. * * Copyright (c) 1995 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tkUnixDraw.c,v 1.3 1999/03/10 07:04:45 stanton Exp $ */ #include "tkPort.h" #include "tkInt.h" #if !defined(__WIN32__) && !defined(MAC_TCL) #include "tkUnixInt.h" #endif /* * The following structure is used to pass information to * ScrollRestrictProc from TkScrollWindow. */ typedef struct ScrollInfo { int done; /* Flag is 0 until filtering is done. */ Display *display; /* Display to filter. */ Window window; /* Window to filter. */ TkRegion region; /* Region into which damage is accumulated. */ int dx, dy; /* Amount by which window was shifted. */ } ScrollInfo; /* * Forward declarations for procedures declared later in this file: */ static Tk_RestrictAction ScrollRestrictProc _ANSI_ARGS_(( ClientData arg, XEvent *eventPtr)); /* *---------------------------------------------------------------------- * * TkScrollWindow -- * * Scroll a rectangle of the specified window and accumulate * damage information in the specified Region. * * Results: * Returns 0 if no damage additional damage was generated. Sets * damageRgn to contain the damaged areas and returns 1 if * GraphicsExpose events were detected. * * Side effects: * Scrolls the bits in the window and enters the event loop * looking for damage events. * *---------------------------------------------------------------------- */ int TkScrollWindow(tkwin, gc, x, y, width, height, dx, dy, damageRgn) Tk_Window tkwin; /* The window to be scrolled. */ GC gc; /* GC for window to be scrolled. */ int x, y, width, height; /* Position rectangle to be scrolled. */ int dx, dy; /* Distance rectangle should be moved. */ TkRegion damageRgn; /* Region to accumulate damage in. */ { Tk_RestrictProc *oldProc; ClientData oldArg, dummy; ScrollInfo info; XCopyArea(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_WindowId(tkwin), gc, x, y, (unsigned int) width, (unsigned int) height, x + dx, y + dy); info.done = 0; info.window = Tk_WindowId(tkwin); info.display = Tk_Display(tkwin); info.region = damageRgn; info.dx = dx; info.dy = dy; /* * Sync the event stream so all of the expose events will be on the * Tk event queue before we start filtering. This avoids busy waiting * while we filter events. */ TkpSync(info.display); oldProc = Tk_RestrictEvents(ScrollRestrictProc, (ClientData) &info, &oldArg); while (!info.done) { Tcl_ServiceEvent(TCL_WINDOW_EVENTS); } Tk_RestrictEvents(oldProc, oldArg, &dummy); return XEmptyRegion((Region) damageRgn) ? 0 : 1; } /* *---------------------------------------------------------------------- * * ScrollRestrictProc -- * * A Tk_RestrictProc used by TkScrollWindow to gather up Expose * information into a single damage region. It accumulates damage * events on the specified window until a NoExpose or the last * GraphicsExpose event is detected. * * Results: * None. * * Side effects: * Discards Expose events after accumulating damage information * for a particular window. * *---------------------------------------------------------------------- */ static Tk_RestrictAction ScrollRestrictProc(arg, eventPtr) ClientData arg; XEvent *eventPtr; { ScrollInfo *info = (ScrollInfo *) arg; XRectangle rect; /* * Defer events which aren't for the specified window. */ if (info->done || (eventPtr->xany.display != info->display) || (eventPtr->xany.window != info->window)) { return TK_DEFER_EVENT; } if (eventPtr->type == NoExpose) { info->done = 1; } else if (eventPtr->type == GraphicsExpose) { rect.x = eventPtr->xgraphicsexpose.x; rect.y = eventPtr->xgraphicsexpose.y; rect.width = eventPtr->xgraphicsexpose.width; rect.height = eventPtr->xgraphicsexpose.height; XUnionRectWithRegion(&rect, (Region) info->region, (Region) info->region); if (eventPtr->xgraphicsexpose.count == 0) { info->done = 1; } } else if (eventPtr->type == Expose) { /* * This case is tricky. This event was already queued before * the XCopyArea was issued. If this area overlaps the area * being copied, then some of the copied area may be invalid. * The easiest way to handle this case is to mark both the * original area and the shifted area as damaged. */ rect.x = eventPtr->xexpose.x; rect.y = eventPtr->xexpose.y; rect.width = eventPtr->xexpose.width; rect.height = eventPtr->xexpose.height; XUnionRectWithRegion(&rect, (Region) info->region, (Region) info->region); rect.x += info->dx; rect.y += info->dy; XUnionRectWithRegion(&rect, (Region) info->region, (Region) info->region); } else { return TK_DEFER_EVENT; } return TK_DISCARD_EVENT; }