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