Eyes.c revision 26df5c7c
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# include <X11/extensions/XInput2.h> 52 53#define offset(field) XtOffsetOf(EyesRec, eyes.field) 54#define goffset(field) XtOffsetOf(WidgetRec, core.field) 55 56static XtResource resources[] = { 57 {(char *) XtNwidth, (char *) XtCWidth, XtRDimension, sizeof(Dimension), 58 goffset(width), XtRImmediate, (XtPointer) 150}, 59 {(char *) XtNheight, (char *) XtCHeight, XtRDimension, sizeof(Dimension), 60 goffset(height), XtRImmediate, (XtPointer) 100}, 61 {(char *) XtNforeground, (char *) XtCForeground, XtRPixel, sizeof(Pixel), 62 offset(pixel[PART_PUPIL]), XtRString, (char *) XtDefaultForeground}, 63 {(char *) XtNbackgroundPixmap, (char *) XtCPixmap, XtRPixmap, sizeof(Pixmap), 64 XtOffsetOf(CoreRec,core.background_pixmap), 65 XtRImmediate, (XtPointer)None}, 66 {(char *) XtNoutline, (char *) XtCForeground, XtRPixel, sizeof(Pixel), 67 offset(pixel[PART_OUTLINE]), XtRString, (char *) XtDefaultForeground}, 68 {(char *) XtNcenterColor, (char *) XtCBackground, XtRPixel, sizeof (Pixel), 69 offset(pixel[PART_CENTER]), XtRString, (char *) XtDefaultBackground}, 70 {(char *) XtNreverseVideo, (char *) XtCReverseVideo, XtRBoolean, sizeof (Boolean), 71 offset (reverse_video), XtRImmediate, (XtPointer) FALSE}, 72 {(char *) XtNbackingStore, (char *) XtCBackingStore, (char *) XtRBackingStore, sizeof (int), 73 offset (backing_store), XtRString, (char *) "default"}, 74 {(char *) XtNshapeWindow, (char *) XtCShapeWindow, XtRBoolean, sizeof (Boolean), 75 offset (shape_window), XtRImmediate, (XtPointer) TRUE}, 76#ifdef XRENDER 77 {(char *) XtNrender, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean), 78 offset(render), XtRImmediate, (XtPointer) TRUE }, 79#endif 80#ifdef PRESENT 81 {(char *) XtNpresent, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean), 82 offset(present), XtRImmediate, (XtPointer) TRUE }, 83#endif 84 {(char *) XtNdistance, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean), 85 offset(distance), XtRImmediate, (XtPointer) FALSE }, 86}; 87 88#undef offset 89#undef goffset 90 91# define EYE_X(n) ((n) * 2.0) 92# define EYE_Y(n) (0.0) 93# define EYE_OFFSET (0.1) /* padding between eyes */ 94# define EYE_THICK (0.175) /* thickness of eye rim */ 95# define BALL_DIAM (0.3) 96# define BALL_PAD (0.175) 97# define EYE_DIAM (2.0 - (EYE_THICK + EYE_OFFSET) * 2) 98# define BALL_DIST ((EYE_DIAM - BALL_DIAM) / 2.0 - BALL_PAD) 99# define W_MIN_X (-1.0 + EYE_OFFSET) 100# define W_MAX_X (3.0 - EYE_OFFSET) 101# define W_MIN_Y (-1.0 + EYE_OFFSET) 102# define W_MAX_Y (1.0 - EYE_OFFSET) 103 104# define TPOINT_NONE (-1000) /* special value meaning "not yet set" */ 105# define TPointEqual(a, b) ((a).x == (b).x && (a).y == (b).y) 106# define XPointEqual(a, b) ((a).x == (b).x && (a).y == (b).y) 107# define AngleBetween(A, A0, A1) (A0 <= A1 ? A0 <= A && A <= A1 : \ 108 A0 <= A || A <= A1) 109 110static int delays[] = { 50, 100, 200, 400, 0 }; 111 112static void ClassInitialize(void) 113{ 114 XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore, 115 NULL, 0 ); 116} 117 118WidgetClass eyesWidgetClass = (WidgetClass) &eyesClassRec; 119 120#ifdef PRESENT 121static void CheckPresent(EyesWidget w) { 122 const xcb_query_extension_reply_t *xfixes_ext_reply; 123 const xcb_query_extension_reply_t *damage_ext_reply; 124 const xcb_query_extension_reply_t *present_ext_reply; 125 xcb_xfixes_query_version_cookie_t xfixes_cookie; 126 xcb_xfixes_query_version_reply_t *xfixes_reply; 127 xcb_damage_query_version_cookie_t damage_cookie; 128 xcb_damage_query_version_reply_t *damage_reply; 129 xcb_present_query_version_cookie_t present_cookie; 130 xcb_present_query_version_reply_t *present_reply; 131 132 if (!w->eyes.present) 133 return; 134 135 xcb_prefetch_extension_data(xt_xcb(w), &xcb_xfixes_id); 136 xcb_prefetch_extension_data(xt_xcb(w), &xcb_damage_id); 137 xcb_prefetch_extension_data(xt_xcb(w), &xcb_present_id); 138 139 xfixes_ext_reply = xcb_get_extension_data(xt_xcb(w), &xcb_xfixes_id); 140 damage_ext_reply = xcb_get_extension_data(xt_xcb(w), &xcb_damage_id); 141 present_ext_reply = xcb_get_extension_data(xt_xcb(w), &xcb_present_id); 142 if (xfixes_ext_reply == NULL || !xfixes_ext_reply->present 143 || damage_ext_reply == NULL || !damage_ext_reply->present 144 || present_ext_reply == NULL || !present_ext_reply->present) 145 { 146 w->eyes.present = FALSE; 147 } 148 149 if (!w->eyes.present) 150 return; 151 152 /* Now tell the server which versions of the extensions we support */ 153 xfixes_cookie = xcb_xfixes_query_version(xt_xcb(w), 154 XCB_XFIXES_MAJOR_VERSION, 155 XCB_XFIXES_MINOR_VERSION); 156 157 damage_cookie = xcb_damage_query_version(xt_xcb(w), 158 XCB_DAMAGE_MAJOR_VERSION, 159 XCB_DAMAGE_MINOR_VERSION); 160 161 present_cookie = xcb_present_query_version(xt_xcb(w), 162 XCB_PRESENT_MAJOR_VERSION, 163 XCB_PRESENT_MINOR_VERSION); 164 165 xfixes_reply = xcb_xfixes_query_version_reply(xt_xcb(w), 166 xfixes_cookie, 167 NULL); 168 free(xfixes_reply); 169 170 damage_reply = xcb_damage_query_version_reply(xt_xcb(w), 171 damage_cookie, 172 NULL); 173 free(damage_reply); 174 175 present_reply = xcb_present_query_version_reply(xt_xcb(w), 176 present_cookie, 177 NULL); 178 free(present_reply); 179} 180 181static void MakePresentData(EyesWidget w) { 182 183 if (!w->eyes.present) 184 return; 185 186 if (!w->eyes.back_buffer) { 187 xcb_create_pixmap(xt_xcb(w), 188 w->core.depth, 189 w->eyes.back_buffer = xcb_generate_id(xt_xcb(w)), 190 XtWindow(w), 191 w->core.width, 192 w->core.height); 193 } 194 if (!w->eyes.back_damage) { 195 xcb_damage_create(xt_xcb(w), 196 w->eyes.back_damage = xcb_generate_id(xt_xcb(w)), 197 w->eyes.back_buffer, 198 XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY); 199 xcb_xfixes_create_region(xt_xcb(w), 200 w->eyes.back_region = xcb_generate_id(xt_xcb(w)), 201 0, NULL); 202 } 203} 204 205static void UpdatePresent(EyesWidget w) { 206 if (w->eyes.back_buffer) { 207 xcb_damage_subtract(xt_xcb(w), 208 w->eyes.back_damage, 209 None, 210 w->eyes.back_region); 211 xcb_present_pixmap(xt_xcb(w), 212 XtWindow(w), 213 w->eyes.back_buffer, 214 0, 215 None, 216 w->eyes.back_region, 217 0, 0, 218 None, 219 None, 220 None, 221 0, 222 0, 1, 0, 223 0, NULL); 224 } 225} 226 227#endif 228 229#ifdef PRESENT 230#define EyesDrawable(w) (w->eyes.back_buffer ? w->eyes.back_buffer : XtWindow(w)) 231#else 232#define EyesDrawable(w) XtWindow(w) 233#endif 234 235static void draw_it_core(EyesWidget w); 236 237static void EyesGeneric(Widget w, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch) 238{ 239 draw_it_core((EyesWidget) w); 240} 241 242struct root_listen_list { 243 struct root_listen_list *next; 244 Widget widget; 245}; 246 247static struct root_listen_list *root_listen_list; 248 249static Boolean xi2_dispatcher(XEvent *event) { 250 struct root_listen_list *rll; 251 Boolean was_dispatched = False; 252 253 for (rll = root_listen_list; rll; rll = rll->next) { 254 if (XtDisplay(rll->widget) == event->xany.display) { 255 XtDispatchEventToWidget(rll->widget, event); 256 was_dispatched = True; 257 } 258 } 259 return was_dispatched; 260} 261 262static void select_xi2_events(Widget w) 263{ 264 XIEventMask evmasks[1]; 265 unsigned char mask1[(XI_LASTEVENT + 7)/8]; 266 267 memset(mask1, 0, sizeof(mask1)); 268 269 /* select for button and key events from all master devices */ 270 XISetMask(mask1, XI_RawMotion); 271 272 evmasks[0].deviceid = XIAllMasterDevices; 273 evmasks[0].mask_len = sizeof(mask1); 274 evmasks[0].mask = mask1; 275 276 XISelectEvents(XtDisplay(w), 277 RootWindowOfScreen(XtScreen(w)), 278 evmasks, 1); 279 XtSetEventDispatcher(XtDisplay(w), 280 GenericEvent, 281 xi2_dispatcher); 282} 283 284static Boolean xi2_add_root_listener(Widget widget) 285{ 286 struct root_listen_list *rll = malloc (sizeof (struct root_listen_list)); 287 288 if (!rll) 289 return False; 290 rll->widget = widget; 291 rll->next = root_listen_list; 292 if (!root_listen_list) 293 select_xi2_events(widget); 294 root_listen_list = rll; 295 XtInsertEventTypeHandler(widget, GenericEvent, NULL, EyesGeneric, NULL, XtListHead); 296 return True; 297} 298 299static void xi2_remove_root_listener(Widget widget) 300{ 301 struct root_listen_list *rll, **prev; 302 303 for (prev = &root_listen_list; (rll = *prev) != NULL; prev = &rll->next) { 304 if (rll->widget == widget) { 305 *prev = rll->next; 306 free(rll); 307 break; 308 } 309 } 310} 311 312/* Return 1 if XI2 is available, 0 otherwise */ 313static int has_xi2(Display *dpy) 314{ 315 int major, minor; 316 int rc; 317 318 /* We need at least XI 2.0 */ 319 major = 2; 320 minor = 0; 321 322 rc = XIQueryVersion(dpy, &major, &minor); 323 if (rc == BadRequest) { 324 return 0; 325 } else if (rc != Success) { 326 return 0; 327 } 328 return 1; 329} 330 331 332/* ARGSUSED */ 333static void Initialize ( 334 Widget greq, 335 Widget gnew, 336 ArgList args, 337 Cardinal *num_args) 338{ 339 EyesWidget w = (EyesWidget)gnew; 340 XtGCMask valuemask; 341 XGCValues myXGCV; 342 int shape_event_base, shape_error_base; 343#ifdef XRENDER 344 enum EyesPart i; 345#endif 346 347 /* 348 * set the colors if reverse video; these are the colors used: 349 * 350 * background - paper white 351 * foreground - text, ticks black 352 * border - border black (foreground) 353 * 354 * This doesn't completely work since the parent has already made up a 355 * border. Sigh. 356 */ 357 if (w->eyes.reverse_video) { 358 Pixel fg = w->eyes.pixel[PART_PUPIL]; 359 Pixel bg = w->core.background_pixel; 360 361 if (w->core.border_pixel == fg) 362 w->core.border_pixel = bg; 363 if (w->eyes.pixel[PART_OUTLINE] == fg) 364 w->eyes.pixel[PART_OUTLINE] = bg; 365 if (w->eyes.pixel[PART_CENTER] == bg) 366 w->eyes.pixel[PART_CENTER] = fg; 367 w->eyes.pixel[PART_PUPIL] = bg; 368 w->core.background_pixel = fg; 369 } 370 371 myXGCV.foreground = w->eyes.pixel[PART_PUPIL]; 372 myXGCV.background = w->core.background_pixel; 373 valuemask = GCForeground | GCBackground; 374 w->eyes.gc[PART_PUPIL] = XtGetGC(gnew, valuemask, &myXGCV); 375 376 myXGCV.foreground = w->eyes.pixel[PART_OUTLINE]; 377 valuemask = GCForeground | GCBackground; 378 w->eyes.gc[PART_OUTLINE] = XtGetGC(gnew, valuemask, &myXGCV); 379 380 myXGCV.foreground = w->eyes.pixel[PART_CENTER]; 381 myXGCV.background = w->eyes.pixel[PART_PUPIL]; 382 valuemask = GCForeground | GCBackground; 383 w->eyes.gc[PART_CENTER] = XtGetGC(gnew, valuemask, &myXGCV); 384 385 w->eyes.update = 0; 386 /* wait for Realize to add the timeout */ 387 w->eyes.interval_id = 0; 388 389 w->eyes.pupil[0].x = w->eyes.pupil[1].x = TPOINT_NONE; 390 w->eyes.pupil[0].y = w->eyes.pupil[1].y = TPOINT_NONE; 391 392 w->eyes.mouse.x = w->eyes.mouse.y = TPOINT_NONE; 393 394 if (w->eyes.shape_window && !XShapeQueryExtension (XtDisplay (w), 395 &shape_event_base, 396 &shape_error_base)) 397 w->eyes.shape_window = False; 398 w->eyes.shape_mask = 0; 399 w->eyes.gc[PART_SHAPE] = NULL; 400 401 w->eyes.has_xi2 = has_xi2(XtDisplay(w)); 402 403#ifdef XRENDER 404 for (i = 0; i < PART_SHAPE; i ++) { 405 XColor c; 406 XRenderColor rc; 407 408 c.pixel = w->eyes.pixel[i]; 409 XQueryColor(XtDisplay (w), w->core.colormap, &c); 410 411 rc.red = c.red; 412 rc.green = c.green; 413 rc.blue = c.blue; 414 rc.alpha = -1; 415 w->eyes.fill[i] = XRenderCreateSolidFill(XtDisplay (w), &rc); 416 } 417#endif 418#ifdef PRESENT 419 w->eyes.back_buffer = None; 420 w->eyes.back_damage = None; 421 CheckPresent(w); 422#endif 423} 424 425static void 426drawEllipse(EyesWidget w, enum EyesPart part, 427 double centerx, double centery, 428 double oldx, double oldy, 429 double diam) 430{ 431 const TRectangle tpos = { 432 centerx - diam/2.0, 433 centery - diam/2.0, 434 diam, diam }; 435 TRectangle pos; 436 Trectangle(&w->eyes.t, &tpos, &pos); 437 438 if (part == PART_CLEAR) { 439 XFillRectangle(XtDisplay(w), EyesDrawable(w), 440 w->eyes.gc[PART_CENTER], 441 (int)pos.x, (int)pos.y, 442 (int)pos.width+2, (int)pos.height+2); 443 return; 444 } 445#ifdef XRENDER 446 if (w->eyes.render && part != PART_SHAPE && (!w->eyes.shape_window || 447 part != PART_OUTLINE) && 448 w->eyes.picture) { 449 int n, i; 450 double hd, c, s, sx, sy, x, y, px, py; 451 XPointDouble *p; 452 453 pos.x = pos.x + pos.width/2.0; 454 pos.y = pos.y + pos.height/2.0; 455 456 /* determine number of segments to draw */ 457 hd = hypot(pos.width, pos.height)/2; 458 n = (M_PI / acos(hd/(hd+1.0))) + 0.5; 459 if (n < 2) n = 2; 460 461 c = cos(M_PI/n); 462 s = sin(M_PI/n); 463 sx = -(pos.width*s)/pos.height; 464 sy = (pos.height*s)/pos.width; 465 466 n *= 2; 467 p = Xmalloc(sizeof(*p)*n); 468 if (!p) 469 return; 470 x = 0; 471 y = pos.height/2.0; 472 for (i = 0; i < n; i ++) 473 { 474 p[i].x = x + pos.x; 475 p[i].y = y + pos.y; 476 px = x; 477 py = y; 478 x = c*px + sx*py; 479 y = c*py + sy*px; 480 } 481 482 if (oldx != TPOINT_NONE || oldy != TPOINT_NONE) 483 drawEllipse(w, PART_CLEAR, oldx, oldy, 484 TPOINT_NONE, TPOINT_NONE, diam); 485 486 XRenderCompositeDoublePoly(XtDisplay(w), PictOpOver, 487 w->eyes.fill[part], w->eyes.picture, 488 XRenderFindStandardFormat(XtDisplay(w), 489 PictStandardA8), 490 0, 0, 0, 0, p, n, 0); 491 492 Xfree(p); 493 return; 494 } 495#endif 496 if (oldx != TPOINT_NONE || oldy != TPOINT_NONE) 497 drawEllipse(w, PART_CLEAR, oldx, oldy, 498 TPOINT_NONE, TPOINT_NONE, diam); 499 500 XFillArc(XtDisplay(w), 501 part == PART_SHAPE ? w->eyes.shape_mask : EyesDrawable(w), 502 w->eyes.gc[part], 503 (int)(pos.x + 0.5), (int)(pos.y + 0.5), 504 (int)(pos.width + 0.0), (int)(pos.height + 0.0), 505 90*64, 360*64); 506} 507 508 509static void 510eyeLiner(EyesWidget w, 511 Boolean draw, 512 int num) 513{ 514 drawEllipse(w, draw ? PART_OUTLINE : PART_SHAPE, 515 EYE_X(num), EYE_Y(num), 516 TPOINT_NONE, TPOINT_NONE, 517 EYE_DIAM + 2.0*EYE_THICK); 518 if (draw) { 519 drawEllipse(w, PART_CENTER, EYE_X(num), EYE_Y(num), 520 TPOINT_NONE, TPOINT_NONE, 521 EYE_DIAM); 522 } 523} 524 525static TPoint computePupil ( 526 int num, 527 TPoint mouse, 528 const TRectangle *screen) 529{ 530 double cx, cy; 531 double dist; 532 double angle; 533 double dx, dy; 534 double cosa, sina; 535 TPoint ret; 536 537 cx = EYE_X(num); dx = mouse.x - cx; 538 cy = EYE_Y(num); dy = mouse.y - cy; 539 if (dx == 0 && dy == 0); 540 else { 541 angle = atan2 ((double) dy, (double) dx); 542 cosa = cos (angle); 543 sina = sin (angle); 544 dist = BALL_DIST; 545 if (screen) 546 { 547 /* use distance mapping */ 548 double x0, y0, x1, y1; 549 double a[4]; 550 x0 = screen->x - cx; 551 y0 = screen->y - cy; 552 x1 = x0 + screen->width; 553 y1 = y0 + screen->height; 554 a[0] = atan2(y0, x0); 555 a[1] = atan2(y1, x0); 556 a[2] = atan2(y1, x1); 557 a[3] = atan2(y0, x1); 558 if (AngleBetween(angle, a[0], a[1])) 559 { 560 /* left */ 561 dist *= dx / x0; 562 } 563 else if (AngleBetween(angle, a[1], a[2])) 564 { 565 /* bottom */ 566 dist *= dy / y1; 567 } 568 else if (AngleBetween(angle, a[2], a[3])) 569 { 570 /* right */ 571 dist *= dx / x1; 572 } 573 else if (AngleBetween(angle, a[3], a[0])) 574 { 575 /* top */ 576 dist *= dy / y0; 577 } 578 if (dist > BALL_DIST) 579 dist = BALL_DIST; 580 } 581 if (dist > hypot ((double) dx, (double) dy)) { 582 cx += dx; 583 cy += dy; 584 } else { 585 cx += dist * cosa; 586 cy += dist * sina; 587 } 588 } 589 ret.x = cx; 590 ret.y = cy; 591 return ret; 592} 593 594static void computePupils ( 595 EyesWidget w, 596 TPoint mouse, 597 TPoint pupils[2]) 598{ 599 TRectangle screen, *sp = NULL; 600 if (w->eyes.distance) { 601 Window r, cw; 602 int x, y; 603 r = RootWindowOfScreen(w->core.screen); 604 XTranslateCoordinates(XtDisplay(w), XtWindow(w), r, 0, 0, &x, &y, &cw); 605 screen.x = Tx(-x, -y, &w->eyes.t); 606 screen.y = Ty(-x, -y, &w->eyes.t); 607 screen.width = Twidth (w->core.screen->width, w->core.screen->height, 608 &w->eyes.t); 609 screen.height = Theight(w->core.screen->width, w->core.screen->height, 610 &w->eyes.t); 611 sp = &screen; 612 } 613 pupils[0] = computePupil (0, mouse, sp); 614 pupils[1] = computePupil (1, mouse, sp); 615} 616 617static void 618eyeBall(EyesWidget w, 619 Boolean draw, 620 TPoint *old, 621 int num) 622{ 623 drawEllipse(w, draw ? PART_PUPIL : PART_CLEAR, 624 w->eyes.pupil[num].x, w->eyes.pupil[num].y, 625 old ? old->x : TPOINT_NONE, old ? old->y : TPOINT_NONE, 626 BALL_DIAM); 627} 628 629static void repaint_window (EyesWidget w) 630{ 631 if (XtIsRealized ((Widget) w)) { 632#ifdef PRESENT 633 MakePresentData(w); 634#endif 635 eyeLiner (w, TRUE, 0); 636 eyeLiner (w, TRUE, 1); 637 computePupils (w, w->eyes.mouse, w->eyes.pupil); 638 eyeBall (w, TRUE, NULL, 0); 639 eyeBall (w, TRUE, NULL, 1); 640#ifdef PRESENT 641 UpdatePresent(w); 642#endif 643 } 644} 645 646static void 647drawEye(EyesWidget w, TPoint newpupil, int num) 648{ 649 XPoint xnewpupil, xpupil; 650 651 xpupil.x = Xx(w->eyes.pupil[num].x, w->eyes.pupil[num].y, &w->eyes.t); 652 xpupil.y = Xy(w->eyes.pupil[num].x, w->eyes.pupil[num].y, &w->eyes.t); 653 xnewpupil.x = Xx(newpupil.x, newpupil.y, &w->eyes.t); 654 xnewpupil.y = Xy(newpupil.x, newpupil.y, &w->eyes.t); 655 if ( 656#ifdef XRENDER 657 w->eyes.picture ? !TPointEqual(w->eyes.pupil[num], newpupil) : 658#endif 659 !XPointEqual(xpupil, xnewpupil)) { 660 TPoint oldpupil = w->eyes.pupil[num]; 661 w->eyes.pupil[num] = newpupil; 662 eyeBall (w, TRUE, &oldpupil, num); 663 } 664} 665 666static void 667drawEyes(EyesWidget w, TPoint mouse) 668{ 669 TPoint newpupil[2]; 670 int num; 671 672#ifdef PRESENT 673 MakePresentData(w); 674#endif 675 if (TPointEqual (mouse, w->eyes.mouse)) { 676 if (delays[w->eyes.update + 1] != 0) 677 ++w->eyes.update; 678 return; 679 } 680 computePupils (w, mouse, newpupil); 681 for (num = 0; num < 2; num ++) { 682 drawEye(w, newpupil[num], num); 683 } 684 685 w->eyes.mouse = mouse; 686 w->eyes.update = 0; 687#ifdef PRESENT 688 UpdatePresent(w); 689#endif 690} 691 692static void draw_it_core(EyesWidget w) 693{ 694 Window rep_root, rep_child; 695 int rep_rootx, rep_rooty; 696 unsigned int rep_mask; 697 int dx, dy; 698 TPoint mouse; 699 Display *dpy = XtDisplay (w); 700 Window win = XtWindow (w); 701 702 XQueryPointer (dpy, win, &rep_root, &rep_child, 703 &rep_rootx, &rep_rooty, &dx, &dy, &rep_mask); 704 mouse.x = Tx(dx, dy, &w->eyes.t); 705 mouse.y = Ty(dx, dy, &w->eyes.t); 706 707 drawEyes(w, mouse); 708} 709 710/* ARGSUSED */ 711static void draw_it ( 712 XtPointer client_data, 713 XtIntervalId *id) /* unused */ 714{ 715 EyesWidget w = (EyesWidget)client_data; 716 717 if (XtIsRealized((Widget)w)) { 718 draw_it_core(w); 719 } 720 if (!w->eyes.has_xi2) { 721 w->eyes.interval_id = 722 XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) w), 723 delays[w->eyes.update], draw_it, (XtPointer)w); 724 } 725} /* draw_it */ 726 727static void Resize (Widget gw) 728{ 729 EyesWidget w = (EyesWidget) gw; 730 XGCValues xgcv; 731 Widget parent; 732 Display *dpy = XtDisplay (w); 733 int x, y; 734 735 if (XtIsRealized (gw)) 736 { 737 SetTransform (&w->eyes.t, 738 0, w->core.width, 739 w->core.height, 0, 740 W_MIN_X, W_MAX_X, 741 W_MIN_Y, W_MAX_Y); 742#ifdef PRESENT 743 if (w->eyes.back_buffer) { 744 xcb_free_pixmap(xt_xcb(w), 745 w->eyes.back_buffer); 746 w->eyes.back_buffer = None; 747 xcb_damage_destroy(xt_xcb(w), 748 w->eyes.back_damage); 749 w->eyes.back_damage = None; 750 } 751 MakePresentData(w); 752#endif 753 if (EyesDrawable(w) == XtWindow(w)) 754 XClearWindow (dpy, XtWindow (w)); 755 756#ifdef XRENDER 757 if (w->eyes.picture) { 758 XRenderFreePicture(dpy, w->eyes.picture); 759 w->eyes.picture = 0; 760 } 761#endif 762 if (w->eyes.shape_window) { 763 w->eyes.shape_mask = XCreatePixmap (dpy, XtWindow (w), 764 w->core.width, w->core.height, 1); 765 if (!w->eyes.gc[PART_SHAPE]) 766 w->eyes.gc[PART_SHAPE] = XCreateGC (dpy, w->eyes.shape_mask, 767 0, &xgcv); 768 XSetForeground (dpy, w->eyes.gc[PART_SHAPE], 0); 769 XFillRectangle (dpy, w->eyes.shape_mask, w->eyes.gc[PART_SHAPE], 770 0, 0, w->core.width, w->core.height); 771 XSetForeground (dpy, w->eyes.gc[PART_SHAPE], 1); 772 eyeLiner (w, FALSE, 0); 773 eyeLiner (w, FALSE, 1); 774 x = y = 0; 775 for (parent = (Widget) w; XtParent (parent); parent = XtParent (parent)) { 776 x += parent->core.x + parent->core.border_width; 777 x += parent->core.y + parent->core.border_width; 778 } 779 XShapeCombineMask (XtDisplay (parent), XtWindow (parent), ShapeBounding, 780 x, y, w->eyes.shape_mask, ShapeSet); 781 XFreePixmap (dpy, w->eyes.shape_mask); 782 } 783#ifdef XRENDER 784 if (w->eyes.render) { 785 XRenderPictureAttributes pa; 786 XRenderPictFormat *pf; 787 pf = XRenderFindVisualFormat(dpy, 788 DefaultVisualOfScreen(w->core.screen)); 789 if (pf) 790 w->eyes.picture = XRenderCreatePicture(dpy, EyesDrawable (w), 791 pf, 0, &pa); 792 } 793#endif 794 } 795} 796 797static void Realize ( 798 Widget gw, 799 XtValueMask *valueMask, 800 XSetWindowAttributes *attrs) 801{ 802 EyesWidget w = (EyesWidget)gw; 803 804 if (w->eyes.backing_store != Always + WhenMapped + NotUseful) { 805 attrs->backing_store = w->eyes.backing_store; 806 *valueMask |= CWBackingStore; 807 } 808 XtCreateWindow( gw, (unsigned)InputOutput, (Visual *)CopyFromParent, 809 *valueMask, attrs ); 810 Resize (gw); 811 812 if (w->eyes.has_xi2) 813 xi2_add_root_listener(gw); 814 else 815 w->eyes.interval_id = 816 XtAppAddTimeOut(XtWidgetToApplicationContext(gw), 817 delays[w->eyes.update], draw_it, (XtPointer)gw); 818} 819 820static void Destroy (Widget gw) 821{ 822 EyesWidget w = (EyesWidget)gw; 823 int i; 824 825 if (w->eyes.interval_id) 826 XtRemoveTimeOut (w->eyes.interval_id); 827 for (i = 0; i < PART_MAX; i ++) 828 XtReleaseGC(gw, w->eyes.gc[i]); 829 xi2_remove_root_listener(gw); 830#ifdef XRENDER 831 if (w->eyes.picture) 832 XRenderFreePicture (XtDisplay(w), w->eyes.picture); 833#endif 834} 835 836/* ARGSUSED */ 837static void Redisplay( 838 Widget gw, 839 XEvent *event, 840 Region region) 841{ 842 EyesWidget w; 843 844 w = (EyesWidget) gw; 845 w->eyes.pupil[0].x = TPOINT_NONE; 846 w->eyes.pupil[0].y = TPOINT_NONE; 847 w->eyes.pupil[1].x = TPOINT_NONE; 848 w->eyes.pupil[1].y = TPOINT_NONE; 849 (void) repaint_window ((EyesWidget)gw); 850} 851 852/* ARGSUSED */ 853static Boolean SetValues ( 854 Widget current, 855 Widget request, 856 Widget new, 857 ArgList args, 858 Cardinal *num_args) 859{ 860 return( FALSE ); 861} 862 863EyesClassRec eyesClassRec = { 864 { /* core fields */ 865 /* superclass */ &widgetClassRec, 866 /* class_name */ (char *) "Eyes", 867 /* size */ sizeof(EyesRec), 868 /* class_initialize */ ClassInitialize, 869 /* class_part_initialize */ NULL, 870 /* class_inited */ FALSE, 871 /* initialize */ Initialize, 872 /* initialize_hook */ NULL, 873 /* realize */ Realize, 874 /* actions */ NULL, 875 /* num_actions */ 0, 876 /* resources */ resources, 877 /* num_resources */ XtNumber(resources), 878 /* xrm_class */ NULLQUARK, 879 /* compress_motion */ TRUE, 880 /* compress_exposure */ TRUE, 881 /* compress_enterleave */ TRUE, 882 /* visible_interest */ FALSE, 883 /* destroy */ Destroy, 884 /* resize */ Resize, 885 /* expose */ Redisplay, 886 /* set_values */ SetValues, 887 /* set_values_hook */ NULL, 888 /* set_values_almost */ NULL, 889 /* get_values_hook */ NULL, 890 /* accept_focus */ NULL, 891 /* version */ XtVersion, 892 /* callback_private */ NULL, 893 /* tm_table */ NULL, 894 /* query_geometry */ XtInheritQueryGeometry, 895 } 896}; 897