home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / X / mit / demos / xeyes / Eyes.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-26  |  13.3 KB  |  449 lines

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