1 /*
2  * tkUnixDraw.c --
3  *
4  *	This file contains X specific drawing routines.
5  *
6  * Copyright (c) 1995 Sun Microsystems, Inc.
7  *
8  * See the file "license.terms" for information on usage and redistribution
9  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  *
11  * SCCS: @(#) tkUnixDraw.c 1.7 96/02/15 18:55:26
12  */
13 
14 #include "tkInt.h"
15 
16 /*
17  * The following structure is used to pass information to
18  * ScrollRestrictProc from TkScrollWindow.
19  */
20 
21 typedef struct ScrollInfo {
22     int done;			/* Flag is 0 until filtering is done. */
23     Display *display;		/* Display to filter. */
24     Window window;		/* Window to filter. */
25     TkRegion region;		/* Region into which damage is accumulated. */
26     int dx, dy;			/* Amount by which window was shifted. */
27 } ScrollInfo;
28 
29 /*
30  * Forward declarations for procedures declared later in this file:
31  */
32 
33 static Tk_RestrictAction	ScrollRestrictProc _ANSI_ARGS_((
34     				    ClientData arg, XEvent *eventPtr));
35 
36 /*
37  *----------------------------------------------------------------------
38  *
39  * TkScrollWindow --
40  *
41  *	Scroll a rectangle of the specified window and accumulate
42  *	damage information in the specified Region.
43  *
44  * Results:
45  *	Returns 0 if no damage additional damage was generated.  Sets
46  *	damageRgn to contain the damaged areas and returns 1 if
47  *	GraphicsExpose events were detected.
48  *
49  * Side effects:
50  *	Scrolls the bits in the window and enters the event loop
51  *	looking for damage events.
52  *
53  *----------------------------------------------------------------------
54  */
55 
56 int
TkScrollWindow(tkwin,gc,x,y,width,height,dx,dy,damageRgn)57 TkScrollWindow(tkwin, gc, x, y, width, height, dx, dy, damageRgn)
58     Tk_Window tkwin;		/* The window to be scrolled. */
59     GC gc;			/* GC for window to be scrolled. */
60     int x, y, width, height;	/* Position rectangle to be scrolled. */
61     int dx, dy;			/* Distance rectangle should be moved. */
62     TkRegion damageRgn;		/* Region to accumulate damage in. */
63 {
64     Tk_RestrictProc *oldProc;
65     ClientData oldArg, dummy;
66     ScrollInfo info;
67 
68     XCopyArea(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_WindowId(tkwin), gc,
69 	    x, y, (unsigned int) width, (unsigned int) height, x + dx, y + dy);
70 
71     info.done = 0;
72     info.window = Tk_WindowId(tkwin);
73     info.display = Tk_Display(tkwin);
74     info.region = damageRgn;
75     info.dx = dx;
76     info.dy = dy;
77 
78     /*
79      * Sync the event stream so all of the expose events will be on the
80      * X event queue before we start filtering.  This avoids busy waiting
81      * while we filter events.
82      */
83 
84     XSync(info.display, False);
85     oldProc = Tk_RestrictEvents(ScrollRestrictProc, (ClientData) &info,
86 	    &oldArg);
87     while (!info.done) {
88 	Tcl_DoOneEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT);
89     }
90     Tk_RestrictEvents(oldProc, oldArg, &dummy);
91 
92     return XEmptyRegion((Region) damageRgn) ? 0 : 1;
93 }
94 
95 /*
96  *----------------------------------------------------------------------
97  *
98  * ScrollRestrictProc --
99  *
100  *	A Tk_RestrictProc used by TkScrollWindow to gather up Expose
101  *	information into a single damage region.  It accumulates damage
102  *	events on the specified window until a NoExpose or the last
103  *	GraphicsExpose event is detected.
104  *
105  * Results:
106  *	None.
107  *
108  * Side effects:
109  *	Discards Expose events after accumulating damage information
110  *	for a particular window.
111  *
112  *----------------------------------------------------------------------
113  */
114 
115 static Tk_RestrictAction
ScrollRestrictProc(arg,eventPtr)116 ScrollRestrictProc(arg, eventPtr)
117     ClientData arg;
118     XEvent *eventPtr;
119 {
120     ScrollInfo *info = (ScrollInfo *) arg;
121     XRectangle rect;
122 
123     /*
124      * Defer events which aren't for the specified window.
125      */
126 
127     if (info->done || (eventPtr->xany.display != info->display)
128 	    || (eventPtr->xany.window != info->window)) {
129 	return TK_DEFER_EVENT;
130     }
131 
132     if (eventPtr->type == NoExpose) {
133 	info->done = 1;
134     } else if (eventPtr->type == GraphicsExpose) {
135 	rect.x = eventPtr->xgraphicsexpose.x;
136 	rect.y = eventPtr->xgraphicsexpose.y;
137 	rect.width = eventPtr->xgraphicsexpose.width;
138 	rect.height = eventPtr->xgraphicsexpose.height;
139 	XUnionRectWithRegion(&rect, (Region) info->region,
140 		(Region) info->region);
141 
142 	if (eventPtr->xgraphicsexpose.count == 0) {
143 	    info->done = 1;
144 	}
145     } else if (eventPtr->type == Expose) {
146 
147 	/*
148 	 * This case is tricky.  This event was already queued before
149 	 * the XCopyArea was issued.  If this area overlaps the area
150 	 * being copied, then some of the copied area may be invalid.
151 	 * The easiest way to handle this case is to mark both the
152 	 * original area and the shifted area as damaged.
153 	 */
154 
155 	rect.x = eventPtr->xexpose.x;
156 	rect.y = eventPtr->xexpose.y;
157 	rect.width = eventPtr->xexpose.width;
158 	rect.height = eventPtr->xexpose.height;
159 	XUnionRectWithRegion(&rect, (Region) info->region,
160 		(Region) info->region);
161 	rect.x += info->dx;
162 	rect.y += info->dy;
163 	XUnionRectWithRegion(&rect, (Region) info->region,
164 		(Region) info->region);
165     } else {
166 	return TK_DEFER_EVENT;
167     }
168     return TK_DISCARD_EVENT;
169 }
170 
171