Eyes.c revision a1d141d5
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# include <X11/Xos.h> 40# include <stdio.h> 41# include <X11/IntrinsicP.h> 42# include <X11/StringDefs.h> 43# include <X11/Xmu/Converters.h> 44# include "EyesP.h" 45# include <math.h> 46# include <X11/extensions/shape.h> 47 48#if (defined(SVR4) || defined(SYSV) && defined(i386)) 49extern double hypot(double, double); 50#endif 51 52#define offset(field) XtOffsetOf(EyesRec, eyes.field) 53#define goffset(field) XtOffsetOf(WidgetRec, core.field) 54 55static XtResource resources[] = { 56 {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension), 57 goffset(width), XtRImmediate, (XtPointer) 150}, 58 {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension), 59 goffset(height), XtRImmediate, (XtPointer) 100}, 60 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), 61 offset(puppixel), XtRString, XtDefaultForeground}, 62 {XtNoutline, XtCForeground, XtRPixel, sizeof(Pixel), 63 offset(outline), XtRString, XtDefaultForeground}, 64 {XtNcenterColor, XtCBackground, XtRPixel, sizeof (Pixel), 65 offset(center), XtRString, XtDefaultBackground}, 66 {XtNreverseVideo, XtCReverseVideo, XtRBoolean, sizeof (Boolean), 67 offset (reverse_video), XtRImmediate, (XtPointer) FALSE}, 68 {XtNbackingStore, XtCBackingStore, XtRBackingStore, sizeof (int), 69 offset (backing_store), XtRString, "default"}, 70 {XtNshapeWindow, XtCShapeWindow, XtRBoolean, sizeof (Boolean), 71 offset (shape_window), XtRImmediate, (XtPointer) TRUE}, 72}; 73 74#undef offset 75#undef goffset 76 77# define NUM_EYES 2 78# define EYE_X(n) ((n) * 2.0) 79# define EYE_Y(n) (0.0) 80# define EYE_OFFSET (0.1) /* padding between eyes */ 81# define EYE_THICK (0.175) /* thickness of eye rim */ 82# define BALL_WIDTH (0.3) 83# define BALL_PAD (0.05) 84# define EYE_WIDTH (2.0 - (EYE_THICK + EYE_OFFSET) * 2) 85# define EYE_HEIGHT EYE_WIDTH 86# define EYE_HWIDTH (EYE_WIDTH / 2.0) 87# define EYE_HHEIGHT (EYE_HEIGHT / 2.0) 88# define BALL_HEIGHT BALL_WIDTH 89# define BALL_DIST ((EYE_WIDTH - BALL_WIDTH) / 2.0 - BALL_PAD) 90# define W_MIN_X (-1.0 + EYE_OFFSET) 91# define W_MAX_X (3.0 - EYE_OFFSET) 92# define W_MIN_Y (-1.0 + EYE_OFFSET) 93# define W_MAX_Y (1.0 - EYE_OFFSET) 94 95# define TPointEqual(a, b) ((a).x == (b).x && (a).y == (b).y) 96# define XPointEqual(a, b) ((a).x == (b).x && (a).y == (b).y) 97 98static int delays[] = { 50, 100, 200, 400, 0 }; 99 100static void ClassInitialize(void) 101{ 102 XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore, 103 NULL, 0 ); 104} 105 106WidgetClass eyesWidgetClass = (WidgetClass) &eyesClassRec; 107 108/* ARGSUSED */ 109static void Initialize ( 110 Widget greq, 111 Widget gnew, 112 ArgList args, 113 Cardinal *num_args) 114{ 115 EyesWidget w = (EyesWidget)gnew; 116 XtGCMask valuemask; 117 XGCValues myXGCV; 118 int shape_event_base, shape_error_base; 119 120 /* 121 * set the colors if reverse video; these are the colors used: 122 * 123 * background - paper white 124 * foreground - text, ticks black 125 * border - border black (foreground) 126 * 127 * This doesn't completely work since the parent has already made up a 128 * border. Sigh. 129 */ 130 if (w->eyes.reverse_video) { 131 Pixel fg = w->eyes.puppixel; 132 Pixel bg = w->core.background_pixel; 133 134 if (w->core.border_pixel == fg) 135 w->core.border_pixel = bg; 136 if (w->eyes.outline == fg) 137 w->eyes.outline = bg; 138 if (w->eyes.center == bg) 139 w->eyes.center = fg; 140 w->eyes.puppixel = bg; 141 w->core.background_pixel = fg; 142 } 143 144 myXGCV.foreground = w->eyes.puppixel; 145 myXGCV.background = w->core.background_pixel; 146 valuemask = GCForeground | GCBackground; 147 w->eyes.pupGC = XtGetGC(gnew, valuemask, &myXGCV); 148 149 myXGCV.foreground = w->eyes.outline; 150 valuemask = GCForeground | GCBackground; 151 w->eyes.outGC = XtGetGC(gnew, valuemask, &myXGCV); 152 153 myXGCV.foreground = w->eyes.center; 154 myXGCV.background = w->eyes.puppixel; 155 valuemask = GCForeground | GCBackground; 156 w->eyes.centerGC = XtGetGC(gnew, valuemask, &myXGCV); 157 158 w->eyes.update = 0; 159 /* wait for Realize to add the timeout */ 160 w->eyes.interval_id = 0; 161 162 w->eyes.pupil[0].x = w->eyes.pupil[1].x = -1000; 163 w->eyes.pupil[0].y = w->eyes.pupil[1].y = -1000; 164 165 w->eyes.mouse.x = w->eyes.mouse.y = -1000; 166 167 if (w->eyes.shape_window && !XShapeQueryExtension (XtDisplay (w), 168 &shape_event_base, 169 &shape_error_base)) 170 w->eyes.shape_window = False; 171 w->eyes.shape_mask = 0; 172 w->eyes.shapeGC = 0; 173} 174 175static void eyeLiner ( 176 EyesWidget w, 177 Drawable d, 178 GC outgc, 179 GC centergc, 180 int num) 181{ 182 Display *dpy = XtDisplay(w); 183 184 TFillArc (dpy, d, outgc, &w->eyes.t, 185 EYE_X(num) - EYE_HWIDTH - EYE_THICK, 186 EYE_Y(num) - EYE_HHEIGHT - EYE_THICK, 187 EYE_WIDTH + EYE_THICK * 2.0, 188 EYE_HEIGHT + EYE_THICK * 2.0, 189 90 * 64, 360 * 64); 190 if (centergc) { 191 TFillArc (dpy, d, centergc, &w->eyes.t, 192 EYE_X(num) - EYE_HWIDTH, 193 EYE_Y(num) - EYE_HHEIGHT, 194 EYE_WIDTH, EYE_HEIGHT, 195 90 * 64, 360 * 64); 196 } 197} 198 199static TPoint computePupil ( 200 int num, 201 TPoint mouse) 202{ 203 double cx, cy; 204 double dist; 205 double angle; 206 double x, y; 207 double h; 208 double dx, dy; 209 double cosa, sina; 210 TPoint ret; 211 212 dx = mouse.x - EYE_X(num); 213 dy = mouse.y - EYE_Y(num); 214 if (dx == 0 && dy == 0) { 215 cx = EYE_X(num); 216 cy = EYE_Y(num); 217 } else { 218 angle = atan2 ((double) dy, (double) dx); 219 cosa = cos (angle); 220 sina = sin (angle); 221 h = hypot (EYE_HHEIGHT * cosa, EYE_HWIDTH * sina); 222 x = (EYE_HWIDTH * EYE_HHEIGHT) * cosa / h; 223 y = (EYE_HWIDTH * EYE_HHEIGHT) * sina / h; 224 dist = BALL_DIST * hypot (x, y); 225 if (dist > hypot ((double) dx, (double) dy)) { 226 cx = dx + EYE_X(num); 227 cy = dy + EYE_Y(num); 228 } else { 229 cx = dist * cosa + EYE_X(num); 230 cy = dist * sina + EYE_Y(num); 231 } 232 } 233 ret.x = cx; 234 ret.y = cy; 235 return ret; 236} 237 238static void computePupils ( 239 TPoint mouse, 240 TPoint pupils[2]) 241{ 242 pupils[0] = computePupil (0, mouse); 243 pupils[1] = computePupil (1, mouse); 244} 245 246static void eyeBall ( 247 EyesWidget w, 248 GC gc, 249 int num) 250{ 251 Display *dpy = XtDisplay(w); 252 Window win = XtWindow(w); 253 254 TFillArc (dpy, win, gc, &w->eyes.t, 255 w->eyes.pupil[num].x - BALL_WIDTH / 2.0, 256 w->eyes.pupil[num].y - BALL_HEIGHT / 2.0, 257 BALL_WIDTH, BALL_HEIGHT, 258 90 * 64, 360 * 64); 259} 260 261static void repaint_window (EyesWidget w) 262{ 263 if (XtIsRealized ((Widget) w)) { 264 eyeLiner (w, XtWindow (w), w->eyes.outGC, w->eyes.centerGC, 0); 265 eyeLiner (w, XtWindow (w), w->eyes.outGC, w->eyes.centerGC, 1); 266 computePupils (w->eyes.mouse, w->eyes.pupil); 267 eyeBall (w, w->eyes.pupGC, 0); 268 eyeBall (w, w->eyes.pupGC, 1); 269 } 270} 271 272/* ARGSUSED */ 273static void draw_it ( 274 XtPointer client_data, 275 XtIntervalId *id) /* unused */ 276{ 277 EyesWidget w = (EyesWidget)client_data; 278 Window rep_root, rep_child; 279 int rep_rootx, rep_rooty; 280 unsigned int rep_mask; 281 int dx, dy; 282 TPoint mouse; 283 Display *dpy = XtDisplay (w); 284 Window win = XtWindow (w); 285 TPoint newpupil[2]; 286 XPoint xnewpupil, xpupil; 287 288 if (XtIsRealized((Widget)w)) { 289 XQueryPointer (dpy, win, &rep_root, &rep_child, 290 &rep_rootx, &rep_rooty, &dx, &dy, &rep_mask); 291 mouse.x = Tx(dx, dy, &w->eyes.t); 292 mouse.y = Ty(dx, dy, &w->eyes.t); 293 if (!TPointEqual (mouse, w->eyes.mouse)) { 294 computePupils (mouse, newpupil); 295 xpupil.x = Xx(w->eyes.pupil[0].x, w->eyes.pupil[0].y, &w->eyes.t); 296 xpupil.y = Xy(w->eyes.pupil[0].x, w->eyes.pupil[0].y, &w->eyes.t); 297 xnewpupil.x = Xx(newpupil[0].x, newpupil[0].y, &w->eyes.t); 298 xnewpupil.y = Xy(newpupil[0].x, newpupil[0].y, &w->eyes.t); 299 if (!XPointEqual (xpupil, xnewpupil)) { 300 if (w->eyes.pupil[0].x != -1000 || w->eyes.pupil[0].y != -1000) 301 eyeBall (w, w->eyes.centerGC, 0); 302 w->eyes.pupil[0] = newpupil[0]; 303 eyeBall (w, w->eyes.pupGC, 0); 304 } 305 xpupil.x = Xx(w->eyes.pupil[1].x, w->eyes.pupil[1].y, &w->eyes.t); 306 xpupil.y = Xy(w->eyes.pupil[1].x, w->eyes.pupil[1].y, &w->eyes.t); 307 xnewpupil.x = Xx(newpupil[1].x, newpupil[1].y, &w->eyes.t); 308 xnewpupil.y = Xy(newpupil[1].x, newpupil[1].y, &w->eyes.t); 309 if (!XPointEqual (xpupil, xnewpupil)) { 310 if (w->eyes.pupil[1].x != -1 || w->eyes.pupil[1].y != -1) 311 eyeBall (w, w->eyes.centerGC, 1); 312 w->eyes.pupil[1] = newpupil[1]; 313 eyeBall (w, w->eyes.pupGC, 1); 314 } 315 w->eyes.mouse = mouse; 316 w->eyes.update = 0; 317 } else { 318 if (delays[w->eyes.update + 1] != 0) 319 ++w->eyes.update; 320 } 321 } 322 w->eyes.interval_id = 323 XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) w), 324 delays[w->eyes.update], draw_it, (XtPointer)w); 325} /* draw_it */ 326 327static void Resize (Widget gw) 328{ 329 EyesWidget w = (EyesWidget) gw; 330 XGCValues xgcv; 331 Widget parent; 332 int x, y; 333 334 if (XtIsRealized (gw)) 335 { 336 XClearWindow (XtDisplay (w), XtWindow (w)); 337 SetTransform (&w->eyes.t, 338 0, w->core.width, 339 w->core.height, 0, 340 W_MIN_X, W_MAX_X, 341 W_MIN_Y, W_MAX_Y); 342 if (w->eyes.shape_window) { 343 w->eyes.shape_mask = XCreatePixmap (XtDisplay (w), XtWindow (w), 344 w->core.width, w->core.height, 1); 345 if (!w->eyes.shapeGC) 346 w->eyes.shapeGC = XCreateGC (XtDisplay (w), w->eyes.shape_mask, 0, &xgcv); 347 XSetForeground (XtDisplay (w), w->eyes.shapeGC, 0); 348 XFillRectangle (XtDisplay (w), w->eyes.shape_mask, w->eyes.shapeGC, 0, 0, 349 w->core.width, w->core.height); 350 XSetForeground (XtDisplay (w), w->eyes.shapeGC, 1); 351 eyeLiner (w, w->eyes.shape_mask, w->eyes.shapeGC, (GC) 0, 0); 352 eyeLiner (w, w->eyes.shape_mask, w->eyes.shapeGC, (GC) 0, 1); 353 x = y = 0; 354 for (parent = (Widget) w; XtParent (parent); parent = XtParent (parent)) { 355 x += parent->core.x + parent->core.border_width; 356 x += parent->core.y + parent->core.border_width; 357 } 358 XShapeCombineMask (XtDisplay (parent), XtWindow (parent), ShapeBounding, 359 x, y, w->eyes.shape_mask, ShapeSet); 360 XFreePixmap (XtDisplay (w), w->eyes.shape_mask); 361 } 362 } 363} 364 365static void Realize ( 366 Widget gw, 367 XtValueMask *valueMask, 368 XSetWindowAttributes *attrs) 369{ 370 EyesWidget w = (EyesWidget)gw; 371 372 if (w->eyes.backing_store != Always + WhenMapped + NotUseful) { 373 attrs->backing_store = w->eyes.backing_store; 374 *valueMask |= CWBackingStore; 375 } 376 XtCreateWindow( gw, (unsigned)InputOutput, (Visual *)CopyFromParent, 377 *valueMask, attrs ); 378 Resize (gw); 379 w->eyes.interval_id = 380 XtAppAddTimeOut(XtWidgetToApplicationContext(gw), 381 delays[w->eyes.update], draw_it, (XtPointer)gw); 382} 383 384static void Destroy (Widget gw) 385{ 386 EyesWidget w = (EyesWidget)gw; 387 388 if (w->eyes.interval_id) 389 XtRemoveTimeOut (w->eyes.interval_id); 390 XtReleaseGC(gw, w->eyes.pupGC); 391 XtReleaseGC(gw, w->eyes.outGC); 392 XtReleaseGC(gw, w->eyes.centerGC); 393} 394 395/* ARGSUSED */ 396static void Redisplay( 397 Widget gw, 398 XEvent *event, 399 Region region) 400{ 401 EyesWidget w; 402 403 w = (EyesWidget) gw; 404 w->eyes.pupil[0].x = -1000; 405 w->eyes.pupil[0].y = -1000; 406 w->eyes.pupil[1].x = -1000; 407 w->eyes.pupil[1].y = -1000; 408 (void) repaint_window ((EyesWidget)gw); 409} 410 411/* ARGSUSED */ 412static Boolean SetValues ( 413 Widget current, 414 Widget request, 415 Widget new, 416 ArgList args, 417 Cardinal *num_args) 418{ 419 return( FALSE ); 420} 421 422EyesClassRec eyesClassRec = { 423 { /* core fields */ 424 /* superclass */ &widgetClassRec, 425 /* class_name */ "Eyes", 426 /* size */ sizeof(EyesRec), 427 /* class_initialize */ ClassInitialize, 428 /* class_part_initialize */ NULL, 429 /* class_inited */ FALSE, 430 /* initialize */ Initialize, 431 /* initialize_hook */ NULL, 432 /* realize */ Realize, 433 /* actions */ NULL, 434 /* num_actions */ 0, 435 /* resources */ resources, 436 /* num_resources */ XtNumber(resources), 437 /* xrm_class */ NULLQUARK, 438 /* compress_motion */ TRUE, 439 /* compress_exposure */ TRUE, 440 /* compress_enterleave */ TRUE, 441 /* visible_interest */ FALSE, 442 /* destroy */ Destroy, 443 /* resize */ Resize, 444 /* expose */ Redisplay, 445 /* set_values */ SetValues, 446 /* set_values_hook */ NULL, 447 /* set_values_almost */ NULL, 448 /* get_values_hook */ NULL, 449 /* accept_focus */ NULL, 450 /* version */ XtVersion, 451 /* callback_private */ NULL, 452 /* tm_table */ NULL, 453 /* query_geometry */ XtInheritQueryGeometry, 454 } 455}; 456