1a1d141d5Smrg/*
2a1d141d5Smrg
3a1d141d5SmrgCopyright (c) 1991  X Consortium
43bee1c92SmrgCopyright (c) 2023  q3k
5a1d141d5Smrg
6a1d141d5SmrgPermission is hereby granted, free of charge, to any person obtaining
7a1d141d5Smrga copy of this software and associated documentation files (the
8a1d141d5Smrg"Software"), to deal in the Software without restriction, including
9a1d141d5Smrgwithout limitation the rights to use, copy, modify, merge, publish,
10a1d141d5Smrgdistribute, sublicense, and/or sell copies of the Software, and to
11a1d141d5Smrgpermit persons to whom the Software is furnished to do so, subject to
12a1d141d5Smrgthe following conditions:
13a1d141d5Smrg
14a1d141d5SmrgThe above copyright notice and this permission notice shall be included
15a1d141d5Smrgin all copies or substantial portions of the Software.
16a1d141d5Smrg
17a1d141d5SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18a1d141d5SmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19a1d141d5SmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20a1d141d5SmrgIN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
21a1d141d5SmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22a1d141d5SmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23a1d141d5SmrgOTHER DEALINGS IN THE SOFTWARE.
24a1d141d5Smrg
25a1d141d5SmrgExcept as contained in this notice, the name of the X Consortium shall
26a1d141d5Smrgnot be used in advertising or otherwise to promote the sale, use or
27a1d141d5Smrgother dealings in this Software without prior written authorization
28a1d141d5Smrgfrom the X Consortium.
29a1d141d5Smrg
30a1d141d5Smrg*/
31a1d141d5Smrg
32a1d141d5Smrg/*
33a1d141d5Smrg * Eyes.c
34a1d141d5Smrg *
35a1d141d5Smrg * a widget which follows the mouse around
36a1d141d5Smrg */
37a1d141d5Smrg
382ddb6cf1Smrg#ifdef HAVE_CONFIG_H
392ddb6cf1Smrg# include "config.h"
402ddb6cf1Smrg#endif
412ddb6cf1Smrg
42a1d141d5Smrg# include <X11/Xos.h>
43a1d141d5Smrg# include <stdio.h>
44a1d141d5Smrg# include <X11/IntrinsicP.h>
45a1d141d5Smrg# include <X11/StringDefs.h>
46a1d141d5Smrg# include <X11/Xmu/Converters.h>
47a1d141d5Smrg# include "EyesP.h"
48a1d141d5Smrg# include <math.h>
49a1d141d5Smrg# include <X11/extensions/shape.h>
502ddb6cf1Smrg# include <X11/Xlibint.h>
512ddb6cf1Smrg# include <stdlib.h>
5226df5c7cSmrg# include <X11/extensions/XInput2.h>
533bee1c92Smrg# include <assert.h>
54a1d141d5Smrg
55a1d141d5Smrg#define offset(field) XtOffsetOf(EyesRec, eyes.field)
56a1d141d5Smrg#define goffset(field) XtOffsetOf(WidgetRec, core.field)
57a1d141d5Smrg
58a1d141d5Smrgstatic XtResource resources[] = {
5926df5c7cSmrg    {(char *) XtNwidth, (char *) XtCWidth, XtRDimension, sizeof(Dimension),
60a1d141d5Smrg	goffset(width), XtRImmediate, (XtPointer) 150},
6126df5c7cSmrg    {(char *) XtNheight, (char *) XtCHeight, XtRDimension, sizeof(Dimension),
62a1d141d5Smrg	goffset(height), XtRImmediate, (XtPointer) 100},
6326df5c7cSmrg    {(char *) XtNforeground, (char *) XtCForeground, XtRPixel, sizeof(Pixel),
6426df5c7cSmrg        offset(pixel[PART_PUPIL]), XtRString, (char *) XtDefaultForeground},
6526df5c7cSmrg    {(char *) XtNbackgroundPixmap, (char *) XtCPixmap, XtRPixmap, sizeof(Pixmap),
6626df5c7cSmrg     XtOffsetOf(CoreRec,core.background_pixmap),
6726df5c7cSmrg     XtRImmediate, (XtPointer)None},
6826df5c7cSmrg    {(char *) XtNoutline, (char *) XtCForeground, XtRPixel, sizeof(Pixel),
6926df5c7cSmrg        offset(pixel[PART_OUTLINE]), XtRString, (char *) XtDefaultForeground},
7026df5c7cSmrg    {(char *) XtNcenterColor, (char *) XtCBackground, XtRPixel, sizeof (Pixel),
7126df5c7cSmrg	offset(pixel[PART_CENTER]), XtRString, (char *) XtDefaultBackground},
7226df5c7cSmrg    {(char *) XtNreverseVideo, (char *) XtCReverseVideo, XtRBoolean, sizeof (Boolean),
73a1d141d5Smrg	offset (reverse_video), XtRImmediate, (XtPointer) FALSE},
7426df5c7cSmrg    {(char *) XtNbackingStore, (char *) XtCBackingStore, (char *) XtRBackingStore, sizeof (int),
7526df5c7cSmrg    	offset (backing_store), XtRString, (char *) "default"},
7626df5c7cSmrg    {(char *) XtNshapeWindow, (char *) XtCShapeWindow, XtRBoolean, sizeof (Boolean),
77a1d141d5Smrg	offset (shape_window), XtRImmediate, (XtPointer) TRUE},
782ddb6cf1Smrg#ifdef XRENDER
7926df5c7cSmrg    {(char *) XtNrender, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean),
802ddb6cf1Smrg	offset(render), XtRImmediate, (XtPointer) TRUE },
812ddb6cf1Smrg#endif
8226df5c7cSmrg#ifdef PRESENT
8326df5c7cSmrg    {(char *) XtNpresent, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean),
8426df5c7cSmrg     offset(present), XtRImmediate, (XtPointer) TRUE },
8526df5c7cSmrg#endif
8626df5c7cSmrg    {(char *) XtNdistance, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean),
872ddb6cf1Smrg	offset(distance), XtRImmediate, (XtPointer) FALSE },
883bee1c92Smrg    {(char *) XtNbiblicallyAccurate, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean),
893bee1c92Smrg	offset(biblically_accurate), XtRImmediate, (XtPointer) FALSE },
90a1d141d5Smrg};
91a1d141d5Smrg
92a1d141d5Smrg#undef offset
93a1d141d5Smrg#undef goffset
94a1d141d5Smrg
95a1d141d5Smrg# define EYE_OFFSET	(0.1)	/* padding between eyes */
96a1d141d5Smrg# define EYE_THICK	(0.175)	/* thickness of eye rim */
972ddb6cf1Smrg# define BALL_DIAM	(0.3)
982ddb6cf1Smrg# define BALL_PAD	(0.175)
992ddb6cf1Smrg# define EYE_DIAM	(2.0 - (EYE_THICK + EYE_OFFSET) * 2)
1002ddb6cf1Smrg# define BALL_DIST	((EYE_DIAM - BALL_DIAM) / 2.0 - BALL_PAD)
101a1d141d5Smrg
1022ddb6cf1Smrg# define TPOINT_NONE	(-1000)	/* special value meaning "not yet set" */
103a1d141d5Smrg# define TPointEqual(a, b)  ((a).x == (b).x && (a).y == (b).y)
104a1d141d5Smrg# define XPointEqual(a, b)  ((a).x == (b).x && (a).y == (b).y)
1052ddb6cf1Smrg# define AngleBetween(A, A0, A1) (A0 <= A1 ? A0 <= A && A <= A1 : \
1062ddb6cf1Smrg					     A0 <= A || A <= A1)
107a1d141d5Smrg
108a1d141d5Smrgstatic int delays[] = { 50, 100, 200, 400, 0 };
109a1d141d5Smrg
1103bee1c92Smrgstatic EyeLayout layout_standard[] = {
1113bee1c92Smrg	{ .x = 0.0, .y = 0.0, },
1123bee1c92Smrg	{ .x = 2.0, .y = 0.0, },
1133bee1c92Smrg};
1143bee1c92Smrg
1153bee1c92Smrgstatic EyeLayout layout_biblical[] = {
1163bee1c92Smrg	{ .x = 0.0+0.75, .y = 0.0, },
1173bee1c92Smrg	{ .x = 1.5+0.75, .y = 0.0, },
1183bee1c92Smrg	{ .x = 3.0+0.75, .y = 0.0, },
1193bee1c92Smrg
1203bee1c92Smrg	{ .x = 0.0+0.00, .y = 1.4, },
1213bee1c92Smrg	{ .x = 1.5+0.00, .y = 1.4, },
1223bee1c92Smrg	{ .x = 3.0+0.00, .y = 1.4, },
1233bee1c92Smrg	{ .x = 4.5+0.00, .y = 1.4, },
1243bee1c92Smrg
1253bee1c92Smrg	{ .x = 0.0+0.75, .y = 2.8, },
1263bee1c92Smrg	{ .x = 1.5+0.75, .y = 2.8, },
1273bee1c92Smrg	{ .x = 3.0+0.75, .y = 2.8, },
1283bee1c92Smrg};
1293bee1c92Smrg
1303bee1c92Smrgstatic EyeConfiguration *EyesConfigure(Boolean biblically_accurate)
1313bee1c92Smrg{
1323bee1c92Smrg	EyeConfiguration *c = calloc(sizeof(EyeConfiguration), 1);
1333bee1c92Smrg	assert(c != NULL);
1343bee1c92Smrg
1353bee1c92Smrg	if (biblically_accurate) {
1363bee1c92Smrg		c->eyes = layout_biblical;
1373bee1c92Smrg		c->count = sizeof(layout_biblical) / sizeof(EyeLayout);
1383bee1c92Smrg	} else {
1393bee1c92Smrg		c->eyes = layout_standard;
1403bee1c92Smrg		c->count = sizeof(layout_standard) / sizeof(EyeLayout);
1413bee1c92Smrg	}
1423bee1c92Smrg
1433bee1c92Smrg	// Calculate the bounding box of the eyes.
1443bee1c92Smrg	c->w_min_x = c->eyes[0].x;
1453bee1c92Smrg	c->w_max_x = c->eyes[0].x;
1463bee1c92Smrg	c->w_min_y = c->eyes[0].y;
1473bee1c92Smrg	c->w_max_y = c->eyes[0].y;
1483bee1c92Smrg
1493bee1c92Smrg	for (int i = 0; i < c->count; i++) {
1503bee1c92Smrg		EyeLayout *l = &c->eyes[i];
1513bee1c92Smrg		if (l->x > c->w_max_x) {
1523bee1c92Smrg			c->w_max_x = l->x;
1533bee1c92Smrg		}
1543bee1c92Smrg		if (l->x < c->w_min_x) {
1553bee1c92Smrg			c->w_min_x = l->x;
1563bee1c92Smrg		}
1573bee1c92Smrg		if (l->y > c->w_max_y) {
1583bee1c92Smrg			c->w_max_y = l->y;
1593bee1c92Smrg		}
1603bee1c92Smrg		if (l->y < c->w_min_y) {
1613bee1c92Smrg			c->w_min_y = l->y;
1623bee1c92Smrg		}
1633bee1c92Smrg	}
1643bee1c92Smrg
1653bee1c92Smrg	// Add half size of eye (2.0) minus padding to each edge.
1663bee1c92Smrg	c->w_min_x -= (1.0 - EYE_OFFSET);
1673bee1c92Smrg	c->w_max_x += (1.0 - EYE_OFFSET);
1683bee1c92Smrg	c->w_min_y -= (1.0 - EYE_OFFSET);
1693bee1c92Smrg	c->w_max_y += (1.0 - EYE_OFFSET);
1703bee1c92Smrg	return c;
1713bee1c92Smrg}
1723bee1c92Smrg
173a1d141d5Smrgstatic void ClassInitialize(void)
174a1d141d5Smrg{
175a1d141d5Smrg    XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
176a1d141d5Smrg		    NULL, 0 );
177a1d141d5Smrg}
178a1d141d5Smrg
179a1d141d5SmrgWidgetClass eyesWidgetClass = (WidgetClass) &eyesClassRec;
180a1d141d5Smrg
18126df5c7cSmrg#ifdef PRESENT
18226df5c7cSmrgstatic void CheckPresent(EyesWidget w) {
18326df5c7cSmrg    const xcb_query_extension_reply_t 	    *xfixes_ext_reply;
18426df5c7cSmrg    const xcb_query_extension_reply_t 	    *damage_ext_reply;
18526df5c7cSmrg    const xcb_query_extension_reply_t 	    *present_ext_reply;
18626df5c7cSmrg    xcb_xfixes_query_version_cookie_t       xfixes_cookie;
18726df5c7cSmrg    xcb_xfixes_query_version_reply_t        *xfixes_reply;
18826df5c7cSmrg    xcb_damage_query_version_cookie_t       damage_cookie;
18926df5c7cSmrg    xcb_damage_query_version_reply_t        *damage_reply;
19026df5c7cSmrg    xcb_present_query_version_cookie_t      present_cookie;
19126df5c7cSmrg    xcb_present_query_version_reply_t       *present_reply;
19226df5c7cSmrg
19326df5c7cSmrg    if (!w->eyes.present)
19426df5c7cSmrg	return;
19526df5c7cSmrg
19626df5c7cSmrg    xcb_prefetch_extension_data(xt_xcb(w), &xcb_xfixes_id);
19726df5c7cSmrg    xcb_prefetch_extension_data(xt_xcb(w), &xcb_damage_id);
19826df5c7cSmrg    xcb_prefetch_extension_data(xt_xcb(w), &xcb_present_id);
19926df5c7cSmrg
20026df5c7cSmrg    xfixes_ext_reply = xcb_get_extension_data(xt_xcb(w), &xcb_xfixes_id);
20126df5c7cSmrg    damage_ext_reply = xcb_get_extension_data(xt_xcb(w), &xcb_damage_id);
20226df5c7cSmrg    present_ext_reply = xcb_get_extension_data(xt_xcb(w), &xcb_present_id);
20326df5c7cSmrg    if (xfixes_ext_reply == NULL || !xfixes_ext_reply->present
20426df5c7cSmrg	|| damage_ext_reply == NULL || !damage_ext_reply->present
20526df5c7cSmrg	|| present_ext_reply == NULL || !present_ext_reply->present)
20626df5c7cSmrg    {
20726df5c7cSmrg	w->eyes.present = FALSE;
20826df5c7cSmrg    }
20926df5c7cSmrg
21026df5c7cSmrg    if (!w->eyes.present)
21126df5c7cSmrg	return;
21226df5c7cSmrg
21326df5c7cSmrg    /* Now tell the server which versions of the extensions we support */
21426df5c7cSmrg    xfixes_cookie = xcb_xfixes_query_version(xt_xcb(w),
21526df5c7cSmrg					     XCB_XFIXES_MAJOR_VERSION,
21626df5c7cSmrg					     XCB_XFIXES_MINOR_VERSION);
21726df5c7cSmrg
21826df5c7cSmrg    damage_cookie = xcb_damage_query_version(xt_xcb(w),
21926df5c7cSmrg					     XCB_DAMAGE_MAJOR_VERSION,
22026df5c7cSmrg					     XCB_DAMAGE_MINOR_VERSION);
22126df5c7cSmrg
22226df5c7cSmrg    present_cookie = xcb_present_query_version(xt_xcb(w),
22326df5c7cSmrg					       XCB_PRESENT_MAJOR_VERSION,
22426df5c7cSmrg					       XCB_PRESENT_MINOR_VERSION);
22526df5c7cSmrg
22626df5c7cSmrg    xfixes_reply = xcb_xfixes_query_version_reply(xt_xcb(w),
22726df5c7cSmrg						  xfixes_cookie,
22826df5c7cSmrg						  NULL);
22926df5c7cSmrg    free(xfixes_reply);
23026df5c7cSmrg
23126df5c7cSmrg    damage_reply = xcb_damage_query_version_reply(xt_xcb(w),
23226df5c7cSmrg						  damage_cookie,
23326df5c7cSmrg						  NULL);
23426df5c7cSmrg    free(damage_reply);
23526df5c7cSmrg
23626df5c7cSmrg    present_reply = xcb_present_query_version_reply(xt_xcb(w),
23726df5c7cSmrg						    present_cookie,
23826df5c7cSmrg						    NULL);
23926df5c7cSmrg    free(present_reply);
24026df5c7cSmrg}
24126df5c7cSmrg
24226df5c7cSmrgstatic void MakePresentData(EyesWidget w) {
24326df5c7cSmrg
24426df5c7cSmrg    if (!w->eyes.present)
24526df5c7cSmrg        return;
24626df5c7cSmrg
24726df5c7cSmrg    if (!w->eyes.back_buffer) {
24826df5c7cSmrg        xcb_create_pixmap(xt_xcb(w),
24926df5c7cSmrg                          w->core.depth,
25026df5c7cSmrg                          w->eyes.back_buffer = xcb_generate_id(xt_xcb(w)),
25126df5c7cSmrg                          XtWindow(w),
25226df5c7cSmrg                          w->core.width,
25326df5c7cSmrg                          w->core.height);
25426df5c7cSmrg    }
25526df5c7cSmrg    if (!w->eyes.back_damage) {
25626df5c7cSmrg        xcb_damage_create(xt_xcb(w),
25726df5c7cSmrg                          w->eyes.back_damage = xcb_generate_id(xt_xcb(w)),
25826df5c7cSmrg                          w->eyes.back_buffer,
25926df5c7cSmrg                          XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
26026df5c7cSmrg        xcb_xfixes_create_region(xt_xcb(w),
26126df5c7cSmrg                                 w->eyes.back_region = xcb_generate_id(xt_xcb(w)),
26226df5c7cSmrg                                 0, NULL);
26326df5c7cSmrg    }
26426df5c7cSmrg}
26526df5c7cSmrg
26626df5c7cSmrgstatic void UpdatePresent(EyesWidget w) {
26726df5c7cSmrg    if (w->eyes.back_buffer) {
26826df5c7cSmrg        xcb_damage_subtract(xt_xcb(w),
26926df5c7cSmrg                            w->eyes.back_damage,
27026df5c7cSmrg                            None,
27126df5c7cSmrg                            w->eyes.back_region);
27226df5c7cSmrg        xcb_present_pixmap(xt_xcb(w),
27326df5c7cSmrg                           XtWindow(w),
27426df5c7cSmrg                           w->eyes.back_buffer,
27526df5c7cSmrg                           0,
27626df5c7cSmrg                           None,
27726df5c7cSmrg                           w->eyes.back_region,
27826df5c7cSmrg                           0, 0,
27926df5c7cSmrg                           None,
28026df5c7cSmrg			   None,
28126df5c7cSmrg			   None,
28226df5c7cSmrg			   0,
28326df5c7cSmrg			   0, 1, 0,
28426df5c7cSmrg			   0, NULL);
28526df5c7cSmrg    }
28626df5c7cSmrg}
28726df5c7cSmrg
28826df5c7cSmrg#endif
28926df5c7cSmrg
29026df5c7cSmrg#ifdef PRESENT
29126df5c7cSmrg#define EyesDrawable(w) (w->eyes.back_buffer ? w->eyes.back_buffer : XtWindow(w))
29226df5c7cSmrg#else
29326df5c7cSmrg#define EyesDrawable(w) XtWindow(w)
29426df5c7cSmrg#endif
29526df5c7cSmrg
29626df5c7cSmrgstatic void draw_it_core(EyesWidget w);
29726df5c7cSmrg
29826df5c7cSmrgstatic void EyesGeneric(Widget w, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch)
29926df5c7cSmrg{
30026df5c7cSmrg        draw_it_core((EyesWidget) w);
30126df5c7cSmrg}
30226df5c7cSmrg
30326df5c7cSmrgstruct root_listen_list {
30426df5c7cSmrg    struct root_listen_list *next;
30526df5c7cSmrg    Widget      widget;
30626df5c7cSmrg};
30726df5c7cSmrg
30826df5c7cSmrgstatic struct root_listen_list *root_listen_list;
30926df5c7cSmrg
31026df5c7cSmrgstatic Boolean xi2_dispatcher(XEvent *event) {
31126df5c7cSmrg    struct root_listen_list *rll;
31226df5c7cSmrg    Boolean was_dispatched = False;
31326df5c7cSmrg
31426df5c7cSmrg    for (rll = root_listen_list; rll; rll = rll->next) {
31526df5c7cSmrg        if (XtDisplay(rll->widget) == event->xany.display) {
31626df5c7cSmrg            XtDispatchEventToWidget(rll->widget, event);
31726df5c7cSmrg            was_dispatched = True;
31826df5c7cSmrg        }
31926df5c7cSmrg    }
32026df5c7cSmrg    return was_dispatched;
32126df5c7cSmrg}
32226df5c7cSmrg
32326df5c7cSmrgstatic void select_xi2_events(Widget w)
32426df5c7cSmrg{
32526df5c7cSmrg    XIEventMask evmasks[1];
32626df5c7cSmrg    unsigned char mask1[(XI_LASTEVENT + 7)/8];
32726df5c7cSmrg
32826df5c7cSmrg    memset(mask1, 0, sizeof(mask1));
32926df5c7cSmrg
33026df5c7cSmrg    /* select for button and key events from all master devices */
33126df5c7cSmrg    XISetMask(mask1, XI_RawMotion);
33226df5c7cSmrg
33326df5c7cSmrg    evmasks[0].deviceid = XIAllMasterDevices;
33426df5c7cSmrg    evmasks[0].mask_len = sizeof(mask1);
33526df5c7cSmrg    evmasks[0].mask = mask1;
33626df5c7cSmrg
33726df5c7cSmrg    XISelectEvents(XtDisplay(w),
33826df5c7cSmrg                   RootWindowOfScreen(XtScreen(w)),
33926df5c7cSmrg                   evmasks, 1);
34026df5c7cSmrg    XtSetEventDispatcher(XtDisplay(w),
34126df5c7cSmrg                         GenericEvent,
34226df5c7cSmrg                         xi2_dispatcher);
34326df5c7cSmrg}
34426df5c7cSmrg
34526df5c7cSmrgstatic Boolean xi2_add_root_listener(Widget widget)
34626df5c7cSmrg{
34726df5c7cSmrg    struct root_listen_list *rll = malloc (sizeof (struct root_listen_list));
34826df5c7cSmrg
34926df5c7cSmrg    if (!rll)
35026df5c7cSmrg        return False;
35126df5c7cSmrg    rll->widget = widget;
35226df5c7cSmrg    rll->next = root_listen_list;
35326df5c7cSmrg    if (!root_listen_list)
35426df5c7cSmrg            select_xi2_events(widget);
35526df5c7cSmrg    root_listen_list = rll;
35626df5c7cSmrg    XtInsertEventTypeHandler(widget, GenericEvent, NULL, EyesGeneric, NULL, XtListHead);
35726df5c7cSmrg    return True;
35826df5c7cSmrg}
35926df5c7cSmrg
36026df5c7cSmrgstatic void xi2_remove_root_listener(Widget widget)
36126df5c7cSmrg{
36226df5c7cSmrg    struct root_listen_list *rll, **prev;
36326df5c7cSmrg
36426df5c7cSmrg    for (prev = &root_listen_list; (rll = *prev) != NULL; prev = &rll->next) {
36526df5c7cSmrg        if (rll->widget == widget) {
36626df5c7cSmrg            *prev = rll->next;
36726df5c7cSmrg            free(rll);
36826df5c7cSmrg            break;
36926df5c7cSmrg        }
37026df5c7cSmrg    }
37126df5c7cSmrg}
37226df5c7cSmrg
37326df5c7cSmrg/* Return 1 if XI2 is available, 0 otherwise */
37426df5c7cSmrgstatic int has_xi2(Display *dpy)
37526df5c7cSmrg{
37626df5c7cSmrg    int major, minor;
37726df5c7cSmrg    int rc;
37826df5c7cSmrg
37926df5c7cSmrg    /* We need at least XI 2.0 */
38026df5c7cSmrg    major = 2;
38126df5c7cSmrg    minor = 0;
38226df5c7cSmrg
38326df5c7cSmrg    rc = XIQueryVersion(dpy, &major, &minor);
38426df5c7cSmrg    if (rc == BadRequest) {
38526df5c7cSmrg	return 0;
38626df5c7cSmrg    } else if (rc != Success) {
38726df5c7cSmrg        return 0;
38826df5c7cSmrg    }
38926df5c7cSmrg    return 1;
39026df5c7cSmrg}
39126df5c7cSmrg
39226df5c7cSmrg
393a1d141d5Smrg/* ARGSUSED */
394a1d141d5Smrgstatic void Initialize (
395a1d141d5Smrg    Widget greq,
396a1d141d5Smrg    Widget gnew,
397a1d141d5Smrg    ArgList args,
398a1d141d5Smrg    Cardinal *num_args)
399a1d141d5Smrg{
400a1d141d5Smrg    EyesWidget w = (EyesWidget)gnew;
401a1d141d5Smrg    XtGCMask	valuemask;
402a1d141d5Smrg    XGCValues	myXGCV;
403a1d141d5Smrg    int shape_event_base, shape_error_base;
4042ddb6cf1Smrg#ifdef XRENDER
4052ddb6cf1Smrg    enum EyesPart i;
4062ddb6cf1Smrg#endif
407a1d141d5Smrg
4083bee1c92Smrg    EyeConfiguration *config = EyesConfigure(w->eyes.biblically_accurate);
4093bee1c92Smrg    TPoint *pupils = calloc(sizeof(TPoint), config->count);
4103bee1c92Smrg    assert(pupils != NULL);
4113bee1c92Smrg    for (int j = 0; j < config->count; j++) {
4123bee1c92Smrg        pupils[j].x = TPOINT_NONE;
4133bee1c92Smrg        pupils[j].y = TPOINT_NONE;
4143bee1c92Smrg    }
4153bee1c92Smrg    w->eyes.configuration = config;
4163bee1c92Smrg    w->eyes.pupils = pupils;
4173bee1c92Smrg
4183bee1c92Smrg
419a1d141d5Smrg    /*
420a1d141d5Smrg     * set the colors if reverse video; these are the colors used:
421a1d141d5Smrg     *
422a1d141d5Smrg     *     background - paper		white
423a1d141d5Smrg     *     foreground - text, ticks	black
424a1d141d5Smrg     *     border - border		black (foreground)
425a1d141d5Smrg     *
4262ddb6cf1Smrg     * This doesn't completely work since the parent has already made up a
427a1d141d5Smrg     * border.  Sigh.
428a1d141d5Smrg     */
429a1d141d5Smrg    if (w->eyes.reverse_video) {
4302ddb6cf1Smrg	Pixel fg = w->eyes.pixel[PART_PUPIL];
431a1d141d5Smrg	Pixel bg = w->core.background_pixel;
432a1d141d5Smrg
433a1d141d5Smrg	if (w->core.border_pixel == fg)
434a1d141d5Smrg 	    w->core.border_pixel = bg;
4352ddb6cf1Smrg	if (w->eyes.pixel[PART_OUTLINE] == fg)
4362ddb6cf1Smrg	    w->eyes.pixel[PART_OUTLINE] = bg;
4372ddb6cf1Smrg	if (w->eyes.pixel[PART_CENTER] == bg)
4382ddb6cf1Smrg	    w->eyes.pixel[PART_CENTER] = fg;
4392ddb6cf1Smrg	w->eyes.pixel[PART_PUPIL] = bg;
440a1d141d5Smrg	w->core.background_pixel = fg;
441a1d141d5Smrg    }
442a1d141d5Smrg
4432ddb6cf1Smrg    myXGCV.foreground = w->eyes.pixel[PART_PUPIL];
444a1d141d5Smrg    myXGCV.background = w->core.background_pixel;
445a1d141d5Smrg    valuemask = GCForeground | GCBackground;
4462ddb6cf1Smrg    w->eyes.gc[PART_PUPIL] = XtGetGC(gnew, valuemask, &myXGCV);
447a1d141d5Smrg
4482ddb6cf1Smrg    myXGCV.foreground = w->eyes.pixel[PART_OUTLINE];
449a1d141d5Smrg    valuemask = GCForeground | GCBackground;
4502ddb6cf1Smrg    w->eyes.gc[PART_OUTLINE] = XtGetGC(gnew, valuemask, &myXGCV);
451a1d141d5Smrg
4522ddb6cf1Smrg    myXGCV.foreground = w->eyes.pixel[PART_CENTER];
4532ddb6cf1Smrg    myXGCV.background = w->eyes.pixel[PART_PUPIL];
454a1d141d5Smrg    valuemask = GCForeground | GCBackground;
4552ddb6cf1Smrg    w->eyes.gc[PART_CENTER] = XtGetGC(gnew, valuemask, &myXGCV);
456a1d141d5Smrg
457a1d141d5Smrg    w->eyes.update = 0;
458a1d141d5Smrg    /* wait for Realize to add the timeout */
459a1d141d5Smrg    w->eyes.interval_id = 0;
460a1d141d5Smrg
4612ddb6cf1Smrg    w->eyes.mouse.x = w->eyes.mouse.y = TPOINT_NONE;
462a1d141d5Smrg
463a1d141d5Smrg    if (w->eyes.shape_window && !XShapeQueryExtension (XtDisplay (w),
464a1d141d5Smrg						       &shape_event_base,
465a1d141d5Smrg						       &shape_error_base))
466a1d141d5Smrg	w->eyes.shape_window = False;
467a1d141d5Smrg    w->eyes.shape_mask = 0;
4682ddb6cf1Smrg    w->eyes.gc[PART_SHAPE] = NULL;
4692ddb6cf1Smrg
47026df5c7cSmrg    w->eyes.has_xi2 = has_xi2(XtDisplay(w));
47126df5c7cSmrg
4722ddb6cf1Smrg#ifdef XRENDER
4732ddb6cf1Smrg    for (i = 0; i < PART_SHAPE; i ++) {
4742ddb6cf1Smrg	XColor c;
4752ddb6cf1Smrg	XRenderColor rc;
4762ddb6cf1Smrg
4772ddb6cf1Smrg	c.pixel = w->eyes.pixel[i];
4782ddb6cf1Smrg	XQueryColor(XtDisplay (w), w->core.colormap, &c);
4792ddb6cf1Smrg
4802ddb6cf1Smrg	rc.red = c.red;
4812ddb6cf1Smrg	rc.green = c.green;
4822ddb6cf1Smrg	rc.blue = c.blue;
4832ddb6cf1Smrg	rc.alpha = -1;
4842ddb6cf1Smrg	w->eyes.fill[i] = XRenderCreateSolidFill(XtDisplay (w), &rc);
4852ddb6cf1Smrg    }
4862ddb6cf1Smrg#endif
48726df5c7cSmrg#ifdef PRESENT
48826df5c7cSmrg    w->eyes.back_buffer = None;
48926df5c7cSmrg    w->eyes.back_damage = None;
49026df5c7cSmrg    CheckPresent(w);
49126df5c7cSmrg#endif
492a1d141d5Smrg}
493a1d141d5Smrg
4942ddb6cf1Smrgstatic void
4952ddb6cf1SmrgdrawEllipse(EyesWidget w, enum EyesPart part,
4962ddb6cf1Smrg	    double centerx, double centery,
4972ddb6cf1Smrg	    double oldx, double oldy,
4982ddb6cf1Smrg	    double diam)
499a1d141d5Smrg{
5002ddb6cf1Smrg    const TRectangle tpos = {
5012ddb6cf1Smrg	centerx - diam/2.0,
5022ddb6cf1Smrg	centery - diam/2.0,
5032ddb6cf1Smrg	diam, diam };
5042ddb6cf1Smrg    TRectangle pos;
5052ddb6cf1Smrg    Trectangle(&w->eyes.t, &tpos, &pos);
5062ddb6cf1Smrg
5072ddb6cf1Smrg    if (part == PART_CLEAR) {
50826df5c7cSmrg	XFillRectangle(XtDisplay(w), EyesDrawable(w),
5092ddb6cf1Smrg		       w->eyes.gc[PART_CENTER],
5102ddb6cf1Smrg		       (int)pos.x, (int)pos.y,
5112ddb6cf1Smrg		       (int)pos.width+2, (int)pos.height+2);
5122ddb6cf1Smrg	return;
5132ddb6cf1Smrg    }
5142ddb6cf1Smrg#ifdef XRENDER
5152ddb6cf1Smrg    if (w->eyes.render && part != PART_SHAPE && (!w->eyes.shape_window ||
5162ddb6cf1Smrg						 part != PART_OUTLINE) &&
5172ddb6cf1Smrg	w->eyes.picture) {
5182ddb6cf1Smrg	int n, i;
5192ddb6cf1Smrg	double hd, c, s, sx, sy, x, y, px, py;
5202ddb6cf1Smrg	XPointDouble *p;
5212ddb6cf1Smrg
5222ddb6cf1Smrg	pos.x = pos.x + pos.width/2.0;
5232ddb6cf1Smrg	pos.y = pos.y + pos.height/2.0;
5242ddb6cf1Smrg
5252ddb6cf1Smrg	/* determine number of segments to draw */
5262ddb6cf1Smrg	hd = hypot(pos.width, pos.height)/2;
5272ddb6cf1Smrg	n = (M_PI / acos(hd/(hd+1.0))) + 0.5;
5282ddb6cf1Smrg	if (n < 2) n = 2;
5292ddb6cf1Smrg
5302ddb6cf1Smrg	c = cos(M_PI/n);
5312ddb6cf1Smrg	s = sin(M_PI/n);
5322ddb6cf1Smrg	sx = -(pos.width*s)/pos.height;
5332ddb6cf1Smrg	sy = (pos.height*s)/pos.width;
5342ddb6cf1Smrg
5352ddb6cf1Smrg	n *= 2;
5362ddb6cf1Smrg	p = Xmalloc(sizeof(*p)*n);
5372ddb6cf1Smrg	if (!p)
5382ddb6cf1Smrg	    return;
5392ddb6cf1Smrg	x = 0;
5402ddb6cf1Smrg	y = pos.height/2.0;
5412ddb6cf1Smrg	for (i = 0; i < n; i ++)
5422ddb6cf1Smrg	{
5432ddb6cf1Smrg	    p[i].x = x + pos.x;
5442ddb6cf1Smrg	    p[i].y = y + pos.y;
5452ddb6cf1Smrg	    px = x;
5462ddb6cf1Smrg	    py = y;
5472ddb6cf1Smrg	    x = c*px + sx*py;
5482ddb6cf1Smrg	    y = c*py + sy*px;
549a1d141d5Smrg	}
5502ddb6cf1Smrg
5512ddb6cf1Smrg	if (oldx != TPOINT_NONE || oldy != TPOINT_NONE)
5522ddb6cf1Smrg	    drawEllipse(w, PART_CLEAR, oldx, oldy,
5532ddb6cf1Smrg			TPOINT_NONE, TPOINT_NONE, diam);
5542ddb6cf1Smrg
5552ddb6cf1Smrg	XRenderCompositeDoublePoly(XtDisplay(w), PictOpOver,
5562ddb6cf1Smrg				   w->eyes.fill[part], w->eyes.picture,
5572ddb6cf1Smrg				   XRenderFindStandardFormat(XtDisplay(w),
5582ddb6cf1Smrg							     PictStandardA8),
5592ddb6cf1Smrg				   0, 0, 0, 0, p, n, 0);
5602ddb6cf1Smrg
5612ddb6cf1Smrg	Xfree(p);
5622ddb6cf1Smrg	return;
5632ddb6cf1Smrg    }
5642ddb6cf1Smrg#endif
5652ddb6cf1Smrg    if (oldx != TPOINT_NONE || oldy != TPOINT_NONE)
5662ddb6cf1Smrg	drawEllipse(w, PART_CLEAR, oldx, oldy,
5672ddb6cf1Smrg		    TPOINT_NONE, TPOINT_NONE, diam);
5682ddb6cf1Smrg
5692ddb6cf1Smrg    XFillArc(XtDisplay(w),
57026df5c7cSmrg	     part == PART_SHAPE ? w->eyes.shape_mask : EyesDrawable(w),
5712ddb6cf1Smrg	     w->eyes.gc[part],
5722ddb6cf1Smrg	     (int)(pos.x + 0.5), (int)(pos.y + 0.5),
5732ddb6cf1Smrg	     (int)(pos.width + 0.0), (int)(pos.height + 0.0),
5742ddb6cf1Smrg	     90*64, 360*64);
5752ddb6cf1Smrg}
5762ddb6cf1Smrg
5772ddb6cf1Smrg
5782ddb6cf1Smrgstatic void
5792ddb6cf1SmrgeyeLiner(EyesWidget	w,
5802ddb6cf1Smrg	 Boolean	draw,
5812ddb6cf1Smrg	 int		num)
5822ddb6cf1Smrg{
5833bee1c92Smrg    EyeLayout *l = &w->eyes.configuration->eyes[num];
5842ddb6cf1Smrg    drawEllipse(w, draw ? PART_OUTLINE : PART_SHAPE,
5853bee1c92Smrg		l->x, l->y,
5862ddb6cf1Smrg		TPOINT_NONE, TPOINT_NONE,
5872ddb6cf1Smrg		EYE_DIAM + 2.0*EYE_THICK);
5882ddb6cf1Smrg    if (draw) {
5893bee1c92Smrg	drawEllipse(w, PART_CENTER, l->x, l->y,
5902ddb6cf1Smrg		    TPOINT_NONE, TPOINT_NONE,
5912ddb6cf1Smrg		    EYE_DIAM);
5922ddb6cf1Smrg    }
593a1d141d5Smrg}
594a1d141d5Smrg
595a1d141d5Smrgstatic TPoint computePupil (
5963bee1c92Smrg    EyeLayout *layout,
5972ddb6cf1Smrg    TPoint	mouse,
5982ddb6cf1Smrg    const TRectangle *screen)
599a1d141d5Smrg{
600a1d141d5Smrg	double	cx, cy;
601a1d141d5Smrg	double	dist;
602a1d141d5Smrg	double	angle;
603a1d141d5Smrg	double	dx, dy;
604a1d141d5Smrg	double	cosa, sina;
605a1d141d5Smrg	TPoint	ret;
606a1d141d5Smrg
6073bee1c92Smrg	cx = layout->x; dx = mouse.x - cx;
6083bee1c92Smrg	cy = layout->y; dy = mouse.y - cy;
6092ddb6cf1Smrg	if (dx == 0 && dy == 0);
6102ddb6cf1Smrg	else {
611a1d141d5Smrg		angle = atan2 ((double) dy, (double) dx);
612a1d141d5Smrg		cosa = cos (angle);
613a1d141d5Smrg		sina = sin (angle);
6142ddb6cf1Smrg		dist = BALL_DIST;
6152ddb6cf1Smrg		if (screen)
6162ddb6cf1Smrg		{
6172ddb6cf1Smrg		    /* use distance mapping */
6182ddb6cf1Smrg		    double x0, y0, x1, y1;
6192ddb6cf1Smrg		    double a[4];
6202ddb6cf1Smrg		    x0 = screen->x - cx;
6212ddb6cf1Smrg		    y0 = screen->y - cy;
6222ddb6cf1Smrg		    x1 = x0 + screen->width;
6232ddb6cf1Smrg		    y1 = y0 + screen->height;
6242ddb6cf1Smrg		    a[0] = atan2(y0, x0);
6252ddb6cf1Smrg		    a[1] = atan2(y1, x0);
6262ddb6cf1Smrg		    a[2] = atan2(y1, x1);
6272ddb6cf1Smrg		    a[3] = atan2(y0, x1);
6282ddb6cf1Smrg		    if (AngleBetween(angle, a[0], a[1]))
6292ddb6cf1Smrg		    {
6302ddb6cf1Smrg			/* left */
6312ddb6cf1Smrg			dist *= dx / x0;
6322ddb6cf1Smrg		    }
6332ddb6cf1Smrg		    else if (AngleBetween(angle, a[1], a[2]))
6342ddb6cf1Smrg		    {
6352ddb6cf1Smrg			/* bottom */
6362ddb6cf1Smrg			dist *= dy / y1;
6372ddb6cf1Smrg		    }
6382ddb6cf1Smrg		    else if (AngleBetween(angle, a[2], a[3]))
6392ddb6cf1Smrg		    {
6402ddb6cf1Smrg			/* right */
6412ddb6cf1Smrg			dist *= dx / x1;
6422ddb6cf1Smrg		    }
6432ddb6cf1Smrg		    else if (AngleBetween(angle, a[3], a[0]))
6442ddb6cf1Smrg		    {
6452ddb6cf1Smrg			/* top */
6462ddb6cf1Smrg			dist *= dy / y0;
6472ddb6cf1Smrg		    }
6482ddb6cf1Smrg		    if (dist > BALL_DIST)
6492ddb6cf1Smrg			dist = BALL_DIST;
6502ddb6cf1Smrg		}
651a1d141d5Smrg		if (dist > hypot ((double) dx, (double) dy)) {
6522ddb6cf1Smrg			cx += dx;
6532ddb6cf1Smrg			cy += dy;
654a1d141d5Smrg		} else {
6552ddb6cf1Smrg			cx += dist * cosa;
6562ddb6cf1Smrg			cy += dist * sina;
657a1d141d5Smrg		}
658a1d141d5Smrg	}
659a1d141d5Smrg	ret.x = cx;
660a1d141d5Smrg	ret.y = cy;
661a1d141d5Smrg	return ret;
662a1d141d5Smrg}
663a1d141d5Smrg
664a1d141d5Smrgstatic void computePupils (
6652ddb6cf1Smrg    EyesWidget	w,
666a1d141d5Smrg    TPoint	mouse,
6673bee1c92Smrg    TPoint  *pupils)
668a1d141d5Smrg{
6692ddb6cf1Smrg    TRectangle screen, *sp = NULL;
6702ddb6cf1Smrg    if (w->eyes.distance) {
6712ddb6cf1Smrg	Window r, cw;
6722ddb6cf1Smrg	int x, y;
6732ddb6cf1Smrg	r = RootWindowOfScreen(w->core.screen);
6742ddb6cf1Smrg	XTranslateCoordinates(XtDisplay(w), XtWindow(w), r, 0, 0, &x, &y, &cw);
6752ddb6cf1Smrg	screen.x = Tx(-x, -y, &w->eyes.t);
6762ddb6cf1Smrg	screen.y = Ty(-x, -y, &w->eyes.t);
6772ddb6cf1Smrg	screen.width  = Twidth (w->core.screen->width, w->core.screen->height,
6782ddb6cf1Smrg				&w->eyes.t);
6792ddb6cf1Smrg	screen.height = Theight(w->core.screen->width, w->core.screen->height,
6802ddb6cf1Smrg				&w->eyes.t);
6812ddb6cf1Smrg	sp = &screen;
6822ddb6cf1Smrg    }
6833bee1c92Smrg    for (int i = 0; i < w->eyes.configuration->count; i++) {
6843bee1c92Smrg        pupils[i] = computePupil(&w->eyes.configuration->eyes[i], mouse, sp);
6853bee1c92Smrg    }
686a1d141d5Smrg}
687a1d141d5Smrg
6882ddb6cf1Smrgstatic void
6892ddb6cf1SmrgeyeBall(EyesWidget	w,
6902ddb6cf1Smrg	Boolean draw,
6912ddb6cf1Smrg	TPoint	*old,
6922ddb6cf1Smrg	int	num)
693a1d141d5Smrg{
6943bee1c92Smrg    //printf("eyeBall(_, %d, %p, %d)\n", draw, old, num);
6952ddb6cf1Smrg    drawEllipse(w, draw ? PART_PUPIL : PART_CLEAR,
6963bee1c92Smrg		w->eyes.pupils[num].x, w->eyes.pupils[num].y,
6972ddb6cf1Smrg		old ? old->x : TPOINT_NONE, old ? old->y : TPOINT_NONE,
6982ddb6cf1Smrg		BALL_DIAM);
699a1d141d5Smrg}
700a1d141d5Smrg
701a1d141d5Smrgstatic void repaint_window (EyesWidget w)
702a1d141d5Smrg{
703a1d141d5Smrg	if (XtIsRealized ((Widget) w)) {
70426df5c7cSmrg#ifdef PRESENT
70526df5c7cSmrg                MakePresentData(w);
70626df5c7cSmrg#endif
7073bee1c92Smrg		for (int i = 0; i < w->eyes.configuration->count; i++) {
7083bee1c92Smrg			eyeLiner (w, TRUE, i);
7093bee1c92Smrg		}
7103bee1c92Smrg		computePupils (w, w->eyes.mouse, w->eyes.pupils);
7113bee1c92Smrg		for (int i = 0; i < w->eyes.configuration->count; i++) {
7123bee1c92Smrg			eyeBall (w, TRUE, NULL, i);
7133bee1c92Smrg		}
71426df5c7cSmrg#ifdef PRESENT
71526df5c7cSmrg                UpdatePresent(w);
71626df5c7cSmrg#endif
717a1d141d5Smrg	}
718a1d141d5Smrg}
719a1d141d5Smrg
7202ddb6cf1Smrgstatic void
7212ddb6cf1SmrgdrawEye(EyesWidget w, TPoint newpupil, int num)
7222ddb6cf1Smrg{
7232ddb6cf1Smrg    XPoint		xnewpupil, xpupil;
7242ddb6cf1Smrg
7253bee1c92Smrg    xpupil.x = Xx(w->eyes.pupils[num].x, w->eyes.pupils[num].y, &w->eyes.t);
7263bee1c92Smrg    xpupil.y = Xy(w->eyes.pupils[num].x, w->eyes.pupils[num].y, &w->eyes.t);
7272ddb6cf1Smrg    xnewpupil.x = Xx(newpupil.x, newpupil.y, &w->eyes.t);
7282ddb6cf1Smrg    xnewpupil.y = Xy(newpupil.x, newpupil.y, &w->eyes.t);
7292ddb6cf1Smrg    if (
7302ddb6cf1Smrg#ifdef XRENDER
7313bee1c92Smrg	w->eyes.picture ? !TPointEqual(w->eyes.pupils[num], newpupil) :
7322ddb6cf1Smrg#endif
7332ddb6cf1Smrg	!XPointEqual(xpupil, xnewpupil)) {
7343bee1c92Smrg	TPoint oldpupil = w->eyes.pupils[num];
7353bee1c92Smrg	w->eyes.pupils[num] = newpupil;
7362ddb6cf1Smrg	eyeBall (w, TRUE, &oldpupil, num);
7372ddb6cf1Smrg    }
7382ddb6cf1Smrg}
7392ddb6cf1Smrg
7402ddb6cf1Smrgstatic void
7412ddb6cf1SmrgdrawEyes(EyesWidget w, TPoint mouse)
7422ddb6cf1Smrg{
7432ddb6cf1Smrg    int			num;
7443bee1c92Smrg    TPoint newpupils[w->eyes.configuration->count];
7452ddb6cf1Smrg
74626df5c7cSmrg#ifdef PRESENT
74726df5c7cSmrg    MakePresentData(w);
74826df5c7cSmrg#endif
7492ddb6cf1Smrg    if (TPointEqual (mouse, w->eyes.mouse)) {
7502ddb6cf1Smrg	if (delays[w->eyes.update + 1] != 0)
7512ddb6cf1Smrg	    ++w->eyes.update;
7522ddb6cf1Smrg	return;
7532ddb6cf1Smrg    }
7543bee1c92Smrg    computePupils (w, mouse, newpupils);
7553bee1c92Smrg    for (num = 0; num < w->eyes.configuration->count; num++) {
7563bee1c92Smrg        drawEye(w, newpupils[num], num);
7572ddb6cf1Smrg    }
7582ddb6cf1Smrg
7592ddb6cf1Smrg    w->eyes.mouse = mouse;
7602ddb6cf1Smrg    w->eyes.update = 0;
76126df5c7cSmrg#ifdef PRESENT
76226df5c7cSmrg    UpdatePresent(w);
76326df5c7cSmrg#endif
7642ddb6cf1Smrg}
7652ddb6cf1Smrg
7662ddb6cf1Smrgstatic void draw_it_core(EyesWidget w)
7672ddb6cf1Smrg{
7682ddb6cf1Smrg    Window		rep_root, rep_child;
7692ddb6cf1Smrg    int			rep_rootx, rep_rooty;
7702ddb6cf1Smrg    unsigned int	rep_mask;
7712ddb6cf1Smrg    int			dx, dy;
7722ddb6cf1Smrg    TPoint		mouse;
7732ddb6cf1Smrg    Display		*dpy = XtDisplay (w);
7742ddb6cf1Smrg    Window		win = XtWindow (w);
7752ddb6cf1Smrg
7762ddb6cf1Smrg    XQueryPointer (dpy, win, &rep_root, &rep_child,
7772ddb6cf1Smrg	    &rep_rootx, &rep_rooty, &dx, &dy, &rep_mask);
7782ddb6cf1Smrg    mouse.x = Tx(dx, dy, &w->eyes.t);
7792ddb6cf1Smrg    mouse.y = Ty(dx, dy, &w->eyes.t);
7802ddb6cf1Smrg
7812ddb6cf1Smrg    drawEyes(w, mouse);
7822ddb6cf1Smrg}
7832ddb6cf1Smrg
784a1d141d5Smrg/* ARGSUSED */
785a1d141d5Smrgstatic void draw_it (
786a1d141d5Smrg     XtPointer client_data,
787a1d141d5Smrg     XtIntervalId *id)		/* unused */
788a1d141d5Smrg{
789a1d141d5Smrg        EyesWidget	w = (EyesWidget)client_data;
790a1d141d5Smrg
791a1d141d5Smrg	if (XtIsRealized((Widget)w)) {
7922ddb6cf1Smrg	        draw_it_core(w);
793a1d141d5Smrg	}
79426df5c7cSmrg        if (!w->eyes.has_xi2) {
79526df5c7cSmrg                w->eyes.interval_id =
79626df5c7cSmrg                        XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) w),
79726df5c7cSmrg                                        delays[w->eyes.update], draw_it, (XtPointer)w);
79826df5c7cSmrg        }
799a1d141d5Smrg} /* draw_it */
800a1d141d5Smrg
801a1d141d5Smrgstatic void Resize (Widget gw)
802a1d141d5Smrg{
803a1d141d5Smrg    EyesWidget	w = (EyesWidget) gw;
804a1d141d5Smrg    XGCValues	xgcv;
805a1d141d5Smrg    Widget	parent;
8062ddb6cf1Smrg    Display	*dpy = XtDisplay (w);
807a1d141d5Smrg    int		x, y;
808a1d141d5Smrg
809a1d141d5Smrg    if (XtIsRealized (gw))
810a1d141d5Smrg    {
811a1d141d5Smrg    	SetTransform (&w->eyes.t,
812a1d141d5Smrg		    	0, w->core.width,
813a1d141d5Smrg 		    	w->core.height, 0,
8143bee1c92Smrg		    	w->eyes.configuration->w_min_x,
8153bee1c92Smrg		    	w->eyes.configuration->w_max_x,
8163bee1c92Smrg		    	w->eyes.configuration->w_min_y,
8173bee1c92Smrg		    	w->eyes.configuration->w_max_y);
81826df5c7cSmrg#ifdef PRESENT
81926df5c7cSmrg        if (w->eyes.back_buffer) {
82026df5c7cSmrg                xcb_free_pixmap(xt_xcb(w),
82126df5c7cSmrg                                w->eyes.back_buffer);
82226df5c7cSmrg                w->eyes.back_buffer = None;
82326df5c7cSmrg                xcb_damage_destroy(xt_xcb(w),
82426df5c7cSmrg                                   w->eyes.back_damage);
82526df5c7cSmrg                w->eyes.back_damage = None;
82626df5c7cSmrg        }
82726df5c7cSmrg        MakePresentData(w);
82826df5c7cSmrg#endif
82926df5c7cSmrg        if (EyesDrawable(w) == XtWindow(w))
83026df5c7cSmrg                XClearWindow (dpy, XtWindow (w));
83126df5c7cSmrg
8322ddb6cf1Smrg#ifdef XRENDER
8332ddb6cf1Smrg	if (w->eyes.picture) {
8342ddb6cf1Smrg	    XRenderFreePicture(dpy, w->eyes.picture);
8352ddb6cf1Smrg	    w->eyes.picture = 0;
8362ddb6cf1Smrg	}
8372ddb6cf1Smrg#endif
838a1d141d5Smrg    	if (w->eyes.shape_window) {
8392ddb6cf1Smrg	    w->eyes.shape_mask = XCreatePixmap (dpy, XtWindow (w),
840a1d141d5Smrg	    	    w->core.width, w->core.height, 1);
8412ddb6cf1Smrg	    if (!w->eyes.gc[PART_SHAPE])
8422ddb6cf1Smrg		w->eyes.gc[PART_SHAPE] = XCreateGC (dpy, w->eyes.shape_mask,
8432ddb6cf1Smrg						    0, &xgcv);
8442ddb6cf1Smrg	    XSetForeground (dpy, w->eyes.gc[PART_SHAPE], 0);
8452ddb6cf1Smrg	    XFillRectangle (dpy, w->eyes.shape_mask, w->eyes.gc[PART_SHAPE],
8462ddb6cf1Smrg			    0, 0, w->core.width, w->core.height);
8472ddb6cf1Smrg	    XSetForeground (dpy, w->eyes.gc[PART_SHAPE], 1);
8483bee1c92Smrg	    for (int i = 0; i < w->eyes.configuration->count; i++) {
8493bee1c92Smrg	        eyeLiner (w, FALSE, i);
8503bee1c92Smrg	    }
851a1d141d5Smrg	    x = y = 0;
852a1d141d5Smrg	    for (parent = (Widget) w; XtParent (parent); parent = XtParent (parent)) {
853a1d141d5Smrg	    	x += parent->core.x + parent->core.border_width;
854a1d141d5Smrg	    	x += parent->core.y + parent->core.border_width;
855a1d141d5Smrg	    }
856a1d141d5Smrg    	    XShapeCombineMask (XtDisplay (parent), XtWindow (parent), ShapeBounding,
857a1d141d5Smrg		       	       x, y, w->eyes.shape_mask, ShapeSet);
8582ddb6cf1Smrg	    XFreePixmap (dpy, w->eyes.shape_mask);
859a1d141d5Smrg    	}
8602ddb6cf1Smrg#ifdef XRENDER
8612ddb6cf1Smrg	if (w->eyes.render) {
8622ddb6cf1Smrg	    XRenderPictureAttributes pa;
8632ddb6cf1Smrg	    XRenderPictFormat *pf;
8642ddb6cf1Smrg	    pf = XRenderFindVisualFormat(dpy,
8652ddb6cf1Smrg					 DefaultVisualOfScreen(w->core.screen));
8662ddb6cf1Smrg	    if (pf)
86726df5c7cSmrg		w->eyes.picture = XRenderCreatePicture(dpy, EyesDrawable (w),
8682ddb6cf1Smrg						       pf, 0, &pa);
8692ddb6cf1Smrg	}
8702ddb6cf1Smrg#endif
871a1d141d5Smrg    }
872a1d141d5Smrg}
873a1d141d5Smrg
874a1d141d5Smrgstatic void Realize (
875a1d141d5Smrg     Widget gw,
876a1d141d5Smrg     XtValueMask *valueMask,
877a1d141d5Smrg     XSetWindowAttributes *attrs)
878a1d141d5Smrg{
879a1d141d5Smrg    EyesWidget	w = (EyesWidget)gw;
880a1d141d5Smrg
881a1d141d5Smrg    if (w->eyes.backing_store != Always + WhenMapped + NotUseful) {
882a1d141d5Smrg     	attrs->backing_store = w->eyes.backing_store;
883a1d141d5Smrg	*valueMask |= CWBackingStore;
884a1d141d5Smrg    }
885a1d141d5Smrg    XtCreateWindow( gw, (unsigned)InputOutput, (Visual *)CopyFromParent,
886a1d141d5Smrg		     *valueMask, attrs );
887a1d141d5Smrg    Resize (gw);
88826df5c7cSmrg
88926df5c7cSmrg    if (w->eyes.has_xi2)
89026df5c7cSmrg            xi2_add_root_listener(gw);
89126df5c7cSmrg    else
89226df5c7cSmrg            w->eyes.interval_id =
89326df5c7cSmrg                    XtAppAddTimeOut(XtWidgetToApplicationContext(gw),
89426df5c7cSmrg                                    delays[w->eyes.update], draw_it, (XtPointer)gw);
895a1d141d5Smrg}
896a1d141d5Smrg
897a1d141d5Smrgstatic void Destroy (Widget gw)
898a1d141d5Smrg{
899a1d141d5Smrg     EyesWidget w = (EyesWidget)gw;
9002ddb6cf1Smrg     int i;
901a1d141d5Smrg
902a1d141d5Smrg     if (w->eyes.interval_id)
903a1d141d5Smrg	XtRemoveTimeOut (w->eyes.interval_id);
9042ddb6cf1Smrg     for (i = 0; i < PART_MAX; i ++)
9052ddb6cf1Smrg	     XtReleaseGC(gw, w->eyes.gc[i]);
90626df5c7cSmrg     xi2_remove_root_listener(gw);
9072ddb6cf1Smrg#ifdef XRENDER
9082ddb6cf1Smrg     if (w->eyes.picture)
9092ddb6cf1Smrg	     XRenderFreePicture (XtDisplay(w), w->eyes.picture);
9102ddb6cf1Smrg#endif
911a1d141d5Smrg}
912a1d141d5Smrg
913a1d141d5Smrg/* ARGSUSED */
914a1d141d5Smrgstatic void Redisplay(
915a1d141d5Smrg     Widget gw,
916a1d141d5Smrg     XEvent *event,
917a1d141d5Smrg     Region region)
918a1d141d5Smrg{
919a1d141d5Smrg    EyesWidget	w;
920a1d141d5Smrg
921a1d141d5Smrg    w = (EyesWidget) gw;
9223bee1c92Smrg    for (int i = 0; i < w->eyes.configuration->count; i++) {
9233bee1c92Smrg        w->eyes.pupils[i].x = TPOINT_NONE;
9243bee1c92Smrg        w->eyes.pupils[i].y = TPOINT_NONE;
9253bee1c92Smrg    }
926a1d141d5Smrg    (void) repaint_window ((EyesWidget)gw);
927a1d141d5Smrg}
928a1d141d5Smrg
929a1d141d5Smrg/* ARGSUSED */
930a1d141d5Smrgstatic Boolean SetValues (
931a1d141d5Smrg    Widget current,
932a1d141d5Smrg    Widget request,
933a1d141d5Smrg    Widget new,
934a1d141d5Smrg    ArgList args,
935a1d141d5Smrg    Cardinal *num_args)
936a1d141d5Smrg{
937a1d141d5Smrg    return( FALSE );
938a1d141d5Smrg}
939a1d141d5Smrg
940a1d141d5SmrgEyesClassRec eyesClassRec = {
941a1d141d5Smrg    { /* core fields */
942a1d141d5Smrg    /* superclass		*/	&widgetClassRec,
94326df5c7cSmrg    /* class_name		*/	(char *) "Eyes",
944a1d141d5Smrg    /* size			*/	sizeof(EyesRec),
945a1d141d5Smrg    /* class_initialize		*/	ClassInitialize,
946a1d141d5Smrg    /* class_part_initialize	*/	NULL,
947a1d141d5Smrg    /* class_inited		*/	FALSE,
948a1d141d5Smrg    /* initialize		*/	Initialize,
949a1d141d5Smrg    /* initialize_hook		*/	NULL,
950a1d141d5Smrg    /* realize			*/	Realize,
951a1d141d5Smrg    /* actions			*/	NULL,
952a1d141d5Smrg    /* num_actions		*/	0,
953a1d141d5Smrg    /* resources		*/	resources,
954a1d141d5Smrg    /* num_resources		*/	XtNumber(resources),
955a1d141d5Smrg    /* xrm_class		*/	NULLQUARK,
956a1d141d5Smrg    /* compress_motion		*/	TRUE,
957a1d141d5Smrg    /* compress_exposure	*/	TRUE,
958a1d141d5Smrg    /* compress_enterleave	*/	TRUE,
959a1d141d5Smrg    /* visible_interest		*/	FALSE,
960a1d141d5Smrg    /* destroy			*/	Destroy,
961a1d141d5Smrg    /* resize			*/	Resize,
962a1d141d5Smrg    /* expose			*/	Redisplay,
963a1d141d5Smrg    /* set_values		*/	SetValues,
964a1d141d5Smrg    /* set_values_hook		*/	NULL,
965a1d141d5Smrg    /* set_values_almost	*/	NULL,
966a1d141d5Smrg    /* get_values_hook		*/	NULL,
967a1d141d5Smrg    /* accept_focus		*/	NULL,
968a1d141d5Smrg    /* version			*/	XtVersion,
969a1d141d5Smrg    /* callback_private		*/	NULL,
970a1d141d5Smrg    /* tm_table			*/	NULL,
971a1d141d5Smrg    /* query_geometry		*/	XtInheritQueryGeometry,
972a1d141d5Smrg    }
973a1d141d5Smrg};
974