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 * RCS: @(#) $Id: tkUnixDraw.c,v 1.6.8.1 2005/07/28 04:57:38 hobbs Exp $
12 */
13
14#include "tkPort.h"
15#include "tkInt.h"
16
17#if !defined(__WIN32__) && !defined(MAC_TCL)
18#include "tkUnixInt.h"
19#endif
20
21/*
22 * The following structure is used to pass information to
23 * ScrollRestrictProc from TkScrollWindow.
24 */
25
26typedef struct ScrollInfo {
27    int done;			/* Flag is 0 until filtering is done. */
28    Display *display;		/* Display to filter. */
29    Window window;		/* Window to filter. */
30    TkRegion region;		/* Region into which damage is accumulated. */
31    int dx, dy;			/* Amount by which window was shifted. */
32} ScrollInfo;
33
34/*
35 * Forward declarations for procedures declared later in this file:
36 */
37
38static Tk_RestrictAction	ScrollRestrictProc _ANSI_ARGS_((
39    				    ClientData arg, XEvent *eventPtr));
40
41/*
42 *----------------------------------------------------------------------
43 *
44 * TkScrollWindow --
45 *
46 *	Scroll a rectangle of the specified window and accumulate
47 *	damage information in the specified Region.
48 *
49 * Results:
50 *	Returns 0 if no damage additional damage was generated.  Sets
51 *	damageRgn to contain the damaged areas and returns 1 if
52 *	GraphicsExpose events were detected.
53 *
54 * Side effects:
55 *	Scrolls the bits in the window and enters the event loop
56 *	looking for damage events.
57 *
58 *----------------------------------------------------------------------
59 */
60
61int
62TkScrollWindow(tkwin, gc, x, y, width, height, dx, dy, damageRgn)
63    Tk_Window tkwin;		/* The window to be scrolled. */
64    GC gc;			/* GC for window to be scrolled. */
65    int x, y, width, height;	/* Position rectangle to be scrolled. */
66    int dx, dy;			/* Distance rectangle should be moved. */
67    TkRegion damageRgn;		/* Region to accumulate damage in. */
68{
69    Tk_RestrictProc *oldProc;
70    ClientData oldArg, dummy;
71    ScrollInfo info;
72
73    XCopyArea(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_WindowId(tkwin), gc,
74	    x, y, (unsigned int) width, (unsigned int) height, x + dx, y + dy);
75
76    info.done = 0;
77    info.window = Tk_WindowId(tkwin);
78    info.display = Tk_Display(tkwin);
79    info.region = damageRgn;
80    info.dx = dx;
81    info.dy = dy;
82
83    /*
84     * Sync the event stream so all of the expose events will be on the
85     * Tk event queue before we start filtering.  This avoids busy waiting
86     * while we filter events.
87     */
88
89    TkpSync(info.display);
90    oldProc = Tk_RestrictEvents(ScrollRestrictProc, (ClientData) &info,
91	    &oldArg);
92    while (!info.done) {
93	Tcl_ServiceEvent(TCL_WINDOW_EVENTS);
94    }
95    Tk_RestrictEvents(oldProc, oldArg, &dummy);
96
97    if (XEmptyRegion((Region) damageRgn)) {
98	return 0;
99    } else {
100	return 1;
101    }
102}
103
104/*
105 *----------------------------------------------------------------------
106 *
107 * ScrollRestrictProc --
108 *
109 *	A Tk_RestrictProc used by TkScrollWindow to gather up Expose
110 *	information into a single damage region.  It accumulates damage
111 *	events on the specified window until a NoExpose or the last
112 *	GraphicsExpose event is detected.
113 *
114 * Results:
115 *	None.
116 *
117 * Side effects:
118 *	Discards Expose events after accumulating damage information
119 *	for a particular window.
120 *
121 *----------------------------------------------------------------------
122 */
123
124static Tk_RestrictAction
125ScrollRestrictProc(arg, eventPtr)
126    ClientData arg;
127    XEvent *eventPtr;
128{
129    ScrollInfo *info = (ScrollInfo *) arg;
130    XRectangle rect;
131
132    /*
133     * Defer events which aren't for the specified window.
134     */
135
136    if (info->done || (eventPtr->xany.display != info->display)
137	    || (eventPtr->xany.window != info->window)) {
138	return TK_DEFER_EVENT;
139    }
140
141    if (eventPtr->type == NoExpose) {
142	info->done = 1;
143    } else if (eventPtr->type == GraphicsExpose) {
144	rect.x = eventPtr->xgraphicsexpose.x;
145	rect.y = eventPtr->xgraphicsexpose.y;
146	rect.width = eventPtr->xgraphicsexpose.width;
147	rect.height = eventPtr->xgraphicsexpose.height;
148	XUnionRectWithRegion(&rect, (Region) info->region,
149		(Region) info->region);
150
151	if (eventPtr->xgraphicsexpose.count == 0) {
152	    info->done = 1;
153	}
154    } else if (eventPtr->type == Expose) {
155
156	/*
157	 * This case is tricky.  This event was already queued before
158	 * the XCopyArea was issued.  If this area overlaps the area
159	 * being copied, then some of the copied area may be invalid.
160	 * The easiest way to handle this case is to mark both the
161	 * original area and the shifted area as damaged.
162	 */
163
164	rect.x = eventPtr->xexpose.x;
165	rect.y = eventPtr->xexpose.y;
166	rect.width = eventPtr->xexpose.width;
167	rect.height = eventPtr->xexpose.height;
168	XUnionRectWithRegion(&rect, (Region) info->region,
169		(Region) info->region);
170	rect.x += info->dx;
171	rect.y += info->dy;
172	XUnionRectWithRegion(&rect, (Region) info->region,
173		(Region) info->region);
174    } else {
175	return TK_DEFER_EVENT;
176    }
177    return TK_DISCARD_EVENT;
178}
179
180/*
181 *----------------------------------------------------------------------
182 *
183 * TkpDrawHighlightBorder --
184 *
185 *	This procedure draws a rectangular ring around the outside of
186 *	a widget to indicate that it has received the input focus.
187 *
188 *      On Unix, we just draw the simple inset ring.  On other sytems,
189 *      e.g. the Mac, the focus ring is a little more complicated, so we
190 *      need this abstraction.
191 *
192 * Results:
193 *	None.
194 *
195 * Side effects:
196 *	A rectangle "width" pixels wide is drawn in "drawable",
197 *	corresponding to the outer area of "tkwin".
198 *
199 *----------------------------------------------------------------------
200 */
201
202void
203TkpDrawHighlightBorder(tkwin, fgGC, bgGC, highlightWidth, drawable)
204    Tk_Window tkwin;
205    GC fgGC;
206    GC bgGC;
207    int highlightWidth;
208    Drawable drawable;
209{
210    TkDrawInsetFocusHighlight(tkwin, fgGC, highlightWidth, drawable, 0);
211}
212
213/*
214 *----------------------------------------------------------------------
215 *
216 * TkpDrawFrame --
217 *
218 *	This procedure draws the rectangular frame area.
219 *
220 * Results:
221 *	None.
222 *
223 * Side effects:
224 *	Draws inside the tkwin area.
225 *
226 *----------------------------------------------------------------------
227 */
228
229void
230TkpDrawFrame (Tk_Window tkwin, Tk_3DBorder border,
231	int highlightWidth, int borderWidth, int relief)
232{
233    Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin),
234	    border, highlightWidth, highlightWidth,
235	    Tk_Width(tkwin) - 2 * highlightWidth,
236	    Tk_Height(tkwin) - 2 * highlightWidth,
237	    borderWidth, relief);
238}
239