Eyes.c revision 26df5c7c
1a1d141d5Smrg/*
2a1d141d5Smrg
3a1d141d5SmrgCopyright (c) 1991  X Consortium
4a1d141d5Smrg
5a1d141d5SmrgPermission is hereby granted, free of charge, to any person obtaining
6a1d141d5Smrga copy of this software and associated documentation files (the
7a1d141d5Smrg"Software"), to deal in the Software without restriction, including
8a1d141d5Smrgwithout limitation the rights to use, copy, modify, merge, publish,
9a1d141d5Smrgdistribute, sublicense, and/or sell copies of the Software, and to
10a1d141d5Smrgpermit persons to whom the Software is furnished to do so, subject to
11a1d141d5Smrgthe following conditions:
12a1d141d5Smrg
13a1d141d5SmrgThe above copyright notice and this permission notice shall be included
14a1d141d5Smrgin all copies or substantial portions of the Software.
15a1d141d5Smrg
16a1d141d5SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17a1d141d5SmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18a1d141d5SmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19a1d141d5SmrgIN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
20a1d141d5SmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21a1d141d5SmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22a1d141d5SmrgOTHER DEALINGS IN THE SOFTWARE.
23a1d141d5Smrg
24a1d141d5SmrgExcept as contained in this notice, the name of the X Consortium shall
25a1d141d5Smrgnot be used in advertising or otherwise to promote the sale, use or
26a1d141d5Smrgother dealings in this Software without prior written authorization
27a1d141d5Smrgfrom the X Consortium.
28a1d141d5Smrg
29a1d141d5Smrg*/
30a1d141d5Smrg
31a1d141d5Smrg/*
32a1d141d5Smrg * Eyes.c
33a1d141d5Smrg *
34a1d141d5Smrg * a widget which follows the mouse around
35a1d141d5Smrg */
36a1d141d5Smrg
372ddb6cf1Smrg#ifdef HAVE_CONFIG_H
382ddb6cf1Smrg# include "config.h"
392ddb6cf1Smrg#endif
402ddb6cf1Smrg
41a1d141d5Smrg# include <X11/Xos.h>
42a1d141d5Smrg# include <stdio.h>
43a1d141d5Smrg# include <X11/IntrinsicP.h>
44a1d141d5Smrg# include <X11/StringDefs.h>
45a1d141d5Smrg# include <X11/Xmu/Converters.h>
46a1d141d5Smrg# include "EyesP.h"
47a1d141d5Smrg# include <math.h>
48a1d141d5Smrg# include <X11/extensions/shape.h>
492ddb6cf1Smrg# include <X11/Xlibint.h>
502ddb6cf1Smrg# include <stdlib.h>
5126df5c7cSmrg# include <X11/extensions/XInput2.h>
52a1d141d5Smrg
53a1d141d5Smrg#define offset(field) XtOffsetOf(EyesRec, eyes.field)
54a1d141d5Smrg#define goffset(field) XtOffsetOf(WidgetRec, core.field)
55a1d141d5Smrg
56a1d141d5Smrgstatic XtResource resources[] = {
5726df5c7cSmrg    {(char *) XtNwidth, (char *) XtCWidth, XtRDimension, sizeof(Dimension),
58a1d141d5Smrg	goffset(width), XtRImmediate, (XtPointer) 150},
5926df5c7cSmrg    {(char *) XtNheight, (char *) XtCHeight, XtRDimension, sizeof(Dimension),
60a1d141d5Smrg	goffset(height), XtRImmediate, (XtPointer) 100},
6126df5c7cSmrg    {(char *) XtNforeground, (char *) XtCForeground, XtRPixel, sizeof(Pixel),
6226df5c7cSmrg        offset(pixel[PART_PUPIL]), XtRString, (char *) XtDefaultForeground},
6326df5c7cSmrg    {(char *) XtNbackgroundPixmap, (char *) XtCPixmap, XtRPixmap, sizeof(Pixmap),
6426df5c7cSmrg     XtOffsetOf(CoreRec,core.background_pixmap),
6526df5c7cSmrg     XtRImmediate, (XtPointer)None},
6626df5c7cSmrg    {(char *) XtNoutline, (char *) XtCForeground, XtRPixel, sizeof(Pixel),
6726df5c7cSmrg        offset(pixel[PART_OUTLINE]), XtRString, (char *) XtDefaultForeground},
6826df5c7cSmrg    {(char *) XtNcenterColor, (char *) XtCBackground, XtRPixel, sizeof (Pixel),
6926df5c7cSmrg	offset(pixel[PART_CENTER]), XtRString, (char *) XtDefaultBackground},
7026df5c7cSmrg    {(char *) XtNreverseVideo, (char *) XtCReverseVideo, XtRBoolean, sizeof (Boolean),
71a1d141d5Smrg	offset (reverse_video), XtRImmediate, (XtPointer) FALSE},
7226df5c7cSmrg    {(char *) XtNbackingStore, (char *) XtCBackingStore, (char *) XtRBackingStore, sizeof (int),
7326df5c7cSmrg    	offset (backing_store), XtRString, (char *) "default"},
7426df5c7cSmrg    {(char *) XtNshapeWindow, (char *) XtCShapeWindow, XtRBoolean, sizeof (Boolean),
75a1d141d5Smrg	offset (shape_window), XtRImmediate, (XtPointer) TRUE},
762ddb6cf1Smrg#ifdef XRENDER
7726df5c7cSmrg    {(char *) XtNrender, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean),
782ddb6cf1Smrg	offset(render), XtRImmediate, (XtPointer) TRUE },
792ddb6cf1Smrg#endif
8026df5c7cSmrg#ifdef PRESENT
8126df5c7cSmrg    {(char *) XtNpresent, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean),
8226df5c7cSmrg     offset(present), XtRImmediate, (XtPointer) TRUE },
8326df5c7cSmrg#endif
8426df5c7cSmrg    {(char *) XtNdistance, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean),
852ddb6cf1Smrg	offset(distance), XtRImmediate, (XtPointer) FALSE },
86a1d141d5Smrg};
87a1d141d5Smrg
88a1d141d5Smrg#undef offset
89a1d141d5Smrg#undef goffset
90a1d141d5Smrg
91a1d141d5Smrg# define EYE_X(n)	((n) * 2.0)
92a1d141d5Smrg# define EYE_Y(n)	(0.0)
93a1d141d5Smrg# define EYE_OFFSET	(0.1)	/* padding between eyes */
94a1d141d5Smrg# define EYE_THICK	(0.175)	/* thickness of eye rim */
952ddb6cf1Smrg# define BALL_DIAM	(0.3)
962ddb6cf1Smrg# define BALL_PAD	(0.175)
972ddb6cf1Smrg# define EYE_DIAM	(2.0 - (EYE_THICK + EYE_OFFSET) * 2)
982ddb6cf1Smrg# define BALL_DIST	((EYE_DIAM - BALL_DIAM) / 2.0 - BALL_PAD)
99a1d141d5Smrg# define W_MIN_X	(-1.0 + EYE_OFFSET)
100a1d141d5Smrg# define W_MAX_X	(3.0 - EYE_OFFSET)
101a1d141d5Smrg# define W_MIN_Y	(-1.0 + EYE_OFFSET)
102a1d141d5Smrg# define W_MAX_Y	(1.0 - EYE_OFFSET)
103a1d141d5Smrg
1042ddb6cf1Smrg# define TPOINT_NONE	(-1000)	/* special value meaning "not yet set" */
105a1d141d5Smrg# define TPointEqual(a, b)  ((a).x == (b).x && (a).y == (b).y)
106a1d141d5Smrg# define XPointEqual(a, b)  ((a).x == (b).x && (a).y == (b).y)
1072ddb6cf1Smrg# define AngleBetween(A, A0, A1) (A0 <= A1 ? A0 <= A && A <= A1 : \
1082ddb6cf1Smrg					     A0 <= A || A <= A1)
109a1d141d5Smrg
110a1d141d5Smrgstatic int delays[] = { 50, 100, 200, 400, 0 };
111a1d141d5Smrg
112a1d141d5Smrgstatic void ClassInitialize(void)
113a1d141d5Smrg{
114a1d141d5Smrg    XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
115a1d141d5Smrg		    NULL, 0 );
116a1d141d5Smrg}
117a1d141d5Smrg
118a1d141d5SmrgWidgetClass eyesWidgetClass = (WidgetClass) &eyesClassRec;
119a1d141d5Smrg
12026df5c7cSmrg#ifdef PRESENT
12126df5c7cSmrgstatic void CheckPresent(EyesWidget w) {
12226df5c7cSmrg    const xcb_query_extension_reply_t 	    *xfixes_ext_reply;
12326df5c7cSmrg    const xcb_query_extension_reply_t 	    *damage_ext_reply;
12426df5c7cSmrg    const xcb_query_extension_reply_t 	    *present_ext_reply;
12526df5c7cSmrg    xcb_xfixes_query_version_cookie_t       xfixes_cookie;
12626df5c7cSmrg    xcb_xfixes_query_version_reply_t        *xfixes_reply;
12726df5c7cSmrg    xcb_damage_query_version_cookie_t       damage_cookie;
12826df5c7cSmrg    xcb_damage_query_version_reply_t        *damage_reply;
12926df5c7cSmrg    xcb_present_query_version_cookie_t      present_cookie;
13026df5c7cSmrg    xcb_present_query_version_reply_t       *present_reply;
13126df5c7cSmrg
13226df5c7cSmrg    if (!w->eyes.present)
13326df5c7cSmrg	return;
13426df5c7cSmrg
13526df5c7cSmrg    xcb_prefetch_extension_data(xt_xcb(w), &xcb_xfixes_id);
13626df5c7cSmrg    xcb_prefetch_extension_data(xt_xcb(w), &xcb_damage_id);
13726df5c7cSmrg    xcb_prefetch_extension_data(xt_xcb(w), &xcb_present_id);
13826df5c7cSmrg
13926df5c7cSmrg    xfixes_ext_reply = xcb_get_extension_data(xt_xcb(w), &xcb_xfixes_id);
14026df5c7cSmrg    damage_ext_reply = xcb_get_extension_data(xt_xcb(w), &xcb_damage_id);
14126df5c7cSmrg    present_ext_reply = xcb_get_extension_data(xt_xcb(w), &xcb_present_id);
14226df5c7cSmrg    if (xfixes_ext_reply == NULL || !xfixes_ext_reply->present
14326df5c7cSmrg	|| damage_ext_reply == NULL || !damage_ext_reply->present
14426df5c7cSmrg	|| present_ext_reply == NULL || !present_ext_reply->present)
14526df5c7cSmrg    {
14626df5c7cSmrg	w->eyes.present = FALSE;
14726df5c7cSmrg    }
14826df5c7cSmrg
14926df5c7cSmrg    if (!w->eyes.present)
15026df5c7cSmrg	return;
15126df5c7cSmrg
15226df5c7cSmrg    /* Now tell the server which versions of the extensions we support */
15326df5c7cSmrg    xfixes_cookie = xcb_xfixes_query_version(xt_xcb(w),
15426df5c7cSmrg					     XCB_XFIXES_MAJOR_VERSION,
15526df5c7cSmrg					     XCB_XFIXES_MINOR_VERSION);
15626df5c7cSmrg
15726df5c7cSmrg    damage_cookie = xcb_damage_query_version(xt_xcb(w),
15826df5c7cSmrg					     XCB_DAMAGE_MAJOR_VERSION,
15926df5c7cSmrg					     XCB_DAMAGE_MINOR_VERSION);
16026df5c7cSmrg
16126df5c7cSmrg    present_cookie = xcb_present_query_version(xt_xcb(w),
16226df5c7cSmrg					       XCB_PRESENT_MAJOR_VERSION,
16326df5c7cSmrg					       XCB_PRESENT_MINOR_VERSION);
16426df5c7cSmrg
16526df5c7cSmrg    xfixes_reply = xcb_xfixes_query_version_reply(xt_xcb(w),
16626df5c7cSmrg						  xfixes_cookie,
16726df5c7cSmrg						  NULL);
16826df5c7cSmrg    free(xfixes_reply);
16926df5c7cSmrg
17026df5c7cSmrg    damage_reply = xcb_damage_query_version_reply(xt_xcb(w),
17126df5c7cSmrg						  damage_cookie,
17226df5c7cSmrg						  NULL);
17326df5c7cSmrg    free(damage_reply);
17426df5c7cSmrg
17526df5c7cSmrg    present_reply = xcb_present_query_version_reply(xt_xcb(w),
17626df5c7cSmrg						    present_cookie,
17726df5c7cSmrg						    NULL);
17826df5c7cSmrg    free(present_reply);
17926df5c7cSmrg}
18026df5c7cSmrg
18126df5c7cSmrgstatic void MakePresentData(EyesWidget w) {
18226df5c7cSmrg
18326df5c7cSmrg    if (!w->eyes.present)
18426df5c7cSmrg        return;
18526df5c7cSmrg
18626df5c7cSmrg    if (!w->eyes.back_buffer) {
18726df5c7cSmrg        xcb_create_pixmap(xt_xcb(w),
18826df5c7cSmrg                          w->core.depth,
18926df5c7cSmrg                          w->eyes.back_buffer = xcb_generate_id(xt_xcb(w)),
19026df5c7cSmrg                          XtWindow(w),
19126df5c7cSmrg                          w->core.width,
19226df5c7cSmrg                          w->core.height);
19326df5c7cSmrg    }
19426df5c7cSmrg    if (!w->eyes.back_damage) {
19526df5c7cSmrg        xcb_damage_create(xt_xcb(w),
19626df5c7cSmrg                          w->eyes.back_damage = xcb_generate_id(xt_xcb(w)),
19726df5c7cSmrg                          w->eyes.back_buffer,
19826df5c7cSmrg                          XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
19926df5c7cSmrg        xcb_xfixes_create_region(xt_xcb(w),
20026df5c7cSmrg                                 w->eyes.back_region = xcb_generate_id(xt_xcb(w)),
20126df5c7cSmrg                                 0, NULL);
20226df5c7cSmrg    }
20326df5c7cSmrg}
20426df5c7cSmrg
20526df5c7cSmrgstatic void UpdatePresent(EyesWidget w) {
20626df5c7cSmrg    if (w->eyes.back_buffer) {
20726df5c7cSmrg        xcb_damage_subtract(xt_xcb(w),
20826df5c7cSmrg                            w->eyes.back_damage,
20926df5c7cSmrg                            None,
21026df5c7cSmrg                            w->eyes.back_region);
21126df5c7cSmrg        xcb_present_pixmap(xt_xcb(w),
21226df5c7cSmrg                           XtWindow(w),
21326df5c7cSmrg                           w->eyes.back_buffer,
21426df5c7cSmrg                           0,
21526df5c7cSmrg                           None,
21626df5c7cSmrg                           w->eyes.back_region,
21726df5c7cSmrg                           0, 0,
21826df5c7cSmrg                           None,
21926df5c7cSmrg			   None,
22026df5c7cSmrg			   None,
22126df5c7cSmrg			   0,
22226df5c7cSmrg			   0, 1, 0,
22326df5c7cSmrg			   0, NULL);
22426df5c7cSmrg    }
22526df5c7cSmrg}
22626df5c7cSmrg
22726df5c7cSmrg#endif
22826df5c7cSmrg
22926df5c7cSmrg#ifdef PRESENT
23026df5c7cSmrg#define EyesDrawable(w) (w->eyes.back_buffer ? w->eyes.back_buffer : XtWindow(w))
23126df5c7cSmrg#else
23226df5c7cSmrg#define EyesDrawable(w) XtWindow(w)
23326df5c7cSmrg#endif
23426df5c7cSmrg
23526df5c7cSmrgstatic void draw_it_core(EyesWidget w);
23626df5c7cSmrg
23726df5c7cSmrgstatic void EyesGeneric(Widget w, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch)
23826df5c7cSmrg{
23926df5c7cSmrg        draw_it_core((EyesWidget) w);
24026df5c7cSmrg}
24126df5c7cSmrg
24226df5c7cSmrgstruct root_listen_list {
24326df5c7cSmrg    struct root_listen_list *next;
24426df5c7cSmrg    Widget      widget;
24526df5c7cSmrg};
24626df5c7cSmrg
24726df5c7cSmrgstatic struct root_listen_list *root_listen_list;
24826df5c7cSmrg
24926df5c7cSmrgstatic Boolean xi2_dispatcher(XEvent *event) {
25026df5c7cSmrg    struct root_listen_list *rll;
25126df5c7cSmrg    Boolean was_dispatched = False;
25226df5c7cSmrg
25326df5c7cSmrg    for (rll = root_listen_list; rll; rll = rll->next) {
25426df5c7cSmrg        if (XtDisplay(rll->widget) == event->xany.display) {
25526df5c7cSmrg            XtDispatchEventToWidget(rll->widget, event);
25626df5c7cSmrg            was_dispatched = True;
25726df5c7cSmrg        }
25826df5c7cSmrg    }
25926df5c7cSmrg    return was_dispatched;
26026df5c7cSmrg}
26126df5c7cSmrg
26226df5c7cSmrgstatic void select_xi2_events(Widget w)
26326df5c7cSmrg{
26426df5c7cSmrg    XIEventMask evmasks[1];
26526df5c7cSmrg    unsigned char mask1[(XI_LASTEVENT + 7)/8];
26626df5c7cSmrg
26726df5c7cSmrg    memset(mask1, 0, sizeof(mask1));
26826df5c7cSmrg
26926df5c7cSmrg    /* select for button and key events from all master devices */
27026df5c7cSmrg    XISetMask(mask1, XI_RawMotion);
27126df5c7cSmrg
27226df5c7cSmrg    evmasks[0].deviceid = XIAllMasterDevices;
27326df5c7cSmrg    evmasks[0].mask_len = sizeof(mask1);
27426df5c7cSmrg    evmasks[0].mask = mask1;
27526df5c7cSmrg
27626df5c7cSmrg    XISelectEvents(XtDisplay(w),
27726df5c7cSmrg                   RootWindowOfScreen(XtScreen(w)),
27826df5c7cSmrg                   evmasks, 1);
27926df5c7cSmrg    XtSetEventDispatcher(XtDisplay(w),
28026df5c7cSmrg                         GenericEvent,
28126df5c7cSmrg                         xi2_dispatcher);
28226df5c7cSmrg}
28326df5c7cSmrg
28426df5c7cSmrgstatic Boolean xi2_add_root_listener(Widget widget)
28526df5c7cSmrg{
28626df5c7cSmrg    struct root_listen_list *rll = malloc (sizeof (struct root_listen_list));
28726df5c7cSmrg
28826df5c7cSmrg    if (!rll)
28926df5c7cSmrg        return False;
29026df5c7cSmrg    rll->widget = widget;
29126df5c7cSmrg    rll->next = root_listen_list;
29226df5c7cSmrg    if (!root_listen_list)
29326df5c7cSmrg            select_xi2_events(widget);
29426df5c7cSmrg    root_listen_list = rll;
29526df5c7cSmrg    XtInsertEventTypeHandler(widget, GenericEvent, NULL, EyesGeneric, NULL, XtListHead);
29626df5c7cSmrg    return True;
29726df5c7cSmrg}
29826df5c7cSmrg
29926df5c7cSmrgstatic void xi2_remove_root_listener(Widget widget)
30026df5c7cSmrg{
30126df5c7cSmrg    struct root_listen_list *rll, **prev;
30226df5c7cSmrg
30326df5c7cSmrg    for (prev = &root_listen_list; (rll = *prev) != NULL; prev = &rll->next) {
30426df5c7cSmrg        if (rll->widget == widget) {
30526df5c7cSmrg            *prev = rll->next;
30626df5c7cSmrg            free(rll);
30726df5c7cSmrg            break;
30826df5c7cSmrg        }
30926df5c7cSmrg    }
31026df5c7cSmrg}
31126df5c7cSmrg
31226df5c7cSmrg/* Return 1 if XI2 is available, 0 otherwise */
31326df5c7cSmrgstatic int has_xi2(Display *dpy)
31426df5c7cSmrg{
31526df5c7cSmrg    int major, minor;
31626df5c7cSmrg    int rc;
31726df5c7cSmrg
31826df5c7cSmrg    /* We need at least XI 2.0 */
31926df5c7cSmrg    major = 2;
32026df5c7cSmrg    minor = 0;
32126df5c7cSmrg
32226df5c7cSmrg    rc = XIQueryVersion(dpy, &major, &minor);
32326df5c7cSmrg    if (rc == BadRequest) {
32426df5c7cSmrg	return 0;
32526df5c7cSmrg    } else if (rc != Success) {
32626df5c7cSmrg        return 0;
32726df5c7cSmrg    }
32826df5c7cSmrg    return 1;
32926df5c7cSmrg}
33026df5c7cSmrg
33126df5c7cSmrg
332a1d141d5Smrg/* ARGSUSED */
333a1d141d5Smrgstatic void Initialize (
334a1d141d5Smrg    Widget greq,
335a1d141d5Smrg    Widget gnew,
336a1d141d5Smrg    ArgList args,
337a1d141d5Smrg    Cardinal *num_args)
338a1d141d5Smrg{
339a1d141d5Smrg    EyesWidget w = (EyesWidget)gnew;
340a1d141d5Smrg    XtGCMask	valuemask;
341a1d141d5Smrg    XGCValues	myXGCV;
342a1d141d5Smrg    int shape_event_base, shape_error_base;
3432ddb6cf1Smrg#ifdef XRENDER
3442ddb6cf1Smrg    enum EyesPart i;
3452ddb6cf1Smrg#endif
346a1d141d5Smrg
347a1d141d5Smrg    /*
348a1d141d5Smrg     * set the colors if reverse video; these are the colors used:
349a1d141d5Smrg     *
350a1d141d5Smrg     *     background - paper		white
351a1d141d5Smrg     *     foreground - text, ticks	black
352a1d141d5Smrg     *     border - border		black (foreground)
353a1d141d5Smrg     *
3542ddb6cf1Smrg     * This doesn't completely work since the parent has already made up a
355a1d141d5Smrg     * border.  Sigh.
356a1d141d5Smrg     */
357a1d141d5Smrg    if (w->eyes.reverse_video) {
3582ddb6cf1Smrg	Pixel fg = w->eyes.pixel[PART_PUPIL];
359a1d141d5Smrg	Pixel bg = w->core.background_pixel;
360a1d141d5Smrg
361a1d141d5Smrg	if (w->core.border_pixel == fg)
362a1d141d5Smrg 	    w->core.border_pixel = bg;
3632ddb6cf1Smrg	if (w->eyes.pixel[PART_OUTLINE] == fg)
3642ddb6cf1Smrg	    w->eyes.pixel[PART_OUTLINE] = bg;
3652ddb6cf1Smrg	if (w->eyes.pixel[PART_CENTER] == bg)
3662ddb6cf1Smrg	    w->eyes.pixel[PART_CENTER] = fg;
3672ddb6cf1Smrg	w->eyes.pixel[PART_PUPIL] = bg;
368a1d141d5Smrg	w->core.background_pixel = fg;
369a1d141d5Smrg    }
370a1d141d5Smrg
3712ddb6cf1Smrg    myXGCV.foreground = w->eyes.pixel[PART_PUPIL];
372a1d141d5Smrg    myXGCV.background = w->core.background_pixel;
373a1d141d5Smrg    valuemask = GCForeground | GCBackground;
3742ddb6cf1Smrg    w->eyes.gc[PART_PUPIL] = XtGetGC(gnew, valuemask, &myXGCV);
375a1d141d5Smrg
3762ddb6cf1Smrg    myXGCV.foreground = w->eyes.pixel[PART_OUTLINE];
377a1d141d5Smrg    valuemask = GCForeground | GCBackground;
3782ddb6cf1Smrg    w->eyes.gc[PART_OUTLINE] = XtGetGC(gnew, valuemask, &myXGCV);
379a1d141d5Smrg
3802ddb6cf1Smrg    myXGCV.foreground = w->eyes.pixel[PART_CENTER];
3812ddb6cf1Smrg    myXGCV.background = w->eyes.pixel[PART_PUPIL];
382a1d141d5Smrg    valuemask = GCForeground | GCBackground;
3832ddb6cf1Smrg    w->eyes.gc[PART_CENTER] = XtGetGC(gnew, valuemask, &myXGCV);
384a1d141d5Smrg
385a1d141d5Smrg    w->eyes.update = 0;
386a1d141d5Smrg    /* wait for Realize to add the timeout */
387a1d141d5Smrg    w->eyes.interval_id = 0;
388a1d141d5Smrg
3892ddb6cf1Smrg    w->eyes.pupil[0].x = w->eyes.pupil[1].x = TPOINT_NONE;
3902ddb6cf1Smrg    w->eyes.pupil[0].y = w->eyes.pupil[1].y = TPOINT_NONE;
391a1d141d5Smrg
3922ddb6cf1Smrg    w->eyes.mouse.x = w->eyes.mouse.y = TPOINT_NONE;
393a1d141d5Smrg
394a1d141d5Smrg    if (w->eyes.shape_window && !XShapeQueryExtension (XtDisplay (w),
395a1d141d5Smrg						       &shape_event_base,
396a1d141d5Smrg						       &shape_error_base))
397a1d141d5Smrg	w->eyes.shape_window = False;
398a1d141d5Smrg    w->eyes.shape_mask = 0;
3992ddb6cf1Smrg    w->eyes.gc[PART_SHAPE] = NULL;
4002ddb6cf1Smrg
40126df5c7cSmrg    w->eyes.has_xi2 = has_xi2(XtDisplay(w));
40226df5c7cSmrg
4032ddb6cf1Smrg#ifdef XRENDER
4042ddb6cf1Smrg    for (i = 0; i < PART_SHAPE; i ++) {
4052ddb6cf1Smrg	XColor c;
4062ddb6cf1Smrg	XRenderColor rc;
4072ddb6cf1Smrg
4082ddb6cf1Smrg	c.pixel = w->eyes.pixel[i];
4092ddb6cf1Smrg	XQueryColor(XtDisplay (w), w->core.colormap, &c);
4102ddb6cf1Smrg
4112ddb6cf1Smrg	rc.red = c.red;
4122ddb6cf1Smrg	rc.green = c.green;
4132ddb6cf1Smrg	rc.blue = c.blue;
4142ddb6cf1Smrg	rc.alpha = -1;
4152ddb6cf1Smrg	w->eyes.fill[i] = XRenderCreateSolidFill(XtDisplay (w), &rc);
4162ddb6cf1Smrg    }
4172ddb6cf1Smrg#endif
41826df5c7cSmrg#ifdef PRESENT
41926df5c7cSmrg    w->eyes.back_buffer = None;
42026df5c7cSmrg    w->eyes.back_damage = None;
42126df5c7cSmrg    CheckPresent(w);
42226df5c7cSmrg#endif
423a1d141d5Smrg}
424a1d141d5Smrg
4252ddb6cf1Smrgstatic void
4262ddb6cf1SmrgdrawEllipse(EyesWidget w, enum EyesPart part,
4272ddb6cf1Smrg	    double centerx, double centery,
4282ddb6cf1Smrg	    double oldx, double oldy,
4292ddb6cf1Smrg	    double diam)
430a1d141d5Smrg{
4312ddb6cf1Smrg    const TRectangle tpos = {
4322ddb6cf1Smrg	centerx - diam/2.0,
4332ddb6cf1Smrg	centery - diam/2.0,
4342ddb6cf1Smrg	diam, diam };
4352ddb6cf1Smrg    TRectangle pos;
4362ddb6cf1Smrg    Trectangle(&w->eyes.t, &tpos, &pos);
4372ddb6cf1Smrg
4382ddb6cf1Smrg    if (part == PART_CLEAR) {
43926df5c7cSmrg	XFillRectangle(XtDisplay(w), EyesDrawable(w),
4402ddb6cf1Smrg		       w->eyes.gc[PART_CENTER],
4412ddb6cf1Smrg		       (int)pos.x, (int)pos.y,
4422ddb6cf1Smrg		       (int)pos.width+2, (int)pos.height+2);
4432ddb6cf1Smrg	return;
4442ddb6cf1Smrg    }
4452ddb6cf1Smrg#ifdef XRENDER
4462ddb6cf1Smrg    if (w->eyes.render && part != PART_SHAPE && (!w->eyes.shape_window ||
4472ddb6cf1Smrg						 part != PART_OUTLINE) &&
4482ddb6cf1Smrg	w->eyes.picture) {
4492ddb6cf1Smrg	int n, i;
4502ddb6cf1Smrg	double hd, c, s, sx, sy, x, y, px, py;
4512ddb6cf1Smrg	XPointDouble *p;
4522ddb6cf1Smrg
4532ddb6cf1Smrg	pos.x = pos.x + pos.width/2.0;
4542ddb6cf1Smrg	pos.y = pos.y + pos.height/2.0;
4552ddb6cf1Smrg
4562ddb6cf1Smrg	/* determine number of segments to draw */
4572ddb6cf1Smrg	hd = hypot(pos.width, pos.height)/2;
4582ddb6cf1Smrg	n = (M_PI / acos(hd/(hd+1.0))) + 0.5;
4592ddb6cf1Smrg	if (n < 2) n = 2;
4602ddb6cf1Smrg
4612ddb6cf1Smrg	c = cos(M_PI/n);
4622ddb6cf1Smrg	s = sin(M_PI/n);
4632ddb6cf1Smrg	sx = -(pos.width*s)/pos.height;
4642ddb6cf1Smrg	sy = (pos.height*s)/pos.width;
4652ddb6cf1Smrg
4662ddb6cf1Smrg	n *= 2;
4672ddb6cf1Smrg	p = Xmalloc(sizeof(*p)*n);
4682ddb6cf1Smrg	if (!p)
4692ddb6cf1Smrg	    return;
4702ddb6cf1Smrg	x = 0;
4712ddb6cf1Smrg	y = pos.height/2.0;
4722ddb6cf1Smrg	for (i = 0; i < n; i ++)
4732ddb6cf1Smrg	{
4742ddb6cf1Smrg	    p[i].x = x + pos.x;
4752ddb6cf1Smrg	    p[i].y = y + pos.y;
4762ddb6cf1Smrg	    px = x;
4772ddb6cf1Smrg	    py = y;
4782ddb6cf1Smrg	    x = c*px + sx*py;
4792ddb6cf1Smrg	    y = c*py + sy*px;
480a1d141d5Smrg	}
4812ddb6cf1Smrg
4822ddb6cf1Smrg	if (oldx != TPOINT_NONE || oldy != TPOINT_NONE)
4832ddb6cf1Smrg	    drawEllipse(w, PART_CLEAR, oldx, oldy,
4842ddb6cf1Smrg			TPOINT_NONE, TPOINT_NONE, diam);
4852ddb6cf1Smrg
4862ddb6cf1Smrg	XRenderCompositeDoublePoly(XtDisplay(w), PictOpOver,
4872ddb6cf1Smrg				   w->eyes.fill[part], w->eyes.picture,
4882ddb6cf1Smrg				   XRenderFindStandardFormat(XtDisplay(w),
4892ddb6cf1Smrg							     PictStandardA8),
4902ddb6cf1Smrg				   0, 0, 0, 0, p, n, 0);
4912ddb6cf1Smrg
4922ddb6cf1Smrg	Xfree(p);
4932ddb6cf1Smrg	return;
4942ddb6cf1Smrg    }
4952ddb6cf1Smrg#endif
4962ddb6cf1Smrg    if (oldx != TPOINT_NONE || oldy != TPOINT_NONE)
4972ddb6cf1Smrg	drawEllipse(w, PART_CLEAR, oldx, oldy,
4982ddb6cf1Smrg		    TPOINT_NONE, TPOINT_NONE, diam);
4992ddb6cf1Smrg
5002ddb6cf1Smrg    XFillArc(XtDisplay(w),
50126df5c7cSmrg	     part == PART_SHAPE ? w->eyes.shape_mask : EyesDrawable(w),
5022ddb6cf1Smrg	     w->eyes.gc[part],
5032ddb6cf1Smrg	     (int)(pos.x + 0.5), (int)(pos.y + 0.5),
5042ddb6cf1Smrg	     (int)(pos.width + 0.0), (int)(pos.height + 0.0),
5052ddb6cf1Smrg	     90*64, 360*64);
5062ddb6cf1Smrg}
5072ddb6cf1Smrg
5082ddb6cf1Smrg
5092ddb6cf1Smrgstatic void
5102ddb6cf1SmrgeyeLiner(EyesWidget	w,
5112ddb6cf1Smrg	 Boolean	draw,
5122ddb6cf1Smrg	 int		num)
5132ddb6cf1Smrg{
5142ddb6cf1Smrg    drawEllipse(w, draw ? PART_OUTLINE : PART_SHAPE,
5152ddb6cf1Smrg		EYE_X(num), EYE_Y(num),
5162ddb6cf1Smrg		TPOINT_NONE, TPOINT_NONE,
5172ddb6cf1Smrg		EYE_DIAM + 2.0*EYE_THICK);
5182ddb6cf1Smrg    if (draw) {
5192ddb6cf1Smrg	drawEllipse(w, PART_CENTER, EYE_X(num), EYE_Y(num),
5202ddb6cf1Smrg		    TPOINT_NONE, TPOINT_NONE,
5212ddb6cf1Smrg		    EYE_DIAM);
5222ddb6cf1Smrg    }
523a1d141d5Smrg}
524a1d141d5Smrg
525a1d141d5Smrgstatic TPoint computePupil (
526a1d141d5Smrg    int		num,
5272ddb6cf1Smrg    TPoint	mouse,
5282ddb6cf1Smrg    const TRectangle *screen)
529a1d141d5Smrg{
530a1d141d5Smrg	double	cx, cy;
531a1d141d5Smrg	double	dist;
532a1d141d5Smrg	double	angle;
533a1d141d5Smrg	double	dx, dy;
534a1d141d5Smrg	double	cosa, sina;
535a1d141d5Smrg	TPoint	ret;
536a1d141d5Smrg
5372ddb6cf1Smrg	cx = EYE_X(num); dx = mouse.x - cx;
5382ddb6cf1Smrg	cy = EYE_Y(num); dy = mouse.y - cy;
5392ddb6cf1Smrg	if (dx == 0 && dy == 0);
5402ddb6cf1Smrg	else {
541a1d141d5Smrg		angle = atan2 ((double) dy, (double) dx);
542a1d141d5Smrg		cosa = cos (angle);
543a1d141d5Smrg		sina = sin (angle);
5442ddb6cf1Smrg		dist = BALL_DIST;
5452ddb6cf1Smrg		if (screen)
5462ddb6cf1Smrg		{
5472ddb6cf1Smrg		    /* use distance mapping */
5482ddb6cf1Smrg		    double x0, y0, x1, y1;
5492ddb6cf1Smrg		    double a[4];
5502ddb6cf1Smrg		    x0 = screen->x - cx;
5512ddb6cf1Smrg		    y0 = screen->y - cy;
5522ddb6cf1Smrg		    x1 = x0 + screen->width;
5532ddb6cf1Smrg		    y1 = y0 + screen->height;
5542ddb6cf1Smrg		    a[0] = atan2(y0, x0);
5552ddb6cf1Smrg		    a[1] = atan2(y1, x0);
5562ddb6cf1Smrg		    a[2] = atan2(y1, x1);
5572ddb6cf1Smrg		    a[3] = atan2(y0, x1);
5582ddb6cf1Smrg		    if (AngleBetween(angle, a[0], a[1]))
5592ddb6cf1Smrg		    {
5602ddb6cf1Smrg			/* left */
5612ddb6cf1Smrg			dist *= dx / x0;
5622ddb6cf1Smrg		    }
5632ddb6cf1Smrg		    else if (AngleBetween(angle, a[1], a[2]))
5642ddb6cf1Smrg		    {
5652ddb6cf1Smrg			/* bottom */
5662ddb6cf1Smrg			dist *= dy / y1;
5672ddb6cf1Smrg		    }
5682ddb6cf1Smrg		    else if (AngleBetween(angle, a[2], a[3]))
5692ddb6cf1Smrg		    {
5702ddb6cf1Smrg			/* right */
5712ddb6cf1Smrg			dist *= dx / x1;
5722ddb6cf1Smrg		    }
5732ddb6cf1Smrg		    else if (AngleBetween(angle, a[3], a[0]))
5742ddb6cf1Smrg		    {
5752ddb6cf1Smrg			/* top */
5762ddb6cf1Smrg			dist *= dy / y0;
5772ddb6cf1Smrg		    }
5782ddb6cf1Smrg		    if (dist > BALL_DIST)
5792ddb6cf1Smrg			dist = BALL_DIST;
5802ddb6cf1Smrg		}
581a1d141d5Smrg		if (dist > hypot ((double) dx, (double) dy)) {
5822ddb6cf1Smrg			cx += dx;
5832ddb6cf1Smrg			cy += dy;
584a1d141d5Smrg		} else {
5852ddb6cf1Smrg			cx += dist * cosa;
5862ddb6cf1Smrg			cy += dist * sina;
587a1d141d5Smrg		}
588a1d141d5Smrg	}
589a1d141d5Smrg	ret.x = cx;
590a1d141d5Smrg	ret.y = cy;
591a1d141d5Smrg	return ret;
592a1d141d5Smrg}
593a1d141d5Smrg
594a1d141d5Smrgstatic void computePupils (
5952ddb6cf1Smrg    EyesWidget	w,
596a1d141d5Smrg    TPoint	mouse,
597a1d141d5Smrg    TPoint	pupils[2])
598a1d141d5Smrg{
5992ddb6cf1Smrg    TRectangle screen, *sp = NULL;
6002ddb6cf1Smrg    if (w->eyes.distance) {
6012ddb6cf1Smrg	Window r, cw;
6022ddb6cf1Smrg	int x, y;
6032ddb6cf1Smrg	r = RootWindowOfScreen(w->core.screen);
6042ddb6cf1Smrg	XTranslateCoordinates(XtDisplay(w), XtWindow(w), r, 0, 0, &x, &y, &cw);
6052ddb6cf1Smrg	screen.x = Tx(-x, -y, &w->eyes.t);
6062ddb6cf1Smrg	screen.y = Ty(-x, -y, &w->eyes.t);
6072ddb6cf1Smrg	screen.width  = Twidth (w->core.screen->width, w->core.screen->height,
6082ddb6cf1Smrg				&w->eyes.t);
6092ddb6cf1Smrg	screen.height = Theight(w->core.screen->width, w->core.screen->height,
6102ddb6cf1Smrg				&w->eyes.t);
6112ddb6cf1Smrg	sp = &screen;
6122ddb6cf1Smrg    }
6132ddb6cf1Smrg    pupils[0] = computePupil (0, mouse, sp);
6142ddb6cf1Smrg    pupils[1] = computePupil (1, mouse, sp);
615a1d141d5Smrg}
616a1d141d5Smrg
6172ddb6cf1Smrgstatic void
6182ddb6cf1SmrgeyeBall(EyesWidget	w,
6192ddb6cf1Smrg	Boolean draw,
6202ddb6cf1Smrg	TPoint	*old,
6212ddb6cf1Smrg	int	num)
622a1d141d5Smrg{
6232ddb6cf1Smrg    drawEllipse(w, draw ? PART_PUPIL : PART_CLEAR,
6242ddb6cf1Smrg		w->eyes.pupil[num].x, w->eyes.pupil[num].y,
6252ddb6cf1Smrg		old ? old->x : TPOINT_NONE, old ? old->y : TPOINT_NONE,
6262ddb6cf1Smrg		BALL_DIAM);
627a1d141d5Smrg}
628a1d141d5Smrg
629a1d141d5Smrgstatic void repaint_window (EyesWidget w)
630a1d141d5Smrg{
631a1d141d5Smrg	if (XtIsRealized ((Widget) w)) {
63226df5c7cSmrg#ifdef PRESENT
63326df5c7cSmrg                MakePresentData(w);
63426df5c7cSmrg#endif
6352ddb6cf1Smrg		eyeLiner (w, TRUE, 0);
6362ddb6cf1Smrg		eyeLiner (w, TRUE, 1);
6372ddb6cf1Smrg		computePupils (w, w->eyes.mouse, w->eyes.pupil);
6382ddb6cf1Smrg		eyeBall (w, TRUE, NULL, 0);
6392ddb6cf1Smrg		eyeBall (w, TRUE, NULL, 1);
64026df5c7cSmrg#ifdef PRESENT
64126df5c7cSmrg                UpdatePresent(w);
64226df5c7cSmrg#endif
643a1d141d5Smrg	}
644a1d141d5Smrg}
645a1d141d5Smrg
6462ddb6cf1Smrgstatic void
6472ddb6cf1SmrgdrawEye(EyesWidget w, TPoint newpupil, int num)
6482ddb6cf1Smrg{
6492ddb6cf1Smrg    XPoint		xnewpupil, xpupil;
6502ddb6cf1Smrg
6512ddb6cf1Smrg    xpupil.x = Xx(w->eyes.pupil[num].x, w->eyes.pupil[num].y, &w->eyes.t);
6522ddb6cf1Smrg    xpupil.y = Xy(w->eyes.pupil[num].x, w->eyes.pupil[num].y, &w->eyes.t);
6532ddb6cf1Smrg    xnewpupil.x = Xx(newpupil.x, newpupil.y, &w->eyes.t);
6542ddb6cf1Smrg    xnewpupil.y = Xy(newpupil.x, newpupil.y, &w->eyes.t);
6552ddb6cf1Smrg    if (
6562ddb6cf1Smrg#ifdef XRENDER
6572ddb6cf1Smrg	w->eyes.picture ? !TPointEqual(w->eyes.pupil[num], newpupil) :
6582ddb6cf1Smrg#endif
6592ddb6cf1Smrg	!XPointEqual(xpupil, xnewpupil)) {
6602ddb6cf1Smrg	TPoint oldpupil = w->eyes.pupil[num];
6612ddb6cf1Smrg	w->eyes.pupil[num] = newpupil;
6622ddb6cf1Smrg	eyeBall (w, TRUE, &oldpupil, num);
6632ddb6cf1Smrg    }
6642ddb6cf1Smrg}
6652ddb6cf1Smrg
6662ddb6cf1Smrgstatic void
6672ddb6cf1SmrgdrawEyes(EyesWidget w, TPoint mouse)
6682ddb6cf1Smrg{
6692ddb6cf1Smrg    TPoint		newpupil[2];
6702ddb6cf1Smrg    int			num;
6712ddb6cf1Smrg
67226df5c7cSmrg#ifdef PRESENT
67326df5c7cSmrg    MakePresentData(w);
67426df5c7cSmrg#endif
6752ddb6cf1Smrg    if (TPointEqual (mouse, w->eyes.mouse)) {
6762ddb6cf1Smrg	if (delays[w->eyes.update + 1] != 0)
6772ddb6cf1Smrg	    ++w->eyes.update;
6782ddb6cf1Smrg	return;
6792ddb6cf1Smrg    }
6802ddb6cf1Smrg    computePupils (w, mouse, newpupil);
6812ddb6cf1Smrg    for (num = 0; num < 2; num ++) {
6822ddb6cf1Smrg	drawEye(w, newpupil[num], num);
6832ddb6cf1Smrg    }
6842ddb6cf1Smrg
6852ddb6cf1Smrg    w->eyes.mouse = mouse;
6862ddb6cf1Smrg    w->eyes.update = 0;
68726df5c7cSmrg#ifdef PRESENT
68826df5c7cSmrg    UpdatePresent(w);
68926df5c7cSmrg#endif
6902ddb6cf1Smrg}
6912ddb6cf1Smrg
6922ddb6cf1Smrgstatic void draw_it_core(EyesWidget w)
6932ddb6cf1Smrg{
6942ddb6cf1Smrg    Window		rep_root, rep_child;
6952ddb6cf1Smrg    int			rep_rootx, rep_rooty;
6962ddb6cf1Smrg    unsigned int	rep_mask;
6972ddb6cf1Smrg    int			dx, dy;
6982ddb6cf1Smrg    TPoint		mouse;
6992ddb6cf1Smrg    Display		*dpy = XtDisplay (w);
7002ddb6cf1Smrg    Window		win = XtWindow (w);
7012ddb6cf1Smrg
7022ddb6cf1Smrg    XQueryPointer (dpy, win, &rep_root, &rep_child,
7032ddb6cf1Smrg	    &rep_rootx, &rep_rooty, &dx, &dy, &rep_mask);
7042ddb6cf1Smrg    mouse.x = Tx(dx, dy, &w->eyes.t);
7052ddb6cf1Smrg    mouse.y = Ty(dx, dy, &w->eyes.t);
7062ddb6cf1Smrg
7072ddb6cf1Smrg    drawEyes(w, mouse);
7082ddb6cf1Smrg}
7092ddb6cf1Smrg
710a1d141d5Smrg/* ARGSUSED */
711a1d141d5Smrgstatic void draw_it (
712a1d141d5Smrg     XtPointer client_data,
713a1d141d5Smrg     XtIntervalId *id)		/* unused */
714a1d141d5Smrg{
715a1d141d5Smrg        EyesWidget	w = (EyesWidget)client_data;
716a1d141d5Smrg
717a1d141d5Smrg	if (XtIsRealized((Widget)w)) {
7182ddb6cf1Smrg	        draw_it_core(w);
719a1d141d5Smrg	}
72026df5c7cSmrg        if (!w->eyes.has_xi2) {
72126df5c7cSmrg                w->eyes.interval_id =
72226df5c7cSmrg                        XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) w),
72326df5c7cSmrg                                        delays[w->eyes.update], draw_it, (XtPointer)w);
72426df5c7cSmrg        }
725a1d141d5Smrg} /* draw_it */
726a1d141d5Smrg
727a1d141d5Smrgstatic void Resize (Widget gw)
728a1d141d5Smrg{
729a1d141d5Smrg    EyesWidget	w = (EyesWidget) gw;
730a1d141d5Smrg    XGCValues	xgcv;
731a1d141d5Smrg    Widget	parent;
7322ddb6cf1Smrg    Display	*dpy = XtDisplay (w);
733a1d141d5Smrg    int		x, y;
734a1d141d5Smrg
735a1d141d5Smrg    if (XtIsRealized (gw))
736a1d141d5Smrg    {
737a1d141d5Smrg    	SetTransform (&w->eyes.t,
738a1d141d5Smrg		    	0, w->core.width,
739a1d141d5Smrg 		    	w->core.height, 0,
740a1d141d5Smrg		    	W_MIN_X, W_MAX_X,
741a1d141d5Smrg		    	W_MIN_Y, W_MAX_Y);
74226df5c7cSmrg#ifdef PRESENT
74326df5c7cSmrg        if (w->eyes.back_buffer) {
74426df5c7cSmrg                xcb_free_pixmap(xt_xcb(w),
74526df5c7cSmrg                                w->eyes.back_buffer);
74626df5c7cSmrg                w->eyes.back_buffer = None;
74726df5c7cSmrg                xcb_damage_destroy(xt_xcb(w),
74826df5c7cSmrg                                   w->eyes.back_damage);
74926df5c7cSmrg                w->eyes.back_damage = None;
75026df5c7cSmrg        }
75126df5c7cSmrg        MakePresentData(w);
75226df5c7cSmrg#endif
75326df5c7cSmrg        if (EyesDrawable(w) == XtWindow(w))
75426df5c7cSmrg                XClearWindow (dpy, XtWindow (w));
75526df5c7cSmrg
7562ddb6cf1Smrg#ifdef XRENDER
7572ddb6cf1Smrg	if (w->eyes.picture) {
7582ddb6cf1Smrg	    XRenderFreePicture(dpy, w->eyes.picture);
7592ddb6cf1Smrg	    w->eyes.picture = 0;
7602ddb6cf1Smrg	}
7612ddb6cf1Smrg#endif
762a1d141d5Smrg    	if (w->eyes.shape_window) {
7632ddb6cf1Smrg	    w->eyes.shape_mask = XCreatePixmap (dpy, XtWindow (w),
764a1d141d5Smrg	    	    w->core.width, w->core.height, 1);
7652ddb6cf1Smrg	    if (!w->eyes.gc[PART_SHAPE])
7662ddb6cf1Smrg		w->eyes.gc[PART_SHAPE] = XCreateGC (dpy, w->eyes.shape_mask,
7672ddb6cf1Smrg						    0, &xgcv);
7682ddb6cf1Smrg	    XSetForeground (dpy, w->eyes.gc[PART_SHAPE], 0);
7692ddb6cf1Smrg	    XFillRectangle (dpy, w->eyes.shape_mask, w->eyes.gc[PART_SHAPE],
7702ddb6cf1Smrg			    0, 0, w->core.width, w->core.height);
7712ddb6cf1Smrg	    XSetForeground (dpy, w->eyes.gc[PART_SHAPE], 1);
7722ddb6cf1Smrg	    eyeLiner (w, FALSE, 0);
7732ddb6cf1Smrg	    eyeLiner (w, FALSE, 1);
774a1d141d5Smrg	    x = y = 0;
775a1d141d5Smrg	    for (parent = (Widget) w; XtParent (parent); parent = XtParent (parent)) {
776a1d141d5Smrg	    	x += parent->core.x + parent->core.border_width;
777a1d141d5Smrg	    	x += parent->core.y + parent->core.border_width;
778a1d141d5Smrg	    }
779a1d141d5Smrg    	    XShapeCombineMask (XtDisplay (parent), XtWindow (parent), ShapeBounding,
780a1d141d5Smrg		       	       x, y, w->eyes.shape_mask, ShapeSet);
7812ddb6cf1Smrg	    XFreePixmap (dpy, w->eyes.shape_mask);
782a1d141d5Smrg    	}
7832ddb6cf1Smrg#ifdef XRENDER
7842ddb6cf1Smrg	if (w->eyes.render) {
7852ddb6cf1Smrg	    XRenderPictureAttributes pa;
7862ddb6cf1Smrg	    XRenderPictFormat *pf;
7872ddb6cf1Smrg	    pf = XRenderFindVisualFormat(dpy,
7882ddb6cf1Smrg					 DefaultVisualOfScreen(w->core.screen));
7892ddb6cf1Smrg	    if (pf)
79026df5c7cSmrg		w->eyes.picture = XRenderCreatePicture(dpy, EyesDrawable (w),
7912ddb6cf1Smrg						       pf, 0, &pa);
7922ddb6cf1Smrg	}
7932ddb6cf1Smrg#endif
794a1d141d5Smrg    }
795a1d141d5Smrg}
796a1d141d5Smrg
797a1d141d5Smrgstatic void Realize (
798a1d141d5Smrg     Widget gw,
799a1d141d5Smrg     XtValueMask *valueMask,
800a1d141d5Smrg     XSetWindowAttributes *attrs)
801a1d141d5Smrg{
802a1d141d5Smrg    EyesWidget	w = (EyesWidget)gw;
803a1d141d5Smrg
804a1d141d5Smrg    if (w->eyes.backing_store != Always + WhenMapped + NotUseful) {
805a1d141d5Smrg     	attrs->backing_store = w->eyes.backing_store;
806a1d141d5Smrg	*valueMask |= CWBackingStore;
807a1d141d5Smrg    }
808a1d141d5Smrg    XtCreateWindow( gw, (unsigned)InputOutput, (Visual *)CopyFromParent,
809a1d141d5Smrg		     *valueMask, attrs );
810a1d141d5Smrg    Resize (gw);
81126df5c7cSmrg
81226df5c7cSmrg    if (w->eyes.has_xi2)
81326df5c7cSmrg            xi2_add_root_listener(gw);
81426df5c7cSmrg    else
81526df5c7cSmrg            w->eyes.interval_id =
81626df5c7cSmrg                    XtAppAddTimeOut(XtWidgetToApplicationContext(gw),
81726df5c7cSmrg                                    delays[w->eyes.update], draw_it, (XtPointer)gw);
818a1d141d5Smrg}
819a1d141d5Smrg
820a1d141d5Smrgstatic void Destroy (Widget gw)
821a1d141d5Smrg{
822a1d141d5Smrg     EyesWidget w = (EyesWidget)gw;
8232ddb6cf1Smrg     int i;
824a1d141d5Smrg
825a1d141d5Smrg     if (w->eyes.interval_id)
826a1d141d5Smrg	XtRemoveTimeOut (w->eyes.interval_id);
8272ddb6cf1Smrg     for (i = 0; i < PART_MAX; i ++)
8282ddb6cf1Smrg	     XtReleaseGC(gw, w->eyes.gc[i]);
82926df5c7cSmrg     xi2_remove_root_listener(gw);
8302ddb6cf1Smrg#ifdef XRENDER
8312ddb6cf1Smrg     if (w->eyes.picture)
8322ddb6cf1Smrg	     XRenderFreePicture (XtDisplay(w), w->eyes.picture);
8332ddb6cf1Smrg#endif
834a1d141d5Smrg}
835a1d141d5Smrg
836a1d141d5Smrg/* ARGSUSED */
837a1d141d5Smrgstatic void Redisplay(
838a1d141d5Smrg     Widget gw,
839a1d141d5Smrg     XEvent *event,
840a1d141d5Smrg     Region region)
841a1d141d5Smrg{
842a1d141d5Smrg    EyesWidget	w;
843a1d141d5Smrg
844a1d141d5Smrg    w = (EyesWidget) gw;
8452ddb6cf1Smrg    w->eyes.pupil[0].x = TPOINT_NONE;
8462ddb6cf1Smrg    w->eyes.pupil[0].y = TPOINT_NONE;
8472ddb6cf1Smrg    w->eyes.pupil[1].x = TPOINT_NONE;
8482ddb6cf1Smrg    w->eyes.pupil[1].y = TPOINT_NONE;
849a1d141d5Smrg    (void) repaint_window ((EyesWidget)gw);
850a1d141d5Smrg}
851a1d141d5Smrg
852a1d141d5Smrg/* ARGSUSED */
853a1d141d5Smrgstatic Boolean SetValues (
854a1d141d5Smrg    Widget current,
855a1d141d5Smrg    Widget request,
856a1d141d5Smrg    Widget new,
857a1d141d5Smrg    ArgList args,
858a1d141d5Smrg    Cardinal *num_args)
859a1d141d5Smrg{
860a1d141d5Smrg    return( FALSE );
861a1d141d5Smrg}
862a1d141d5Smrg
863a1d141d5SmrgEyesClassRec eyesClassRec = {
864a1d141d5Smrg    { /* core fields */
865a1d141d5Smrg    /* superclass		*/	&widgetClassRec,
86626df5c7cSmrg    /* class_name		*/	(char *) "Eyes",
867a1d141d5Smrg    /* size			*/	sizeof(EyesRec),
868a1d141d5Smrg    /* class_initialize		*/	ClassInitialize,
869a1d141d5Smrg    /* class_part_initialize	*/	NULL,
870a1d141d5Smrg    /* class_inited		*/	FALSE,
871a1d141d5Smrg    /* initialize		*/	Initialize,
872a1d141d5Smrg    /* initialize_hook		*/	NULL,
873a1d141d5Smrg    /* realize			*/	Realize,
874a1d141d5Smrg    /* actions			*/	NULL,
875a1d141d5Smrg    /* num_actions		*/	0,
876a1d141d5Smrg    /* resources		*/	resources,
877a1d141d5Smrg    /* num_resources		*/	XtNumber(resources),
878a1d141d5Smrg    /* xrm_class		*/	NULLQUARK,
879a1d141d5Smrg    /* compress_motion		*/	TRUE,
880a1d141d5Smrg    /* compress_exposure	*/	TRUE,
881a1d141d5Smrg    /* compress_enterleave	*/	TRUE,
882a1d141d5Smrg    /* visible_interest		*/	FALSE,
883a1d141d5Smrg    /* destroy			*/	Destroy,
884a1d141d5Smrg    /* resize			*/	Resize,
885a1d141d5Smrg    /* expose			*/	Redisplay,
886a1d141d5Smrg    /* set_values		*/	SetValues,
887a1d141d5Smrg    /* set_values_hook		*/	NULL,
888a1d141d5Smrg    /* set_values_almost	*/	NULL,
889a1d141d5Smrg    /* get_values_hook		*/	NULL,
890a1d141d5Smrg    /* accept_focus		*/	NULL,
891a1d141d5Smrg    /* version			*/	XtVersion,
892a1d141d5Smrg    /* callback_private		*/	NULL,
893a1d141d5Smrg    /* tm_table			*/	NULL,
894a1d141d5Smrg    /* query_geometry		*/	XtInheritQueryGeometry,
895a1d141d5Smrg    }
896a1d141d5Smrg};
897