Skip to content
Snippets Groups Projects
Commit 6f6c975d authored by Serge Bazanski's avatar Serge Bazanski
Browse files

Implement multi-ocular support, add biblical example


This removes the assumption that an xeyes instance displays just a pair
of eyes, and instead allows future developers to implement different
kinds of ocular layouts.

Currently, the ocular layout system only allows for specifying offsets,
but a future change might also make different parts of the eye geometry
configurable: size of different elements, padding, etc.

Signed-off-by: default avatarSerge Bazanski <q3k@q3k.org>
parent f30ef4e0
No related branches found
No related tags found
1 merge request!6Implement multi-ocular support, add biblical example
Pipeline #912305 passed
/* /*
Copyright (c) 1991 X Consortium Copyright (c) 1991 X Consortium
Copyright (c) 2023 q3k
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
...@@ -49,6 +50,7 @@ from the X Consortium. ...@@ -49,6 +50,7 @@ from the X Consortium.
# include <X11/Xlibint.h> # include <X11/Xlibint.h>
# include <stdlib.h> # include <stdlib.h>
# include <X11/extensions/XInput2.h> # include <X11/extensions/XInput2.h>
# include <assert.h>
#define offset(field) XtOffsetOf(EyesRec, eyes.field) #define offset(field) XtOffsetOf(EyesRec, eyes.field)
#define goffset(field) XtOffsetOf(WidgetRec, core.field) #define goffset(field) XtOffsetOf(WidgetRec, core.field)
...@@ -83,23 +85,19 @@ static XtResource resources[] = { ...@@ -83,23 +85,19 @@ static XtResource resources[] = {
#endif #endif
{(char *) XtNdistance, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean), {(char *) XtNdistance, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean),
offset(distance), XtRImmediate, (XtPointer) FALSE }, offset(distance), XtRImmediate, (XtPointer) FALSE },
{(char *) XtNbiblicallyAccurate, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean),
offset(biblically_accurate), XtRImmediate, (XtPointer) FALSE },
}; };
#undef offset #undef offset
#undef goffset #undef goffset
# define EYE_X(n) ((n) * 2.0)
# define EYE_Y(n) (0.0)
# define EYE_OFFSET (0.1) /* padding between eyes */ # define EYE_OFFSET (0.1) /* padding between eyes */
# define EYE_THICK (0.175) /* thickness of eye rim */ # define EYE_THICK (0.175) /* thickness of eye rim */
# define BALL_DIAM (0.3) # define BALL_DIAM (0.3)
# define BALL_PAD (0.175) # define BALL_PAD (0.175)
# define EYE_DIAM (2.0 - (EYE_THICK + EYE_OFFSET) * 2) # define EYE_DIAM (2.0 - (EYE_THICK + EYE_OFFSET) * 2)
# define BALL_DIST ((EYE_DIAM - BALL_DIAM) / 2.0 - BALL_PAD) # define BALL_DIST ((EYE_DIAM - BALL_DIAM) / 2.0 - BALL_PAD)
# define W_MIN_X (-1.0 + EYE_OFFSET)
# define W_MAX_X (3.0 - EYE_OFFSET)
# define W_MIN_Y (-1.0 + EYE_OFFSET)
# define W_MAX_Y (1.0 - EYE_OFFSET)
# define TPOINT_NONE (-1000) /* special value meaning "not yet set" */ # define TPOINT_NONE (-1000) /* special value meaning "not yet set" */
# define TPointEqual(a, b) ((a).x == (b).x && (a).y == (b).y) # define TPointEqual(a, b) ((a).x == (b).x && (a).y == (b).y)
...@@ -109,6 +107,69 @@ static XtResource resources[] = { ...@@ -109,6 +107,69 @@ static XtResource resources[] = {
static int delays[] = { 50, 100, 200, 400, 0 }; static int delays[] = { 50, 100, 200, 400, 0 };
static EyeLayout layout_standard[] = {
{ .x = 0.0, .y = 0.0, },
{ .x = 2.0, .y = 0.0, },
};
static EyeLayout layout_biblical[] = {
{ .x = 0.0+0.75, .y = 0.0, },
{ .x = 1.5+0.75, .y = 0.0, },
{ .x = 3.0+0.75, .y = 0.0, },
{ .x = 0.0+0.00, .y = 1.4, },
{ .x = 1.5+0.00, .y = 1.4, },
{ .x = 3.0+0.00, .y = 1.4, },
{ .x = 4.5+0.00, .y = 1.4, },
{ .x = 0.0+0.75, .y = 2.8, },
{ .x = 1.5+0.75, .y = 2.8, },
{ .x = 3.0+0.75, .y = 2.8, },
};
static EyeConfiguration *EyesConfigure(Boolean biblically_accurate)
{
EyeConfiguration *c = calloc(sizeof(EyeConfiguration), 1);
assert(c != NULL);
if (biblically_accurate) {
c->eyes = layout_biblical;
c->count = sizeof(layout_biblical) / sizeof(EyeLayout);
} else {
c->eyes = layout_standard;
c->count = sizeof(layout_standard) / sizeof(EyeLayout);
}
// Calculate the bounding box of the eyes.
c->w_min_x = c->eyes[0].x;
c->w_max_x = c->eyes[0].x;
c->w_min_y = c->eyes[0].y;
c->w_max_y = c->eyes[0].y;
for (int i = 0; i < c->count; i++) {
EyeLayout *l = &c->eyes[i];
if (l->x > c->w_max_x) {
c->w_max_x = l->x;
}
if (l->x < c->w_min_x) {
c->w_min_x = l->x;
}
if (l->y > c->w_max_y) {
c->w_max_y = l->y;
}
if (l->y < c->w_min_y) {
c->w_min_y = l->y;
}
}
// Add half size of eye (2.0) minus padding to each edge.
c->w_min_x -= (1.0 - EYE_OFFSET);
c->w_max_x += (1.0 - EYE_OFFSET);
c->w_min_y -= (1.0 - EYE_OFFSET);
c->w_max_y += (1.0 - EYE_OFFSET);
return c;
}
static void ClassInitialize(void) static void ClassInitialize(void)
{ {
XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore, XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
...@@ -344,6 +405,17 @@ static void Initialize ( ...@@ -344,6 +405,17 @@ static void Initialize (
enum EyesPart i; enum EyesPart i;
#endif #endif
EyeConfiguration *config = EyesConfigure(w->eyes.biblically_accurate);
TPoint *pupils = calloc(sizeof(TPoint), config->count);
assert(pupils != NULL);
for (int j = 0; j < config->count; j++) {
pupils[j].x = TPOINT_NONE;
pupils[j].y = TPOINT_NONE;
}
w->eyes.configuration = config;
w->eyes.pupils = pupils;
/* /*
* set the colors if reverse video; these are the colors used: * set the colors if reverse video; these are the colors used:
* *
...@@ -386,9 +458,6 @@ static void Initialize ( ...@@ -386,9 +458,6 @@ static void Initialize (
/* wait for Realize to add the timeout */ /* wait for Realize to add the timeout */
w->eyes.interval_id = 0; w->eyes.interval_id = 0;
w->eyes.pupil[0].x = w->eyes.pupil[1].x = TPOINT_NONE;
w->eyes.pupil[0].y = w->eyes.pupil[1].y = TPOINT_NONE;
w->eyes.mouse.x = w->eyes.mouse.y = TPOINT_NONE; w->eyes.mouse.x = w->eyes.mouse.y = TPOINT_NONE;
if (w->eyes.shape_window && !XShapeQueryExtension (XtDisplay (w), if (w->eyes.shape_window && !XShapeQueryExtension (XtDisplay (w),
...@@ -511,19 +580,20 @@ eyeLiner(EyesWidget w, ...@@ -511,19 +580,20 @@ eyeLiner(EyesWidget w,
Boolean draw, Boolean draw,
int num) int num)
{ {
EyeLayout *l = &w->eyes.configuration->eyes[num];
drawEllipse(w, draw ? PART_OUTLINE : PART_SHAPE, drawEllipse(w, draw ? PART_OUTLINE : PART_SHAPE,
EYE_X(num), EYE_Y(num), l->x, l->y,
TPOINT_NONE, TPOINT_NONE, TPOINT_NONE, TPOINT_NONE,
EYE_DIAM + 2.0*EYE_THICK); EYE_DIAM + 2.0*EYE_THICK);
if (draw) { if (draw) {
drawEllipse(w, PART_CENTER, EYE_X(num), EYE_Y(num), drawEllipse(w, PART_CENTER, l->x, l->y,
TPOINT_NONE, TPOINT_NONE, TPOINT_NONE, TPOINT_NONE,
EYE_DIAM); EYE_DIAM);
} }
} }
static TPoint computePupil ( static TPoint computePupil (
int num, EyeLayout *layout,
TPoint mouse, TPoint mouse,
const TRectangle *screen) const TRectangle *screen)
{ {
...@@ -534,8 +604,8 @@ static TPoint computePupil ( ...@@ -534,8 +604,8 @@ static TPoint computePupil (
double cosa, sina; double cosa, sina;
TPoint ret; TPoint ret;
cx = EYE_X(num); dx = mouse.x - cx; cx = layout->x; dx = mouse.x - cx;
cy = EYE_Y(num); dy = mouse.y - cy; cy = layout->y; dy = mouse.y - cy;
if (dx == 0 && dy == 0); if (dx == 0 && dy == 0);
else { else {
angle = atan2 ((double) dy, (double) dx); angle = atan2 ((double) dy, (double) dx);
...@@ -594,7 +664,7 @@ static TPoint computePupil ( ...@@ -594,7 +664,7 @@ static TPoint computePupil (
static void computePupils ( static void computePupils (
EyesWidget w, EyesWidget w,
TPoint mouse, TPoint mouse,
TPoint pupils[2]) TPoint *pupils)
{ {
TRectangle screen, *sp = NULL; TRectangle screen, *sp = NULL;
if (w->eyes.distance) { if (w->eyes.distance) {
...@@ -610,8 +680,9 @@ static void computePupils ( ...@@ -610,8 +680,9 @@ static void computePupils (
&w->eyes.t); &w->eyes.t);
sp = &screen; sp = &screen;
} }
pupils[0] = computePupil (0, mouse, sp); for (int i = 0; i < w->eyes.configuration->count; i++) {
pupils[1] = computePupil (1, mouse, sp); pupils[i] = computePupil(&w->eyes.configuration->eyes[i], mouse, sp);
}
} }
static void static void
...@@ -620,8 +691,9 @@ eyeBall(EyesWidget w, ...@@ -620,8 +691,9 @@ eyeBall(EyesWidget w,
TPoint *old, TPoint *old,
int num) int num)
{ {
//printf("eyeBall(_, %d, %p, %d)\n", draw, old, num);
drawEllipse(w, draw ? PART_PUPIL : PART_CLEAR, drawEllipse(w, draw ? PART_PUPIL : PART_CLEAR,
w->eyes.pupil[num].x, w->eyes.pupil[num].y, w->eyes.pupils[num].x, w->eyes.pupils[num].y,
old ? old->x : TPOINT_NONE, old ? old->y : TPOINT_NONE, old ? old->x : TPOINT_NONE, old ? old->y : TPOINT_NONE,
BALL_DIAM); BALL_DIAM);
} }
...@@ -632,11 +704,13 @@ static void repaint_window (EyesWidget w) ...@@ -632,11 +704,13 @@ static void repaint_window (EyesWidget w)
#ifdef PRESENT #ifdef PRESENT
MakePresentData(w); MakePresentData(w);
#endif #endif
eyeLiner (w, TRUE, 0); for (int i = 0; i < w->eyes.configuration->count; i++) {
eyeLiner (w, TRUE, 1); eyeLiner (w, TRUE, i);
computePupils (w, w->eyes.mouse, w->eyes.pupil); }
eyeBall (w, TRUE, NULL, 0); computePupils (w, w->eyes.mouse, w->eyes.pupils);
eyeBall (w, TRUE, NULL, 1); for (int i = 0; i < w->eyes.configuration->count; i++) {
eyeBall (w, TRUE, NULL, i);
}
#ifdef PRESENT #ifdef PRESENT
UpdatePresent(w); UpdatePresent(w);
#endif #endif
...@@ -648,17 +722,17 @@ drawEye(EyesWidget w, TPoint newpupil, int num) ...@@ -648,17 +722,17 @@ drawEye(EyesWidget w, TPoint newpupil, int num)
{ {
XPoint xnewpupil, xpupil; XPoint xnewpupil, xpupil;
xpupil.x = Xx(w->eyes.pupil[num].x, w->eyes.pupil[num].y, &w->eyes.t); xpupil.x = Xx(w->eyes.pupils[num].x, w->eyes.pupils[num].y, &w->eyes.t);
xpupil.y = Xy(w->eyes.pupil[num].x, w->eyes.pupil[num].y, &w->eyes.t); xpupil.y = Xy(w->eyes.pupils[num].x, w->eyes.pupils[num].y, &w->eyes.t);
xnewpupil.x = Xx(newpupil.x, newpupil.y, &w->eyes.t); xnewpupil.x = Xx(newpupil.x, newpupil.y, &w->eyes.t);
xnewpupil.y = Xy(newpupil.x, newpupil.y, &w->eyes.t); xnewpupil.y = Xy(newpupil.x, newpupil.y, &w->eyes.t);
if ( if (
#ifdef XRENDER #ifdef XRENDER
w->eyes.picture ? !TPointEqual(w->eyes.pupil[num], newpupil) : w->eyes.picture ? !TPointEqual(w->eyes.pupils[num], newpupil) :
#endif #endif
!XPointEqual(xpupil, xnewpupil)) { !XPointEqual(xpupil, xnewpupil)) {
TPoint oldpupil = w->eyes.pupil[num]; TPoint oldpupil = w->eyes.pupils[num];
w->eyes.pupil[num] = newpupil; w->eyes.pupils[num] = newpupil;
eyeBall (w, TRUE, &oldpupil, num); eyeBall (w, TRUE, &oldpupil, num);
} }
} }
...@@ -666,8 +740,8 @@ drawEye(EyesWidget w, TPoint newpupil, int num) ...@@ -666,8 +740,8 @@ drawEye(EyesWidget w, TPoint newpupil, int num)
static void static void
drawEyes(EyesWidget w, TPoint mouse) drawEyes(EyesWidget w, TPoint mouse)
{ {
TPoint newpupil[2];
int num; int num;
TPoint newpupils[w->eyes.configuration->count];
#ifdef PRESENT #ifdef PRESENT
MakePresentData(w); MakePresentData(w);
...@@ -677,9 +751,9 @@ drawEyes(EyesWidget w, TPoint mouse) ...@@ -677,9 +751,9 @@ drawEyes(EyesWidget w, TPoint mouse)
++w->eyes.update; ++w->eyes.update;
return; return;
} }
computePupils (w, mouse, newpupil); computePupils (w, mouse, newpupils);
for (num = 0; num < 2; num ++) { for (num = 0; num < w->eyes.configuration->count; num++) {
drawEye(w, newpupil[num], num); drawEye(w, newpupils[num], num);
} }
w->eyes.mouse = mouse; w->eyes.mouse = mouse;
...@@ -737,8 +811,10 @@ static void Resize (Widget gw) ...@@ -737,8 +811,10 @@ static void Resize (Widget gw)
SetTransform (&w->eyes.t, SetTransform (&w->eyes.t,
0, w->core.width, 0, w->core.width,
w->core.height, 0, w->core.height, 0,
W_MIN_X, W_MAX_X, w->eyes.configuration->w_min_x,
W_MIN_Y, W_MAX_Y); w->eyes.configuration->w_max_x,
w->eyes.configuration->w_min_y,
w->eyes.configuration->w_max_y);
#ifdef PRESENT #ifdef PRESENT
if (w->eyes.back_buffer) { if (w->eyes.back_buffer) {
xcb_free_pixmap(xt_xcb(w), xcb_free_pixmap(xt_xcb(w),
...@@ -769,8 +845,9 @@ static void Resize (Widget gw) ...@@ -769,8 +845,9 @@ static void Resize (Widget gw)
XFillRectangle (dpy, w->eyes.shape_mask, w->eyes.gc[PART_SHAPE], XFillRectangle (dpy, w->eyes.shape_mask, w->eyes.gc[PART_SHAPE],
0, 0, w->core.width, w->core.height); 0, 0, w->core.width, w->core.height);
XSetForeground (dpy, w->eyes.gc[PART_SHAPE], 1); XSetForeground (dpy, w->eyes.gc[PART_SHAPE], 1);
eyeLiner (w, FALSE, 0); for (int i = 0; i < w->eyes.configuration->count; i++) {
eyeLiner (w, FALSE, 1); eyeLiner (w, FALSE, i);
}
x = y = 0; x = y = 0;
for (parent = (Widget) w; XtParent (parent); parent = XtParent (parent)) { for (parent = (Widget) w; XtParent (parent); parent = XtParent (parent)) {
x += parent->core.x + parent->core.border_width; x += parent->core.x + parent->core.border_width;
...@@ -842,10 +919,10 @@ static void Redisplay( ...@@ -842,10 +919,10 @@ static void Redisplay(
EyesWidget w; EyesWidget w;
w = (EyesWidget) gw; w = (EyesWidget) gw;
w->eyes.pupil[0].x = TPOINT_NONE; for (int i = 0; i < w->eyes.configuration->count; i++) {
w->eyes.pupil[0].y = TPOINT_NONE; w->eyes.pupils[i].x = TPOINT_NONE;
w->eyes.pupil[1].x = TPOINT_NONE; w->eyes.pupils[i].y = TPOINT_NONE;
w->eyes.pupil[1].y = TPOINT_NONE; }
(void) repaint_window ((EyesWidget)gw); (void) repaint_window ((EyesWidget)gw);
} }
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#define XtNrender "render" #define XtNrender "render"
#define XtNdistance "distance" #define XtNdistance "distance"
#define XtNbiblicallyAccurate "biblicallyAccurate"
#define XtNpresent "present" #define XtNpresent "present"
......
...@@ -18,6 +18,23 @@ ...@@ -18,6 +18,23 @@
#define SEG_BUFF_SIZE 128 #define SEG_BUFF_SIZE 128
typedef struct {
// X offset
double x;
// Y offset
double y;
} EyeLayout;
typedef struct {
EyeLayout *eyes;
int count;
double w_min_x;
double w_max_x;
double w_min_y;
double w_max_y;
} EyeConfiguration;
/* New fields for the eyes widget instance record */ /* New fields for the eyes widget instance record */
typedef struct { typedef struct {
Pixel pixel[PART_SHAPE]; Pixel pixel[PART_SHAPE];
...@@ -28,7 +45,9 @@ typedef struct { ...@@ -28,7 +45,9 @@ typedef struct {
Boolean shape_window; /* use SetWindowShapeMask */ Boolean shape_window; /* use SetWindowShapeMask */
int update; /* current timeout index */ int update; /* current timeout index */
TPoint mouse; /* old mouse position */ TPoint mouse; /* old mouse position */
TPoint pupil[2]; /* pupil position */ Boolean biblically_accurate;
EyeConfiguration *configuration;
TPoint *pupils;
Transform t; Transform t;
Transform maskt; Transform maskt;
XtIntervalId interval_id; XtIntervalId interval_id;
......
...@@ -52,6 +52,9 @@ disables Xrender and draws traditional eyes. ...@@ -52,6 +52,9 @@ disables Xrender and draws traditional eyes.
.B \-distance .B \-distance
uses an alternative mapping, as if the eyes were set back from the screen, thus following the mouse more precisely. uses an alternative mapping, as if the eyes were set back from the screen, thus following the mouse more precisely.
.TP 8 .TP 8
.B \-biblicallyAccurate
renders the eyes as if they belonged to a biblically accurate angel.
.TP 8
.B \-help .B \-help
print a usage message and exit. print a usage message and exit.
.TP 8 .TP 8
......
...@@ -52,6 +52,7 @@ usage(int exitval) ...@@ -52,6 +52,7 @@ usage(int exitval)
" [-fg {color}] [-bg {color}] [-bd {color}] [-bw {pixels}]\n" " [-fg {color}] [-bg {color}] [-bd {color}] [-bw {pixels}]\n"
" [-shape | +shape] [-outline {color}] [-center {color}]\n" " [-shape | +shape] [-outline {color}] [-center {color}]\n"
" [-backing {backing-store}] [-distance]\n" " [-backing {backing-store}] [-distance]\n"
" [-biblicallyAccurate]\n"
#ifdef XRENDER #ifdef XRENDER
" [-render | +render]\n" " [-render | +render]\n"
#endif #endif
...@@ -81,6 +82,7 @@ static XrmOptionDescRec options[] = { ...@@ -81,6 +82,7 @@ static XrmOptionDescRec options[] = {
{(char *)"+present", (char *)"*eyes.present", XrmoptionNoArg, (char *)"FALSE"}, {(char *)"+present", (char *)"*eyes.present", XrmoptionNoArg, (char *)"FALSE"},
#endif #endif
{(char *)"-distance", (char *)"*eyes.distance", XrmoptionNoArg, (char *)"TRUE"}, {(char *)"-distance", (char *)"*eyes.distance", XrmoptionNoArg, (char *)"TRUE"},
{(char *)"-biblicallyAccurate", (char *)"*eyes.biblicallyAccurate", XrmoptionNoArg, (char *)"TRUE"},
}; };
static Atom wm_delete_window; static Atom wm_delete_window;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment