Eyes.c revision 55074dd0
1/*
2
3Copyright (c) 1991  X Consortium
4
5Permission is hereby granted, free of charge, to any person obtaining
6a copy of this software and associated documentation files (the
7"Software"), to deal in the Software without restriction, including
8without limitation the rights to use, copy, modify, merge, publish,
9distribute, sublicense, and/or sell copies of the Software, and to
10permit persons to whom the Software is furnished to do so, subject to
11the following conditions:
12
13The above copyright notice and this permission notice shall be included
14in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of the X Consortium shall
25not be used in advertising or otherwise to promote the sale, use or
26other dealings in this Software without prior written authorization
27from the X Consortium.
28
29*/
30
31/*
32 * Eyes.c
33 *
34 * a widget which follows the mouse around
35 */
36
37#ifdef HAVE_CONFIG_H
38# include "config.h"
39#endif
40
41# include <X11/Xos.h>
42# include <stdio.h>
43# include <X11/IntrinsicP.h>
44# include <X11/StringDefs.h>
45# include <X11/Xmu/Converters.h>
46# include "EyesP.h"
47# include <math.h>
48# include <X11/extensions/shape.h>
49# include <X11/Xlibint.h>
50# include <stdlib.h>
51
52#if (defined(SVR4) || defined(SYSV) && defined(i386))
53extern double hypot(double, double);
54#endif
55
56#define offset(field) XtOffsetOf(EyesRec, eyes.field)
57#define goffset(field) XtOffsetOf(WidgetRec, core.field)
58
59static XtResource resources[] = {
60    {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
61	goffset(width), XtRImmediate, (XtPointer) 150},
62    {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
63	goffset(height), XtRImmediate, (XtPointer) 100},
64    {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
65        offset(pixel[PART_PUPIL]), XtRString, XtDefaultForeground},
66    {XtNoutline, XtCForeground, XtRPixel, sizeof(Pixel),
67        offset(pixel[PART_OUTLINE]), XtRString, XtDefaultForeground},
68    {XtNcenterColor, XtCBackground, XtRPixel, sizeof (Pixel),
69	offset(pixel[PART_CENTER]), XtRString, XtDefaultBackground},
70    {XtNreverseVideo, XtCReverseVideo, XtRBoolean, sizeof (Boolean),
71	offset (reverse_video), XtRImmediate, (XtPointer) FALSE},
72    {XtNbackingStore, XtCBackingStore, XtRBackingStore, sizeof (int),
73    	offset (backing_store), XtRString, "default"},
74    {XtNshapeWindow, XtCShapeWindow, XtRBoolean, sizeof (Boolean),
75	offset (shape_window), XtRImmediate, (XtPointer) TRUE},
76#ifdef XRENDER
77    {XtNrender, XtCBoolean, XtRBoolean, sizeof(Boolean),
78	offset(render), XtRImmediate, (XtPointer) TRUE },
79#endif
80    {XtNdistance, XtCBoolean, XtRBoolean, sizeof(Boolean),
81	offset(distance), XtRImmediate, (XtPointer) FALSE },
82};
83
84#undef offset
85#undef goffset
86
87# define EYE_X(n)	((n) * 2.0)
88# define EYE_Y(n)	(0.0)
89# define EYE_OFFSET	(0.1)	/* padding between eyes */
90# define EYE_THICK	(0.175)	/* thickness of eye rim */
91# define BALL_DIAM	(0.3)
92# define BALL_PAD	(0.175)
93# define EYE_DIAM	(2.0 - (EYE_THICK + EYE_OFFSET) * 2)
94# define BALL_DIST	((EYE_DIAM - BALL_DIAM) / 2.0 - BALL_PAD)
95# define W_MIN_X	(-1.0 + EYE_OFFSET)
96# define W_MAX_X	(3.0 - EYE_OFFSET)
97# define W_MIN_Y	(-1.0 + EYE_OFFSET)
98# define W_MAX_Y	(1.0 - EYE_OFFSET)
99
100# define TPOINT_NONE	(-1000)	/* special value meaning "not yet set" */
101# define TPointEqual(a, b)  ((a).x == (b).x && (a).y == (b).y)
102# define XPointEqual(a, b)  ((a).x == (b).x && (a).y == (b).y)
103# define AngleBetween(A, A0, A1) (A0 <= A1 ? A0 <= A && A <= A1 : \
104					     A0 <= A || A <= A1)
105
106static int delays[] = { 50, 100, 200, 400, 0 };
107
108static void ClassInitialize(void)
109{
110    XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
111		    NULL, 0 );
112}
113
114WidgetClass eyesWidgetClass = (WidgetClass) &eyesClassRec;
115
116/* ARGSUSED */
117static void Initialize (
118    Widget greq,
119    Widget gnew,
120    ArgList args,
121    Cardinal *num_args)
122{
123    EyesWidget w = (EyesWidget)gnew;
124    XtGCMask	valuemask;
125    XGCValues	myXGCV;
126    int shape_event_base, shape_error_base;
127#ifdef XRENDER
128    enum EyesPart i;
129#endif
130
131    /*
132     * set the colors if reverse video; these are the colors used:
133     *
134     *     background - paper		white
135     *     foreground - text, ticks	black
136     *     border - border		black (foreground)
137     *
138     * This doesn't completely work since the parent has already made up a
139     * border.  Sigh.
140     */
141    if (w->eyes.reverse_video) {
142	Pixel fg = w->eyes.pixel[PART_PUPIL];
143	Pixel bg = w->core.background_pixel;
144
145	if (w->core.border_pixel == fg)
146 	    w->core.border_pixel = bg;
147	if (w->eyes.pixel[PART_OUTLINE] == fg)
148	    w->eyes.pixel[PART_OUTLINE] = bg;
149	if (w->eyes.pixel[PART_CENTER] == bg)
150	    w->eyes.pixel[PART_CENTER] = fg;
151	w->eyes.pixel[PART_PUPIL] = bg;
152	w->core.background_pixel = fg;
153    }
154
155    myXGCV.foreground = w->eyes.pixel[PART_PUPIL];
156    myXGCV.background = w->core.background_pixel;
157    valuemask = GCForeground | GCBackground;
158    w->eyes.gc[PART_PUPIL] = XtGetGC(gnew, valuemask, &myXGCV);
159
160    myXGCV.foreground = w->eyes.pixel[PART_OUTLINE];
161    valuemask = GCForeground | GCBackground;
162    w->eyes.gc[PART_OUTLINE] = XtGetGC(gnew, valuemask, &myXGCV);
163
164    myXGCV.foreground = w->eyes.pixel[PART_CENTER];
165    myXGCV.background = w->eyes.pixel[PART_PUPIL];
166    valuemask = GCForeground | GCBackground;
167    w->eyes.gc[PART_CENTER] = XtGetGC(gnew, valuemask, &myXGCV);
168
169    w->eyes.update = 0;
170    /* wait for Realize to add the timeout */
171    w->eyes.interval_id = 0;
172
173    w->eyes.pupil[0].x = w->eyes.pupil[1].x = TPOINT_NONE;
174    w->eyes.pupil[0].y = w->eyes.pupil[1].y = TPOINT_NONE;
175
176    w->eyes.mouse.x = w->eyes.mouse.y = TPOINT_NONE;
177
178    if (w->eyes.shape_window && !XShapeQueryExtension (XtDisplay (w),
179						       &shape_event_base,
180						       &shape_error_base))
181	w->eyes.shape_window = False;
182    w->eyes.shape_mask = 0;
183    w->eyes.gc[PART_SHAPE] = NULL;
184
185#ifdef XRENDER
186    for (i = 0; i < PART_SHAPE; i ++) {
187	XColor c;
188	XRenderColor rc;
189
190	c.pixel = w->eyes.pixel[i];
191	XQueryColor(XtDisplay (w), w->core.colormap, &c);
192
193	rc.red = c.red;
194	rc.green = c.green;
195	rc.blue = c.blue;
196	rc.alpha = -1;
197	w->eyes.fill[i] = XRenderCreateSolidFill(XtDisplay (w), &rc);
198    }
199#endif
200}
201
202static void
203drawEllipse(EyesWidget w, enum EyesPart part,
204	    double centerx, double centery,
205	    double oldx, double oldy,
206	    double diam)
207{
208    const TRectangle tpos = {
209	centerx - diam/2.0,
210	centery - diam/2.0,
211	diam, diam };
212    TRectangle pos;
213    Trectangle(&w->eyes.t, &tpos, &pos);
214
215    if (part == PART_CLEAR) {
216	XFillRectangle(XtDisplay(w), XtWindow(w),
217		       w->eyes.gc[PART_CENTER],
218		       (int)pos.x, (int)pos.y,
219		       (int)pos.width+2, (int)pos.height+2);
220	return;
221    }
222#ifdef XRENDER
223    if (w->eyes.render && part != PART_SHAPE && (!w->eyes.shape_window ||
224						 part != PART_OUTLINE) &&
225	w->eyes.picture) {
226	int n, i;
227	double hd, c, s, sx, sy, x, y, px, py;
228	XPointDouble *p;
229
230	pos.x = pos.x + pos.width/2.0;
231	pos.y = pos.y + pos.height/2.0;
232
233	/* determine number of segments to draw */
234	hd = hypot(pos.width, pos.height)/2;
235	n = (M_PI / acos(hd/(hd+1.0))) + 0.5;
236	if (n < 2) n = 2;
237
238	c = cos(M_PI/n);
239	s = sin(M_PI/n);
240	sx = -(pos.width*s)/pos.height;
241	sy = (pos.height*s)/pos.width;
242
243	n *= 2;
244	p = Xmalloc(sizeof(*p)*n);
245	if (!p)
246	    return;
247	x = 0;
248	y = pos.height/2.0;
249	for (i = 0; i < n; i ++)
250	{
251	    p[i].x = x + pos.x;
252	    p[i].y = y + pos.y;
253	    px = x;
254	    py = y;
255	    x = c*px + sx*py;
256	    y = c*py + sy*px;
257	}
258
259	if (oldx != TPOINT_NONE || oldy != TPOINT_NONE)
260	    drawEllipse(w, PART_CLEAR, oldx, oldy,
261			TPOINT_NONE, TPOINT_NONE, diam);
262
263	XRenderCompositeDoublePoly(XtDisplay(w), PictOpOver,
264				   w->eyes.fill[part], w->eyes.picture,
265				   XRenderFindStandardFormat(XtDisplay(w),
266							     PictStandardA8),
267				   0, 0, 0, 0, p, n, 0);
268
269	Xfree(p);
270	return;
271    }
272#endif
273    if (oldx != TPOINT_NONE || oldy != TPOINT_NONE)
274	drawEllipse(w, PART_CLEAR, oldx, oldy,
275		    TPOINT_NONE, TPOINT_NONE, diam);
276
277    XFillArc(XtDisplay(w),
278	     part == PART_SHAPE ? w->eyes.shape_mask : XtWindow(w),
279	     w->eyes.gc[part],
280	     (int)(pos.x + 0.5), (int)(pos.y + 0.5),
281	     (int)(pos.width + 0.0), (int)(pos.height + 0.0),
282	     90*64, 360*64);
283}
284
285
286static void
287eyeLiner(EyesWidget	w,
288	 Boolean	draw,
289	 int		num)
290{
291    drawEllipse(w, draw ? PART_OUTLINE : PART_SHAPE,
292		EYE_X(num), EYE_Y(num),
293		TPOINT_NONE, TPOINT_NONE,
294		EYE_DIAM + 2.0*EYE_THICK);
295    if (draw) {
296	drawEllipse(w, PART_CENTER, EYE_X(num), EYE_Y(num),
297		    TPOINT_NONE, TPOINT_NONE,
298		    EYE_DIAM);
299    }
300}
301
302static TPoint computePupil (
303    int		num,
304    TPoint	mouse,
305    const TRectangle *screen)
306{
307	double	cx, cy;
308	double	dist;
309	double	angle;
310	double	dx, dy;
311	double	cosa, sina;
312	TPoint	ret;
313
314	cx = EYE_X(num); dx = mouse.x - cx;
315	cy = EYE_Y(num); dy = mouse.y - cy;
316	if (dx == 0 && dy == 0);
317	else {
318		angle = atan2 ((double) dy, (double) dx);
319		cosa = cos (angle);
320		sina = sin (angle);
321		dist = BALL_DIST;
322		if (screen)
323		{
324		    /* use distance mapping */
325		    double x0, y0, x1, y1;
326		    double a[4];
327		    x0 = screen->x - cx;
328		    y0 = screen->y - cy;
329		    x1 = x0 + screen->width;
330		    y1 = y0 + screen->height;
331		    a[0] = atan2(y0, x0);
332		    a[1] = atan2(y1, x0);
333		    a[2] = atan2(y1, x1);
334		    a[3] = atan2(y0, x1);
335		    if (AngleBetween(angle, a[0], a[1]))
336		    {
337			/* left */
338			dist *= dx / x0;
339		    }
340		    else if (AngleBetween(angle, a[1], a[2]))
341		    {
342			/* bottom */
343			dist *= dy / y1;
344		    }
345		    else if (AngleBetween(angle, a[2], a[3]))
346		    {
347			/* right */
348			dist *= dx / x1;
349		    }
350		    else if (AngleBetween(angle, a[3], a[0]))
351		    {
352			/* top */
353			dist *= dy / y0;
354		    }
355		    if (dist > BALL_DIST)
356			dist = BALL_DIST;
357		}
358		if (dist > hypot ((double) dx, (double) dy)) {
359			cx += dx;
360			cy += dy;
361		} else {
362			cx += dist * cosa;
363			cy += dist * sina;
364		}
365	}
366	ret.x = cx;
367	ret.y = cy;
368	return ret;
369}
370
371static void computePupils (
372    EyesWidget	w,
373    TPoint	mouse,
374    TPoint	pupils[2])
375{
376    TRectangle screen, *sp = NULL;
377    if (w->eyes.distance) {
378	Window r, cw;
379	int x, y;
380	r = RootWindowOfScreen(w->core.screen);
381	XTranslateCoordinates(XtDisplay(w), XtWindow(w), r, 0, 0, &x, &y, &cw);
382	screen.x = Tx(-x, -y, &w->eyes.t);
383	screen.y = Ty(-x, -y, &w->eyes.t);
384	screen.width  = Twidth (w->core.screen->width, w->core.screen->height,
385				&w->eyes.t);
386	screen.height = Theight(w->core.screen->width, w->core.screen->height,
387				&w->eyes.t);
388	sp = &screen;
389    }
390    pupils[0] = computePupil (0, mouse, sp);
391    pupils[1] = computePupil (1, mouse, sp);
392}
393
394static void
395eyeBall(EyesWidget	w,
396	Boolean draw,
397	TPoint	*old,
398	int	num)
399{
400    drawEllipse(w, draw ? PART_PUPIL : PART_CLEAR,
401		w->eyes.pupil[num].x, w->eyes.pupil[num].y,
402		old ? old->x : TPOINT_NONE, old ? old->y : TPOINT_NONE,
403		BALL_DIAM);
404}
405
406static void repaint_window (EyesWidget w)
407{
408	if (XtIsRealized ((Widget) w)) {
409		eyeLiner (w, TRUE, 0);
410		eyeLiner (w, TRUE, 1);
411		computePupils (w, w->eyes.mouse, w->eyes.pupil);
412		eyeBall (w, TRUE, NULL, 0);
413		eyeBall (w, TRUE, NULL, 1);
414	}
415}
416
417static void
418drawEye(EyesWidget w, TPoint newpupil, int num)
419{
420    XPoint		xnewpupil, xpupil;
421
422    xpupil.x = Xx(w->eyes.pupil[num].x, w->eyes.pupil[num].y, &w->eyes.t);
423    xpupil.y = Xy(w->eyes.pupil[num].x, w->eyes.pupil[num].y, &w->eyes.t);
424    xnewpupil.x = Xx(newpupil.x, newpupil.y, &w->eyes.t);
425    xnewpupil.y = Xy(newpupil.x, newpupil.y, &w->eyes.t);
426    if (
427#ifdef XRENDER
428	w->eyes.picture ? !TPointEqual(w->eyes.pupil[num], newpupil) :
429#endif
430	!XPointEqual(xpupil, xnewpupil)) {
431	TPoint oldpupil = w->eyes.pupil[num];
432	w->eyes.pupil[num] = newpupil;
433	eyeBall (w, TRUE, &oldpupil, num);
434    }
435}
436
437static void
438drawEyes(EyesWidget w, TPoint mouse)
439{
440    TPoint		newpupil[2];
441    int			num;
442
443    if (TPointEqual (mouse, w->eyes.mouse)) {
444	if (delays[w->eyes.update + 1] != 0)
445	    ++w->eyes.update;
446	return;
447    }
448    computePupils (w, mouse, newpupil);
449    for (num = 0; num < 2; num ++) {
450	drawEye(w, newpupil[num], num);
451    }
452
453    w->eyes.mouse = mouse;
454    w->eyes.update = 0;
455}
456
457static void draw_it_core(EyesWidget w)
458{
459    Window		rep_root, rep_child;
460    int			rep_rootx, rep_rooty;
461    unsigned int	rep_mask;
462    int			dx, dy;
463    TPoint		mouse;
464    Display		*dpy = XtDisplay (w);
465    Window		win = XtWindow (w);
466
467    XQueryPointer (dpy, win, &rep_root, &rep_child,
468	    &rep_rootx, &rep_rooty, &dx, &dy, &rep_mask);
469    mouse.x = Tx(dx, dy, &w->eyes.t);
470    mouse.y = Ty(dx, dy, &w->eyes.t);
471
472    drawEyes(w, mouse);
473}
474
475/* ARGSUSED */
476static void draw_it (
477     XtPointer client_data,
478     XtIntervalId *id)		/* unused */
479{
480        EyesWidget	w = (EyesWidget)client_data;
481
482	if (XtIsRealized((Widget)w)) {
483	        draw_it_core(w);
484	}
485	w->eyes.interval_id =
486		XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) w),
487				delays[w->eyes.update], draw_it, (XtPointer)w);
488} /* draw_it */
489
490static void Resize (Widget gw)
491{
492    EyesWidget	w = (EyesWidget) gw;
493    XGCValues	xgcv;
494    Widget	parent;
495    Display	*dpy = XtDisplay (w);
496    int		x, y;
497
498    if (XtIsRealized (gw))
499    {
500	XClearWindow (dpy, XtWindow (w));
501    	SetTransform (&w->eyes.t,
502		    	0, w->core.width,
503 		    	w->core.height, 0,
504		    	W_MIN_X, W_MAX_X,
505		    	W_MIN_Y, W_MAX_Y);
506#ifdef XRENDER
507	if (w->eyes.picture) {
508	    XRenderFreePicture(dpy, w->eyes.picture);
509	    w->eyes.picture = 0;
510	}
511#endif
512    	if (w->eyes.shape_window) {
513	    w->eyes.shape_mask = XCreatePixmap (dpy, XtWindow (w),
514	    	    w->core.width, w->core.height, 1);
515	    if (!w->eyes.gc[PART_SHAPE])
516		w->eyes.gc[PART_SHAPE] = XCreateGC (dpy, w->eyes.shape_mask,
517						    0, &xgcv);
518	    XSetForeground (dpy, w->eyes.gc[PART_SHAPE], 0);
519	    XFillRectangle (dpy, w->eyes.shape_mask, w->eyes.gc[PART_SHAPE],
520			    0, 0, w->core.width, w->core.height);
521	    XSetForeground (dpy, w->eyes.gc[PART_SHAPE], 1);
522	    eyeLiner (w, FALSE, 0);
523	    eyeLiner (w, FALSE, 1);
524	    x = y = 0;
525	    for (parent = (Widget) w; XtParent (parent); parent = XtParent (parent)) {
526	    	x += parent->core.x + parent->core.border_width;
527	    	x += parent->core.y + parent->core.border_width;
528	    }
529    	    XShapeCombineMask (XtDisplay (parent), XtWindow (parent), ShapeBounding,
530		       	       x, y, w->eyes.shape_mask, ShapeSet);
531	    XFreePixmap (dpy, w->eyes.shape_mask);
532    	}
533#ifdef XRENDER
534	if (w->eyes.render) {
535	    XRenderPictureAttributes pa;
536	    XRenderPictFormat *pf;
537	    pf = XRenderFindVisualFormat(dpy,
538					 DefaultVisualOfScreen(w->core.screen));
539	    if (pf)
540		w->eyes.picture = XRenderCreatePicture(dpy, XtWindow (w),
541						       pf, 0, &pa);
542	}
543#endif
544    }
545}
546
547static void Realize (
548     Widget gw,
549     XtValueMask *valueMask,
550     XSetWindowAttributes *attrs)
551{
552    EyesWidget	w = (EyesWidget)gw;
553
554    if (w->eyes.backing_store != Always + WhenMapped + NotUseful) {
555     	attrs->backing_store = w->eyes.backing_store;
556	*valueMask |= CWBackingStore;
557    }
558    XtCreateWindow( gw, (unsigned)InputOutput, (Visual *)CopyFromParent,
559		     *valueMask, attrs );
560    Resize (gw);
561    w->eyes.interval_id =
562	XtAppAddTimeOut(XtWidgetToApplicationContext(gw),
563			delays[w->eyes.update], draw_it, (XtPointer)gw);
564}
565
566static void Destroy (Widget gw)
567{
568     EyesWidget w = (EyesWidget)gw;
569     int i;
570
571     if (w->eyes.interval_id)
572	XtRemoveTimeOut (w->eyes.interval_id);
573     for (i = 0; i < PART_MAX; i ++)
574	     XtReleaseGC(gw, w->eyes.gc[i]);
575#ifdef XRENDER
576     if (w->eyes.picture)
577	     XRenderFreePicture (XtDisplay(w), w->eyes.picture);
578#endif
579}
580
581/* ARGSUSED */
582static void Redisplay(
583     Widget gw,
584     XEvent *event,
585     Region region)
586{
587    EyesWidget	w;
588
589    w = (EyesWidget) gw;
590    w->eyes.pupil[0].x = TPOINT_NONE;
591    w->eyes.pupil[0].y = TPOINT_NONE;
592    w->eyes.pupil[1].x = TPOINT_NONE;
593    w->eyes.pupil[1].y = TPOINT_NONE;
594    (void) repaint_window ((EyesWidget)gw);
595}
596
597/* ARGSUSED */
598static Boolean SetValues (
599    Widget current,
600    Widget request,
601    Widget new,
602    ArgList args,
603    Cardinal *num_args)
604{
605    return( FALSE );
606}
607
608EyesClassRec eyesClassRec = {
609    { /* core fields */
610    /* superclass		*/	&widgetClassRec,
611    /* class_name		*/	"Eyes",
612    /* size			*/	sizeof(EyesRec),
613    /* class_initialize		*/	ClassInitialize,
614    /* class_part_initialize	*/	NULL,
615    /* class_inited		*/	FALSE,
616    /* initialize		*/	Initialize,
617    /* initialize_hook		*/	NULL,
618    /* realize			*/	Realize,
619    /* actions			*/	NULL,
620    /* num_actions		*/	0,
621    /* resources		*/	resources,
622    /* num_resources		*/	XtNumber(resources),
623    /* xrm_class		*/	NULLQUARK,
624    /* compress_motion		*/	TRUE,
625    /* compress_exposure	*/	TRUE,
626    /* compress_enterleave	*/	TRUE,
627    /* visible_interest		*/	FALSE,
628    /* destroy			*/	Destroy,
629    /* resize			*/	Resize,
630    /* expose			*/	Redisplay,
631    /* set_values		*/	SetValues,
632    /* set_values_hook		*/	NULL,
633    /* set_values_almost	*/	NULL,
634    /* get_values_hook		*/	NULL,
635    /* accept_focus		*/	NULL,
636    /* version			*/	XtVersion,
637    /* callback_private		*/	NULL,
638    /* tm_table			*/	NULL,
639    /* query_geometry		*/	XtInheritQueryGeometry,
640    }
641};
642