summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSerge Bazanski <q3k@q3k.org>2023-05-28 14:21:56 +0200
committerSerge Bazanski <q3k@q3k.org>2023-05-28 14:28:58 +0200
commit6f6c975d2cdc1f615f83576c9d1f828e1cdabda3 (patch)
treefa3f59c025ea60b640416b9432a3d10f81048235
parentf30ef4e0f3e464f6304bdc85d28ebec0c2ba5e4f (diff)
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: Serge Bazanski <q3k@q3k.org>
-rw-r--r--Eyes.c157
-rw-r--r--Eyes.h1
-rw-r--r--EyesP.h21
-rw-r--r--man/xeyes.man3
-rw-r--r--xeyes.c2
5 files changed, 143 insertions, 41 deletions
diff --git a/Eyes.c b/Eyes.c
index aa54b1c..11a1f8e 100644
--- a/Eyes.c
+++ b/Eyes.c
@@ -1,6 +1,7 @@
/*
Copyright (c) 1991 X Consortium
+Copyright (c) 2023 q3k
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -49,6 +50,7 @@ from the X Consortium.
# include <X11/Xlibint.h>
# include <stdlib.h>
# include <X11/extensions/XInput2.h>
+# include <assert.h>
#define offset(field) XtOffsetOf(EyesRec, eyes.field)
#define goffset(field) XtOffsetOf(WidgetRec, core.field)
@@ -83,23 +85,19 @@ static XtResource resources[] = {
#endif
{(char *) XtNdistance, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean),
offset(distance), XtRImmediate, (XtPointer) FALSE },
+ {(char *) XtNbiblicallyAccurate, (char *) XtCBoolean, XtRBoolean, sizeof(Boolean),
+ offset(biblically_accurate), XtRImmediate, (XtPointer) FALSE },
};
#undef offset
#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_THICK (0.175) /* thickness of eye rim */
# define BALL_DIAM (0.3)
# define BALL_PAD (0.175)
# define EYE_DIAM (2.0 - (EYE_THICK + EYE_OFFSET) * 2)
# 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 TPointEqual(a, b) ((a).x == (b).x && (a).y == (b).y)
@@ -109,6 +107,69 @@ static XtResource resources[] = {
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)
{
XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
@@ -344,6 +405,17 @@ static void Initialize (
enum EyesPart i;
#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:
*
@@ -386,9 +458,6 @@ static void Initialize (
/* wait for Realize to add the timeout */
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;
if (w->eyes.shape_window && !XShapeQueryExtension (XtDisplay (w),
@@ -511,19 +580,20 @@ eyeLiner(EyesWidget w,
Boolean draw,
int num)
{
+ EyeLayout *l = &w->eyes.configuration->eyes[num];
drawEllipse(w, draw ? PART_OUTLINE : PART_SHAPE,
- EYE_X(num), EYE_Y(num),
+ l->x, l->y,
TPOINT_NONE, TPOINT_NONE,
EYE_DIAM + 2.0*EYE_THICK);
if (draw) {
- drawEllipse(w, PART_CENTER, EYE_X(num), EYE_Y(num),
+ drawEllipse(w, PART_CENTER, l->x, l->y,
TPOINT_NONE, TPOINT_NONE,
EYE_DIAM);
}
}
static TPoint computePupil (
- int num,
+ EyeLayout *layout,
TPoint mouse,
const TRectangle *screen)
{
@@ -534,8 +604,8 @@ static TPoint computePupil (
double cosa, sina;
TPoint ret;
- cx = EYE_X(num); dx = mouse.x - cx;
- cy = EYE_Y(num); dy = mouse.y - cy;
+ cx = layout->x; dx = mouse.x - cx;
+ cy = layout->y; dy = mouse.y - cy;
if (dx == 0 && dy == 0);
else {
angle = atan2 ((double) dy, (double) dx);
@@ -594,7 +664,7 @@ static TPoint computePupil (
static void computePupils (
EyesWidget w,
TPoint mouse,
- TPoint pupils[2])
+ TPoint *pupils)
{
TRectangle screen, *sp = NULL;
if (w->eyes.distance) {
@@ -610,8 +680,9 @@ static void computePupils (
&w->eyes.t);
sp = &screen;
}
- pupils[0] = computePupil (0, mouse, sp);
- pupils[1] = computePupil (1, mouse, sp);
+ for (int i = 0; i < w->eyes.configuration->count; i++) {
+ pupils[i] = computePupil(&w->eyes.configuration->eyes[i], mouse, sp);
+ }
}
static void
@@ -620,8 +691,9 @@ eyeBall(EyesWidget w,
TPoint *old,
int num)
{
+ //printf("eyeBall(_, %d, %p, %d)\n", draw, old, num);
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,
BALL_DIAM);
}
@@ -632,11 +704,13 @@ static void repaint_window (EyesWidget w)
#ifdef PRESENT
MakePresentData(w);
#endif
- eyeLiner (w, TRUE, 0);
- eyeLiner (w, TRUE, 1);
- computePupils (w, w->eyes.mouse, w->eyes.pupil);
- eyeBall (w, TRUE, NULL, 0);
- eyeBall (w, TRUE, NULL, 1);
+ for (int i = 0; i < w->eyes.configuration->count; i++) {
+ eyeLiner (w, TRUE, i);
+ }
+ computePupils (w, w->eyes.mouse, w->eyes.pupils);
+ for (int i = 0; i < w->eyes.configuration->count; i++) {
+ eyeBall (w, TRUE, NULL, i);
+ }
#ifdef PRESENT
UpdatePresent(w);
#endif
@@ -648,17 +722,17 @@ drawEye(EyesWidget w, TPoint newpupil, int num)
{
XPoint xnewpupil, xpupil;
- xpupil.x = Xx(w->eyes.pupil[num].x, w->eyes.pupil[num].y, &w->eyes.t);
- xpupil.y = Xy(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.pupils[num].x, w->eyes.pupils[num].y, &w->eyes.t);
xnewpupil.x = Xx(newpupil.x, newpupil.y, &w->eyes.t);
xnewpupil.y = Xy(newpupil.x, newpupil.y, &w->eyes.t);
if (
#ifdef XRENDER
- w->eyes.picture ? !TPointEqual(w->eyes.pupil[num], newpupil) :
+ w->eyes.picture ? !TPointEqual(w->eyes.pupils[num], newpupil) :
#endif
!XPointEqual(xpupil, xnewpupil)) {
- TPoint oldpupil = w->eyes.pupil[num];
- w->eyes.pupil[num] = newpupil;
+ TPoint oldpupil = w->eyes.pupils[num];
+ w->eyes.pupils[num] = newpupil;
eyeBall (w, TRUE, &oldpupil, num);
}
}
@@ -666,8 +740,8 @@ drawEye(EyesWidget w, TPoint newpupil, int num)
static void
drawEyes(EyesWidget w, TPoint mouse)
{
- TPoint newpupil[2];
int num;
+ TPoint newpupils[w->eyes.configuration->count];
#ifdef PRESENT
MakePresentData(w);
@@ -677,9 +751,9 @@ drawEyes(EyesWidget w, TPoint mouse)
++w->eyes.update;
return;
}
- computePupils (w, mouse, newpupil);
- for (num = 0; num < 2; num ++) {
- drawEye(w, newpupil[num], num);
+ computePupils (w, mouse, newpupils);
+ for (num = 0; num < w->eyes.configuration->count; num++) {
+ drawEye(w, newpupils[num], num);
}
w->eyes.mouse = mouse;
@@ -737,8 +811,10 @@ static void Resize (Widget gw)
SetTransform (&w->eyes.t,
0, w->core.width,
w->core.height, 0,
- W_MIN_X, W_MAX_X,
- W_MIN_Y, W_MAX_Y);
+ w->eyes.configuration->w_min_x,
+ w->eyes.configuration->w_max_x,
+ w->eyes.configuration->w_min_y,
+ w->eyes.configuration->w_max_y);
#ifdef PRESENT
if (w->eyes.back_buffer) {
xcb_free_pixmap(xt_xcb(w),
@@ -769,8 +845,9 @@ static void Resize (Widget gw)
XFillRectangle (dpy, w->eyes.shape_mask, w->eyes.gc[PART_SHAPE],
0, 0, w->core.width, w->core.height);
XSetForeground (dpy, w->eyes.gc[PART_SHAPE], 1);
- eyeLiner (w, FALSE, 0);
- eyeLiner (w, FALSE, 1);
+ for (int i = 0; i < w->eyes.configuration->count; i++) {
+ eyeLiner (w, FALSE, i);
+ }
x = y = 0;
for (parent = (Widget) w; XtParent (parent); parent = XtParent (parent)) {
x += parent->core.x + parent->core.border_width;
@@ -842,10 +919,10 @@ static void Redisplay(
EyesWidget w;
w = (EyesWidget) gw;
- w->eyes.pupil[0].x = TPOINT_NONE;
- w->eyes.pupil[0].y = TPOINT_NONE;
- w->eyes.pupil[1].x = TPOINT_NONE;
- w->eyes.pupil[1].y = TPOINT_NONE;
+ for (int i = 0; i < w->eyes.configuration->count; i++) {
+ w->eyes.pupils[i].x = TPOINT_NONE;
+ w->eyes.pupils[i].y = TPOINT_NONE;
+ }
(void) repaint_window ((EyesWidget)gw);
}
diff --git a/Eyes.h b/Eyes.h
index 6bac782..66b3880 100644
--- a/Eyes.h
+++ b/Eyes.h
@@ -34,6 +34,7 @@
#define XtNrender "render"
#define XtNdistance "distance"
+#define XtNbiblicallyAccurate "biblicallyAccurate"
#define XtNpresent "present"
diff --git a/EyesP.h b/EyesP.h
index ecd565b..fa7e940 100644
--- a/EyesP.h
+++ b/EyesP.h
@@ -18,6 +18,23 @@
#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 */
typedef struct {
Pixel pixel[PART_SHAPE];
@@ -28,7 +45,9 @@ typedef struct {
Boolean shape_window; /* use SetWindowShapeMask */
int update; /* current timeout index */
TPoint mouse; /* old mouse position */
- TPoint pupil[2]; /* pupil position */
+ Boolean biblically_accurate;
+ EyeConfiguration *configuration;
+ TPoint *pupils;
Transform t;
Transform maskt;
XtIntervalId interval_id;
diff --git a/man/xeyes.man b/man/xeyes.man
index 298e152..6696237 100644
--- a/man/xeyes.man
+++ b/man/xeyes.man
@@ -52,6 +52,9 @@ disables Xrender and draws traditional eyes.
.B \-distance
uses an alternative mapping, as if the eyes were set back from the screen, thus following the mouse more precisely.
.TP 8
+.B \-biblicallyAccurate
+renders the eyes as if they belonged to a biblically accurate angel.
+.TP 8
.B \-help
print a usage message and exit.
.TP 8
diff --git a/xeyes.c b/xeyes.c
index c5484a0..a9d484f 100644
--- a/xeyes.c
+++ b/xeyes.c
@@ -52,6 +52,7 @@ usage(int exitval)
" [-fg {color}] [-bg {color}] [-bd {color}] [-bw {pixels}]\n"
" [-shape | +shape] [-outline {color}] [-center {color}]\n"
" [-backing {backing-store}] [-distance]\n"
+ " [-biblicallyAccurate]\n"
#ifdef XRENDER
" [-render | +render]\n"
#endif
@@ -81,6 +82,7 @@ static XrmOptionDescRec options[] = {
{(char *)"+present", (char *)"*eyes.present", XrmoptionNoArg, (char *)"FALSE"},
#endif
{(char *)"-distance", (char *)"*eyes.distance", XrmoptionNoArg, (char *)"TRUE"},
+{(char *)"-biblicallyAccurate", (char *)"*eyes.biblicallyAccurate", XrmoptionNoArg, (char *)"TRUE"},
};
static Atom wm_delete_window;