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