-/* xscreensaver, Copyright (c) 2003, 2005 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2003-2014 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* Requires a system with scalable fonts. (X's font handing sucks. A lot.)
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#undef DEBUG
+
#include <math.h>
+
+#ifndef HAVE_COCOA
+# include <X11/Intrinsic.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
#include "screenhack.h"
-#include <X11/Intrinsic.h>
+#include "textclient.h"
#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
#include "xdbe.h"
#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
-extern XtAppContext app;
-
-
typedef struct {
char *text;
int x, y, width, height;
#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
XdbeBackBuffer backb;
+ Bool dbeclear_p;
#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
Bool dbuf; /* Whether we're using double buffering. */
- Bool dbeclear_p; /* ? */
int border_width; /* size of the font outline */
char *charset; /* registry and encoding for font lookups */
double speed; /* frame rate multiplier */
double linger; /* multiplier for how long to leave words on screen */
Bool trails_p;
- Bool debug_p;
enum { PAGE, SCROLL } mode;
char *font_override; /* if -font was specified on the cmd line */
- FILE *pipe;
- XtInputId pipe_id;
- Time subproc_relaunch_delay;
- Bool input_available_p;
-
char buf [40]; /* this only needs to be as big as one "word". */
int buf_tail;
+ Bool early_p;
+ time_t start_time;
int nsentences;
sentence **sentences;
Bool spawn_p; /* whether it is time to create a new sentence */
int latest_sentence;
+ unsigned long frame_delay;
+ int id_tick;
+ text_data *tc;
+
+# ifdef DEBUG
+ Bool debug_p;
+ int debug_metrics_p, debug_metrics_antialiasing_p;
+ int debug_scale;
+# endif /* DEBUG */
} state;
-static void launch_text_generator (state *);
static void drain_input (state *s);
+static int
+pick_font_size (state *s)
+{
+ double scale = s->xgwa.height / 1024.0; /* shrink for small windows */
+ int min, max, r, pixel;
+
+ min = scale * 24;
+ max = scale * 260;
+
+ if (min < 10) min = 10;
+ if (max < 30) max = 30;
+
+ r = ((max-min)/3)+1;
+
+ pixel = min + ((random() % r) + (random() % r) + (random() % r));
+
+ if (s->mode == SCROLL) /* scroll mode likes bigger fonts */
+ pixel *= 1.5;
+
+ return pixel;
+}
+
+
/* Finds the set of scalable fonts on the system; picks one;
and loads that font in a random pixel size.
Returns False if something went wrong.
static Bool
pick_font_1 (state *s, sentence *se)
{
+ Bool ok = False;
char pattern[1024];
+
+# ifndef HAVE_COCOA /* real Xlib */
char **names = 0;
char **names2 = 0;
XFontStruct *info = 0;
int count = 0, count2 = 0;
int i;
- Bool ok = False;
if (se->font)
{
#undef INT
#undef STR
- {
- double scale = s->xgwa.height / 1024.0; /* shrink for small windows */
- int min, max, r;
-
- min = scale * 24;
- max = scale * 260;
-
- if (min < 10) min = 10;
- if (max < 30) max = 30;
-
- r = ((max-min)/3)+1;
-
- pixel = min + ((random() % r) + (random() % r) + (random() % r));
-
- if (s->mode == SCROLL) /* scroll mode likes bigger fonts */
- pixel *= 1.5;
- }
+ pixel = pick_font_size (s);
#if 0
/* Occasionally change the aspect ratio of the font, by increasing
XFreeFontInfo (names2, info, count2);
XFreeFontNames (names);
+# else /* HAVE_COCOA */
+
+ if (s->font_override)
+ sprintf (pattern, "%.200s", s->font_override);
+ else
+ {
+ const char *family = "random";
+ const char *weight = ((random() % 2) ? "normal" : "bold");
+ const char *slant = ((random() % 2) ? "o" : "r");
+ int size = 10 * pick_font_size (s);
+ sprintf (pattern, "*-%s-%s-%s-*-%d-*", family, weight, slant, size);
+ }
+ ok = True;
+# endif /* HAVE_COCOA */
+
if (! ok) return False;
se->font = XLoadQueryFont (s->dpy, pattern);
+
+# ifdef DEBUG
if (! se->font)
{
- fprintf (stderr, "%s: unable to load font %s\n",
- progname, pattern);
+ if (s->debug_p)
+ fprintf (stderr, "%s: unable to load font %s\n",
+ progname, pattern);
return False;
}
if (s->debug_p)
fprintf(stderr, "%s: %s\n", progname, pattern);
+# endif /* DEBUG */
se->font_name = strdup (pattern);
XSetFont (s->dpy, se->fg_gc, se->font->fid);
for (i = 0; i < 20; i++)
if (pick_font_1 (s, se))
return;
- fprintf (stderr, "%s: too many failures: giving up!\n", progname);
+ fprintf (stderr, "%s: too many font-loading failures: giving up!\n", progname);
exit (1);
}
/* Returns a newly-allocated string with one word in it, or NULL if there
is no complete word available.
*/
-static char *
+static const char *
get_word_text (state *s)
{
- char *start = s->buf;
- char *end;
+ const char *start = s->buf;
+ const char *end;
char *result = 0;
int lfs = 0;
+ drain_input (s);
+
+ /* If we just launched, and haven't had any text yet, and it has been
+ more than 2 seconds since we launched, then push out "Loading..."
+ as our first text. So if the text source is speedy, just use that.
+ But if we'd display a blank screen for a while, give 'em something
+ to see.
+ */
+ if (s->early_p &&
+ !*s->buf &&
+ !unread_word_text &&
+ s->start_time < ((time ((time_t *) 0) - 2)))
+ {
+ unread_word_text = "Loading...";
+ s->early_p = False;
+ }
+
if (unread_word_text)
{
- char *s = unread_word_text;
+ start = unread_word_text;
unread_word_text = 0;
- return s;
+ return start;
}
/* Skip over whitespace at the beginning of the buffer,
s->buf_tail -= n;
}
- /* See if there is more to be read, now that there's room in the buffer. */
- drain_input (s);
-
return result;
}
/* Gets some random text, and creates a "word" object from it.
*/
static word *
-new_word (state *s, sentence *se, char *txt, Bool alloc_p)
+new_word (state *s, sentence *se, const char *txt, Bool alloc_p)
{
word *w;
XCharStruct overall;
int dir, ascent, descent;
int bw = s->border_width;
+ int slack;
if (!txt)
return 0;
w = (word *) calloc (1, sizeof(*w));
XTextExtents (se->font, txt, strlen(txt), &dir, &ascent, &descent, &overall);
- w->width = overall.rbearing - overall.lbearing + bw + bw;
- w->height = overall.ascent + overall.descent + bw + bw;
+ /* Leave a little more slack. Not entirely clear on what's going on here,
+ but maybe it's fonts with goofy metrics. */
+ slack = (overall.ascent + overall.descent) * 0.25;
+ if (slack < bw*2) slack = bw*2;
+ overall.lbearing -= slack;
+ overall.rbearing += slack;
+ overall.ascent += slack;
+ overall.descent += slack;
+
+ w->width = overall.rbearing - overall.lbearing;
+ w->height = overall.ascent + overall.descent;
w->ascent = overall.ascent + bw;
w->lbearing = overall.lbearing - bw;
w->rbearing = overall.width + bw;
XGCValues gcv;
GC gc0, gc1;
+ if (w->width <= 0) w->width = 1;
+ if (w->height <= 0) w->height = 1;
+
w->pixmap = XCreatePixmap (s->dpy, s->b, w->width, w->height, 1L);
w->mask = XCreatePixmap (s->dpy, s->b, w->width, w->height, 1L);
XFillRectangle (s->dpy, w->mask, gc0, 0, 0, w->width, w->height);
XFillRectangle (s->dpy, w->pixmap, gc0, 0, 0, w->width, w->height);
+# ifdef DEBUG
if (s->debug_p)
{
/* bounding box (behind the characters) */
0, 0, w->width-1, w->height-1);
}
+ if (s->debug_p > 1)
+ {
+ /* bounding box (behind *each* character) */
+ char *ss;
+ int x = 0;
+ for (ss = txt; *ss; ss++)
+ {
+ XTextExtents (se->font, ss, 1, &dir, &ascent, &descent, &overall);
+ XDrawRectangle (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
+ x, w->ascent - overall.ascent,
+ overall.width,
+ overall.ascent + overall.descent);
+ XDrawRectangle (s->dpy, w->mask, gc1,
+ x, w->ascent - overall.ascent,
+ overall.width,
+ overall.ascent + overall.descent);
+
+ XDrawRectangle (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
+ x - overall.lbearing, w->ascent - overall.ascent,
+ overall.rbearing,
+ overall.ascent + overall.descent);
+ XDrawRectangle (s->dpy, w->mask, gc1,
+ x - overall.lbearing, w->ascent - overall.ascent,
+ overall.rbearing,
+ overall.ascent + overall.descent);
+
+
+ x += overall.width;
+ }
+ }
+# endif /* DEBUG */
+
/* Draw foreground text */
XDrawString (s->dpy, w->pixmap, gc1, -w->lbearing, w->ascent,
txt, strlen(txt));
0, 0, w->width, w->height,
i, j);
+# ifdef DEBUG
if (s->debug_p)
{
- XSetFunction (s->dpy, gc1, GXset);
+ XSetFunction (s->dpy, gc1, GXcopy);
if (w->ascent != w->height)
{
/* baseline (on top of the characters) */
w->rbearing, 0, w->rbearing, w->height-1);
}
}
+# endif /* DEBUG */
XFreeGC (s->dpy, gc0);
XFreeGC (s->dpy, gc1);
}
- w->text = txt;
+ w->text = strdup (txt);
return w;
}
static sentence *
-new_sentence (state *s)
+new_sentence (state *st, state *s)
{
- static int id = 0;
XGCValues gcv;
sentence *se = (sentence *) calloc (1, sizeof (*se));
se->fg_gc = XCreateGC (s->dpy, s->b, 0, &gcv);
se->anim_state = IN;
- se->id = ++id;
+ se->id = ++st->id_tick;
return se;
}
for (k = 0; k < L; k++)
{
- char *t2 = malloc (2);
+ char t2[2];
word *w2;
int xoff, yoff;
break;
}
+# ifdef DEBUG
if (s->debug_p)
se->dark_p = (se->fg.red*2 + se->fg.green*3 + se->fg.blue <
se->bg.red*2 + se->bg.green*3 + se->bg.blue);
+# endif /* DEBUG */
if (XAllocColor (s->dpy, s->xgwa.colormap, &se->fg))
XSetForeground (s->dpy, se->fg_gc, se->fg.pixel);
while (!done)
{
- char *txt = get_word_text (s);
+ const char *txt = get_word_text (s);
word *w;
if (!txt)
{
y + se->font->ascent > s->xgwa.height)
{
unread_word (s, w);
- done = True;
+ /* done = True; */
break;
}
}
fprintf (stderr, " %s", se->words[i]->text);
fprintf (stderr, "\n");
}
-# endif
+# endif /* DEBUG */
}
sentence *se = s->sentences[i];
if (! se)
{
- se = new_sentence (s);
+ se = new_sentence (s, s);
populate_sentence (s, se);
if (se->nwords > 0)
s->spawn_p = False, any = True;
progname, se->id,
se->words[0]->x + se->width,
rand_p ? " randomly" : "");
-# endif
+# endif /* DEBUG */
}
}
}
# ifdef DEBUG
if (s->debug_p)
fprintf (stderr, "%s: PAUSE %d\n", progname, se->id);
-# endif
+# endif /* DEBUG */
break;
case PAUSE:
if (--se->pause_tick <= 0)
# ifdef DEBUG
if (s->debug_p)
fprintf (stderr, "%s: OUT %d\n", progname, se->id);
-# endif
+# endif /* DEBUG */
}
break;
case OUT:
# ifdef DEBUG
if (s->debug_p)
fprintf (stderr, "%s: DEAD %d\n", progname, se->id);
-# endif
+# endif /* DEBUG */
{
int j;
for (j = 0; j < s->nsentences; j++)
}
-/* Render all the words to the screen, and run the animation one step.
- Clear screen first, swap buffers after.
- */
-static void
-draw_fontglide (state *s)
+#ifdef DEBUG
+
+static Pixmap
+scale_ximage (Screen *screen, Window window, XImage *img, int scale,
+ int margin)
{
- int i;
+ Display *dpy = DisplayOfScreen (screen);
+ int x, y;
+ unsigned width = img->width, height = img->height;
+ Pixmap p2;
+ GC gc;
+
+ p2 = XCreatePixmap (dpy, window, width*scale, height*scale, img->depth);
+ gc = XCreateGC (dpy, p2, 0, 0);
+
+ XSetForeground (dpy, gc, BlackPixelOfScreen (screen));
+ XFillRectangle (dpy, p2, gc, 0, 0, width*scale, height*scale);
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ XSetForeground (dpy, gc, XGetPixel (img, x, y));
+ XFillRectangle (dpy, p2, gc, x*scale, y*scale, scale, scale);
+ }
- if (s->spawn_p)
- more_sentences (s);
+ if (scale > 2)
+ {
+ XWindowAttributes xgwa;
+ XColor c;
+ c.red = c.green = c.blue = 0x4444;
+ c.flags = DoRed|DoGreen|DoBlue;
+ XGetWindowAttributes (dpy, window, &xgwa);
+ if (! XAllocColor (dpy, xgwa.colormap, &c)) abort();
+ XSetForeground (dpy, gc, c.pixel);
+ XDrawRectangle (dpy, p2, gc, 0, 0, width*scale-1, height*scale-1);
+ XDrawRectangle (dpy, p2, gc, margin*scale, margin*scale,
+ width*scale-1, height*scale-1);
+ for (y = 0; y <= height - 2*margin; y++)
+ XDrawLine (dpy, p2, gc,
+ margin*scale, (y+margin)*scale-1,
+ (width-margin)*scale, (y+margin)*scale-1);
+ for (x = 0; x <= width - 2*margin; x++)
+ XDrawLine (dpy, p2, gc,
+ (x+margin)*scale-1, margin*scale,
+ (x+margin)*scale-1, (height-margin)*scale);
+ XFreeColors (dpy, xgwa.colormap, &c.pixel, 1, 0);
+ }
- if (!s->trails_p)
- XFillRectangle (s->dpy, s->b, s->bg_gc,
- 0, 0, s->xgwa.width, s->xgwa.height);
+ XFreeGC (dpy, gc);
+ return p2;
+}
- for (i = 0; i < s->nsentences; i++)
- draw_sentence (s, s->sentences[i]);
-#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
- if (s->backb)
- {
- XdbeSwapInfo info[1];
- info[0].swap_window = s->window;
- info[0].swap_action = (s->dbeclear_p ? XdbeBackground : XdbeUndefined);
- XdbeSwapBuffers (s->dpy, info, 1);
- }
- else
-#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
- if (s->dbuf)
+static int check_edge (Display *dpy, Drawable p, GC gc,
+ unsigned msg_x, unsigned msg_y, const char *msg,
+ XImage *img,
+ unsigned x, unsigned y, unsigned dim, unsigned end)
+{
+ unsigned pt[2];
+ pt[0] = x;
+ pt[1] = y;
+ end += pt[dim];
+
+ for (;;)
{
- XCopyArea (s->dpy, s->b, s->window, s->bg_gc,
- 0, 0, s->xgwa.width, s->xgwa.height, 0, 0);
+ if (pt[dim] == end)
+ {
+ XDrawString (dpy, p, gc, msg_x, msg_y, msg, strlen (msg));
+ return 1;
+ }
+
+ if (XGetPixel(img, pt[0], pt[1]) & 0xffffff)
+ break;
+
+ ++pt[dim];
}
+
+ return 0;
}
-static void
-handle_events (state *s)
+static unsigned long
+fontglide_draw_metrics (state *s)
{
- while (XPending (s->dpy))
- {
- XEvent event;
- XNextEvent (s->dpy, &event);
+ unsigned int margin = (s->debug_metrics_antialiasing_p ? 2 : 0);
+
+ char txt[2], txt2[80];
+ const char *fn = (s->font_override ? s->font_override : "fixed");
+ XFontStruct *font = XLoadQueryFont (s->dpy, fn);
+ XFontStruct *font2 = XLoadQueryFont (s->dpy, "fixed");
+ XCharStruct c, overall;
+ int dir, ascent, descent;
+ int x, y;
+ int sc = s->debug_scale;
+ GC gc;
+ unsigned long red = 0xFFFF0000; /* so shoot me */
+ unsigned long green = 0xFF00FF00;
+ unsigned long blue = 0xFF6666FF;
+ unsigned long yellow = 0xFFFFFF00;
+ unsigned long cyan = 0xFF004040;
+ int i;
+ Drawable dest = s->b ? s->b : s->window;
+
+ if (sc < 1) sc = 1;
+
+ txt[0] = s->debug_metrics_p;
+ txt[1] = 0;
+
+ gc = XCreateGC (s->dpy, dest, 0, 0);
+ XSetFont (s->dpy, gc, font->fid);
+
+# ifdef HAVE_COCOA
+ jwxyz_XSetAntiAliasing (s->dpy, gc, False);
+# endif
+
+ XTextExtents (font, txt, strlen(txt),
+ &dir, &ascent, &descent, &overall);
+ c = (s->debug_metrics_p >= font->min_char_or_byte2
+ ? font->per_char[s->debug_metrics_p - font->min_char_or_byte2]
+ : overall);
+
+ XSetForeground (s->dpy, gc, BlackPixelOfScreen (s->xgwa.screen));
+ XFillRectangle (s->dpy, dest, gc, 0, 0, s->xgwa.width, s->xgwa.height);
- if (event.xany.type == ConfigureNotify)
+ x = (s->xgwa.width - overall.width) / 2;
+ y = (s->xgwa.height - (2 * sc * (ascent + descent))) / 2;
+
+ for (i = 0; i < 2; i++)
+ {
+ XCharStruct cc = (i == 0 ? c : overall);
+ int x1 = s->xgwa.width * 0.15;
+ int x2 = s->xgwa.width * 0.85;
+ int x3 = s->xgwa.width;
+
+ int pixw = margin * 2 + cc.rbearing - cc.lbearing;
+ int pixh = margin * 2 + cc.ascent + cc.descent;
+ Pixmap p = (pixw > 0 && pixh > 0
+ ? XCreatePixmap (s->dpy, dest, pixw, pixh, s->xgwa.depth)
+ : 0);
+
+ if (p)
{
- XGetWindowAttributes (s->dpy, s->window, &s->xgwa);
+ Pixmap p2;
+ GC gc2 = XCreateGC (s->dpy, p, 0, 0);
+# ifdef HAVE_COCOA
+ jwxyz_XSetAntiAliasing (s->dpy, gc2, False);
+# endif
+ XSetFont (s->dpy, gc2, font->fid);
+ XSetForeground (s->dpy, gc2, BlackPixelOfScreen (s->xgwa.screen));
+ XFillRectangle (s->dpy, p, gc2, 0, 0, pixw, pixh);
+ XSetForeground (s->dpy, gc, WhitePixelOfScreen (s->xgwa.screen));
+ XSetForeground (s->dpy, gc2, WhitePixelOfScreen (s->xgwa.screen));
+# ifdef HAVE_COCOA
+ jwxyz_XSetAntiAliasing (s->dpy, gc2, s->debug_metrics_antialiasing_p);
+# endif
+ XDrawString (s->dpy, p, gc2,
+ -cc.lbearing + margin,
+ cc.ascent + margin,
+ txt, strlen(txt));
+ {
+ unsigned x2, y2;
+ XImage *img = XGetImage (s->dpy, p, 0, 0, pixw, pixh,
+ ~0L, ZPixmap);
+ XImage *img2;
- if (s->dbuf && (s->ba))
- {
- XFreePixmap (s->dpy, s->ba);
- s->ba = XCreatePixmap (s->dpy, s->window,
- s->xgwa.width, s->xgwa.height,
- s->xgwa.depth);
- XFillRectangle (s->dpy, s->ba, s->bg_gc, 0, 0,
- s->xgwa.width, s->xgwa.height);
- s->b = s->ba;
- }
+ if (i == 1)
+ {
+ unsigned w = pixw - margin * 2, h = pixh - margin * 2;
+
+ if (margin > 0)
+ {
+ /* Check for ink escape. */
+ unsigned long ink = 0;
+ for (y2 = 0; y2 != pixh; ++y2)
+ for (x2 = 0; x2 != pixw; ++x2)
+ {
+ /* Sloppy... */
+ if (! (x2 >= margin &&
+ x2 < pixw - margin &&
+ y2 >= margin &&
+ y2 < pixh - margin))
+ ink |= XGetPixel (img, x2, y2);
+ }
+
+ if (ink & 0xFFFFFF)
+ {
+ XSetFont (s->dpy, gc, font2->fid);
+ XDrawString (s->dpy, dest, gc, 10, 40, "Ink escape!", 11);
+ }
+ }
+
+ /* ...And wasted space. */
+ if (w && h)
+ {
+ if (check_edge (s->dpy, dest, gc, 120, 60, "left",
+ img, margin, margin, 1, h) |
+ check_edge (s->dpy, dest, gc, 160, 60, "right",
+ img, margin + w - 1, margin, 1, h) |
+ check_edge (s->dpy, dest, gc, 200, 60, "top",
+ img, margin, margin, 0, w) |
+ check_edge (s->dpy, dest, gc, 240, 60, "bottom",
+ img, margin, margin + h - 1, 0, w))
+ {
+ XSetFont (s->dpy, gc, font2->fid);
+ XDrawString (s->dpy, dest, gc, 10, 60,
+ "Wasted space: ", 14);
+ }
+ }
+ }
+
+ if (s->debug_metrics_antialiasing_p)
+ {
+ /* Draw a dark cyan boundary around antialiased glyphs */
+ img2 = XCreateImage (s->dpy, s->xgwa.visual, img->depth,
+ ZPixmap, 0, NULL,
+ img->width, img->height,
+ img->bitmap_pad, 0);
+ img2->data = malloc (img->bytes_per_line * img->height);
+
+ for (y2 = 0; y2 != pixh; ++y2)
+ for (x2 = 0; x2 != pixw; ++x2)
+ {
+ unsigned long px = XGetPixel (img, x2, y2);
+ if ((px & 0xffffff) == 0)
+ {
+ unsigned long neighbors = 0;
+ if (x2)
+ neighbors |= XGetPixel (img, x2 - 1, y2);
+ if (x2 != pixw - 1)
+ neighbors |= XGetPixel (img, x2 + 1, y2);
+ if (y2)
+ neighbors |= XGetPixel (img, x2, y2 - 1);
+ if (y2 != pixh - 1)
+ neighbors |= XGetPixel (img, x2, y2 + 1);
+ XPutPixel (img2, x2, y2,
+ (neighbors & 0xffffff
+ ? cyan
+ : BlackPixelOfScreen (s->xgwa.screen)));
+ }
+ else
+ {
+ XPutPixel (img2, x2, y2, px);
+ }
+ }
+ }
+ else
+ {
+ img2 = img;
+ img = NULL;
+ }
+
+ p2 = scale_ximage (s->xgwa.screen, s->window, img2, sc, margin);
+ if (img)
+ XDestroyImage (img);
+ XDestroyImage (img2);
+ }
+
+ XCopyArea (s->dpy, p2, dest, gc,
+ 0, 0, sc*pixw, sc*pixh,
+ x + sc * (cc.lbearing - margin),
+ y - sc * (cc.ascent + margin));
+ XFreePixmap (s->dpy, p);
+ XFreePixmap (s->dpy, p2);
+ XFreeGC (s->dpy, gc2);
+ }
+
+ if (i == 0)
+ {
+ XSetFont (s->dpy, gc, font->fid);
+ XSetForeground (s->dpy, gc, WhitePixelOfScreen (s->xgwa.screen));
+# ifdef HAVE_COCOA
+ jwxyz_XSetAntiAliasing (s->dpy, gc, s->debug_metrics_antialiasing_p);
+# endif
+ sprintf (txt2, "%c [XX%cXX] [%c%c%c%c]",
+ s->debug_metrics_p, s->debug_metrics_p,
+ s->debug_metrics_p, s->debug_metrics_p,
+ s->debug_metrics_p, s->debug_metrics_p);
+ XDrawString (s->dpy, dest, gc,
+ x + (sc*cc.rbearing/2) - cc.rbearing/2, ascent + 10,
+ txt2, strlen(txt2));
+# ifdef HAVE_COCOA
+ jwxyz_XSetAntiAliasing (s->dpy, gc, False);
+# endif
+ XSetFont (s->dpy, gc, font2->fid);
+ sprintf (txt2, "%c %3d 0%03o 0x%02x%s",
+ s->debug_metrics_p, s->debug_metrics_p,
+ s->debug_metrics_p, s->debug_metrics_p,
+ (txt[0] < font->min_char_or_byte2 ? " *" : ""));
+ XDrawString (s->dpy, dest, gc,
+ 10, 20,
+ txt2, strlen(txt2));
}
- screenhack_handle_event (s->dpy, &event);
+# ifdef HAVE_COCOA
+ jwxyz_XSetAntiAliasing (s->dpy, gc, True);
+# endif
+
+ XSetFont (s->dpy, gc, font2->fid);
+
+ XSetForeground (s->dpy, gc, red);
+
+ sprintf (txt2, "%s ascent %d",
+ (i == 0 ? "char" : "overall"),
+ ascent);
+ XDrawString (s->dpy, dest, gc, 10, y - sc*ascent - 2,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc, 0, y - sc*ascent, x3, y - sc*ascent);
+
+ sprintf (txt2, "%s descent %d",
+ (i == 0 ? "char" : "overall"),
+ descent);
+ XDrawString (s->dpy, dest, gc, 10, y + sc*descent - 2,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc, 0, y + sc*descent, x3, y + sc*descent);
+
+
+ /* ascent, descent, baseline */
+
+ XSetForeground (s->dpy, gc, green);
+
+ sprintf (txt2, "ascent %d", cc.ascent);
+ if (cc.ascent != 0)
+ XDrawString (s->dpy, dest, gc, x1, y - sc*cc.ascent - 2,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ x1, y - sc*cc.ascent, x2, y - sc*cc.ascent);
+
+ sprintf (txt2, "descent %d", cc.descent);
+ if (cc.descent != 0)
+ XDrawString (s->dpy, dest, gc, x1, y + sc*cc.descent - 2,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ x1, y + sc*cc.descent, x2, y + sc*cc.descent);
+
+ XSetForeground (s->dpy, gc, yellow);
+ strcpy (txt2, "baseline");
+ XDrawString (s->dpy, dest, gc, x1, y - 2,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc, x1, y, x2, y);
+
+
+ /* origin, width */
+
+ XSetForeground (s->dpy, gc, blue);
+
+ strcpy (txt2, "origin");
+ XDrawString (s->dpy, dest, gc,
+ x + 2, y + sc*(descent + 10),
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ x, y - sc*(ascent - 10),
+ x, y + sc*(descent + 10));
+
+ sprintf (txt2, "width %d", cc.width);
+ XDrawString (s->dpy, dest, gc,
+ x + sc*cc.width + 2,
+ y + sc*(descent + 10) + 10,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ x + sc*cc.width, y - sc*(ascent - 10),
+ x + sc*cc.width, y + sc*(descent + 10));
+
+
+ /* lbearing, rbearing */
+
+ XSetForeground (s->dpy, gc, green);
+
+ sprintf (txt2, "lbearing %d", cc.lbearing);
+ XDrawString (s->dpy, dest, gc, x + sc*cc.lbearing + 2,
+ y + sc * descent + 30,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ x + sc*cc.lbearing, y - sc*ascent,
+ x + sc*cc.lbearing, y + sc*descent + 20);
+
+ sprintf (txt2, "rbearing %d", cc.rbearing);
+ XDrawString (s->dpy, dest, gc, x + sc*cc.rbearing + 2,
+ y + sc * descent + 40,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ x + sc*cc.rbearing, y - sc*ascent,
+ x + sc*cc.rbearing, y + sc*descent + 40);
+
+ y += sc * (ascent + descent) * 2;
}
+
+ if (dest != s->window)
+ XCopyArea (s->dpy, dest, s->window, s->bg_gc,
+ 0, 0, s->xgwa.width, s->xgwa.height, 0, 0);
+
+ XFreeGC (s->dpy, gc);
+ XFreeFont (s->dpy, font);
+ XFreeFont (s->dpy, font2);
+ return s->frame_delay;
}
+# endif /* DEBUG */
-\f
-/* Subprocess.
- (This bit mostly cribbed from phosphor.c)
- */
-static void
-subproc_cb (XtPointer closure, int *source, XtInputId *id)
+/* Render all the words to the screen, and run the animation one step.
+ Clear screen first, swap buffers after.
+ */
+static unsigned long
+fontglide_draw (Display *dpy, Window window, void *closure)
{
state *s = (state *) closure;
- s->input_available_p = True;
-}
+ int i;
+# ifdef DEBUG
+ if (s->debug_metrics_p)
+ return fontglide_draw_metrics (closure);
+# endif /* DEBUG */
-static void
-launch_text_generator (state *s)
-{
- char *oprogram = get_string_resource ("program", "Program");
- char *program = (char *) malloc (strlen (oprogram) + 10);
- strcpy (program, "( ");
- strcat (program, oprogram);
- strcat (program, " ) 2>&1");
+ if (s->spawn_p)
+ more_sentences (s);
- if (s->debug_p)
- fprintf (stderr, "%s: forking: %s\n", progname, program);
+ if (!s->trails_p)
+ XFillRectangle (s->dpy, s->b, s->bg_gc,
+ 0, 0, s->xgwa.width, s->xgwa.height);
+
+ for (i = 0; i < s->nsentences; i++)
+ draw_sentence (s, s->sentences[i]);
- if ((s->pipe = popen (program, "r")))
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+ if (s->backb)
{
- s->pipe_id =
- XtAppAddInput (app, fileno (s->pipe),
- (XtPointer) (XtInputReadMask | XtInputExceptMask),
- subproc_cb, (XtPointer) s);
+ XdbeSwapInfo info[1];
+ info[0].swap_window = s->window;
+ info[0].swap_action = (s->dbeclear_p ? XdbeBackground : XdbeUndefined);
+ XdbeSwapBuffers (s->dpy, info, 1);
}
else
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+ if (s->dbuf)
{
- perror (program);
+ XCopyArea (s->dpy, s->b, s->window, s->bg_gc,
+ 0, 0, s->xgwa.width, s->xgwa.height, 0, 0);
}
-}
-
-static void
-relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
-{
- state *s = (state *) closure;
- launch_text_generator (s);
+ return s->frame_delay;
}
+
/* When the subprocess has generated some output, this reads as much as it
can into s->buf at s->buf_tail.
*/
static void
drain_input (state *s)
{
- /* allow subproc_cb() to run, if the select() down in Xt says that
- input is available. This tells us whether we can read() without
- blocking. */
- if (! s->input_available_p)
- if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
- XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
-
- if (! s->pipe) return;
- if (! s->input_available_p) return;
- s->input_available_p = False;
-
- if (s->buf_tail < sizeof(s->buf) - 2)
+ while (s->buf_tail < sizeof(s->buf) - 2)
{
- int target = sizeof(s->buf) - s->buf_tail - 2;
- int n;
- n = read (fileno (s->pipe),
- (void *) (s->buf + s->buf_tail),
- target);
- if (n > 0)
- {
- s->buf_tail += n;
- s->buf[s->buf_tail] = 0;
- }
+ int c = textclient_getc (s->tc);
+ if (c > 0)
+ s->buf[s->buf_tail++] = (char) c;
else
- {
- XtRemoveInput (s->pipe_id);
- s->pipe_id = 0;
- pclose (s->pipe);
- s->pipe = 0;
-
- /* If the process didn't print a terminating newline, add one. */
- if (s->buf_tail > 1 &&
- s->buf[s->buf_tail-1] != '\n')
- {
- s->buf[s->buf_tail++] = '\n';
- s->buf[s->buf_tail] = 0;
- }
-
- /* Then add one more, to make sure there's a sentence break at EOF.
- */
- s->buf[s->buf_tail++] = '\n';
- s->buf[s->buf_tail] = 0;
-
- /* Set up a timer to re-launch the subproc in a bit. */
- XtAppAddTimeOut (app, s->subproc_relaunch_delay,
- relaunch_generator_timer,
- (XtPointer) s);
- }
+ break;
}
}
\f
/* Window setup and resource loading */
-static state *
-init_fontglide (Display *dpy, Window window)
+static void *
+fontglide_init (Display *dpy, Window window)
{
XGCValues gcv;
state *s = (state *) calloc (1, sizeof(*s));
s->dpy = dpy;
s->window = window;
+ s->frame_delay = get_integer_resource (dpy, "delay", "Integer");
XGetWindowAttributes (s->dpy, s->window, &s->xgwa);
- s->font_override = get_string_resource ("font", "Font");
+ s->font_override = get_string_resource (dpy, "font", "Font");
if (s->font_override && (!*s->font_override || *s->font_override == '('))
s->font_override = 0;
- s->charset = get_string_resource ("fontCharset", "FontCharset");
- s->border_width = get_integer_resource ("fontBorderWidth", "Integer");
+ s->charset = get_string_resource (dpy, "fontCharset", "FontCharset");
+ s->border_width = get_integer_resource (dpy, "fontBorderWidth", "Integer");
if (s->border_width < 0 || s->border_width > 20)
s->border_width = 1;
- s->speed = get_float_resource ("speed", "Float");
+ s->speed = get_float_resource (dpy, "speed", "Float");
if (s->speed <= 0 || s->speed > 200)
s->speed = 1;
- s->linger = get_float_resource ("linger", "Float");
+ s->linger = get_float_resource (dpy, "linger", "Float");
if (s->linger <= 0 || s->linger > 200)
s->linger = 1;
- s->debug_p = get_boolean_resource ("debug", "Debug");
- s->trails_p = get_boolean_resource ("trails", "Trails");
+ s->trails_p = get_boolean_resource (dpy, "trails", "Trails");
+
+# ifdef DEBUG
+ s->debug_p = get_boolean_resource (dpy, "debug", "Debug");
+ s->debug_metrics_p = (get_boolean_resource (dpy, "debugMetrics", "Debug")
+ ? 199 : 0);
+ s->debug_scale = 6;
+# endif /* DEBUG */
+
+ s->dbuf = get_boolean_resource (dpy, "doubleBuffer", "Boolean");
+
+# ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
+ s->dbuf = False;
+# endif
- s->dbuf = get_boolean_resource ("doubleBuffer", "Boolean");
- s->dbeclear_p = get_boolean_resource ("useDBEClear", "Boolean");
+# ifdef DEBUG
+ if (s->debug_metrics_p) s->trails_p = False;
+# endif /* DEBUG */
if (s->trails_p) s->dbuf = False; /* don't need it in this case */
{
- char *ss = get_string_resource ("mode", "Mode");
+ const char *ss = get_string_resource (dpy, "mode", "Mode");
if (!ss || !*ss || !strcasecmp (ss, "random"))
s->mode = ((random() % 2) ? SCROLL : PAGE);
else if (!strcasecmp (ss, "scroll"))
if (s->dbuf)
{
#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+ s->dbeclear_p = get_boolean_resource (dpy, "useDBEClear", "Boolean");
if (s->dbeclear_p)
s->b = xdbe_get_backbuffer (dpy, window, XdbeBackground);
else
s->b = s->window;
}
- gcv.foreground = get_pixel_resource ("background", "Background",
- s->dpy, s->xgwa.colormap);
+ gcv.foreground = get_pixel_resource (s->dpy, s->xgwa.colormap,
+ "background", "Background");
s->bg_gc = XCreateGC (s->dpy, s->b, GCForeground, &gcv);
- s->subproc_relaunch_delay = 2 * 1000;
-
- launch_text_generator (s);
-
s->nsentences = 5; /* #### */
s->sentences = (sentence **) calloc (s->nsentences, sizeof (sentence *));
s->spawn_p = True;
+ s->early_p = True;
+ s->start_time = time ((time_t *) 0);
+ s->tc = textclient_open (dpy);
+
return s;
}
-\f
-char *progclass = "FontGlide";
+static Bool
+fontglide_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+# ifdef DEBUG
+ state *s = (state *) closure;
+
+ if (! s->debug_metrics_p)
+ return False;
+ if (event->xany.type == KeyPress)
+ {
+ KeySym keysym;
+ char c = 0;
+ XLookupString (&event->xkey, &c, 1, &keysym, 0);
+ if (c == '\t')
+ s->debug_metrics_antialiasing_p ^= True;
+ else if (c == 3 || c == 27)
+ exit (0);
+ else if (c >= ' ')
+ s->debug_metrics_p = (unsigned char) c;
+ else if (keysym == XK_Left || keysym == XK_Right)
+ {
+ s->debug_metrics_p += (keysym == XK_Left ? -1 : 1);
+ if (s->debug_metrics_p > 255)
+ s->debug_metrics_p = 1;
+ else if (s->debug_metrics_p <= 0)
+ s->debug_metrics_p = 255;
+ return True;
+ }
+ else if (keysym == XK_Up)
+ s->debug_scale++;
+ else if (keysym == XK_Down)
+ s->debug_scale = (s->debug_scale > 1 ? s->debug_scale-1 : 1);
+ else
+ return False;
+ return True;
+ }
+# endif /* DEBUG */
+
+ return False;
+}
+
+
+static void
+fontglide_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+ state *s = (state *) closure;
+ XGetWindowAttributes (s->dpy, s->window, &s->xgwa);
+
+ if (s->dbuf && s->ba)
+ {
+ XFreePixmap (s->dpy, s->ba);
+ s->ba = XCreatePixmap (s->dpy, s->window,
+ s->xgwa.width, s->xgwa.height,
+ s->xgwa.depth);
+ XFillRectangle (s->dpy, s->ba, s->bg_gc, 0, 0,
+ s->xgwa.width, s->xgwa.height);
+ s->b = s->ba;
+ }
+}
+
+static void
+fontglide_free (Display *dpy, Window window, void *closure)
+{
+ state *s = (state *) closure;
+ textclient_close (s->tc);
+
+ /* #### there's more to free here */
+
+ free (s);
+}
+
-char *defaults [] = {
+static const char *fontglide_defaults [] = {
".background: #000000",
".foreground: #DDDDDD",
".borderColor: #555555",
"*delay: 10000",
"*program: xscreensaver-text",
+ "*usePty: false",
"*mode: random",
".font: (default)",
"*fontCharset: iso8859-1",
"*speed: 1.0",
"*linger: 1.0",
"*trails: False",
+# ifdef DEBUG
"*debug: False",
+ "*debugMetrics: False",
+# endif /* DEBUG */
"*doubleBuffer: True",
-#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+# ifdef HAVE_DOUBLE_BUFFER_EXTENSION
"*useDBE: True",
"*useDBEClear: True",
-#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+# endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
0
};
-XrmOptionDescRec options [] = {
+static XrmOptionDescRec fontglide_options [] = {
+ { "-mode", ".mode", XrmoptionSepArg, 0 },
{ "-scroll", ".mode", XrmoptionNoArg, "scroll" },
{ "-page", ".mode", XrmoptionNoArg, "page" },
{ "-random", ".mode", XrmoptionNoArg, "random" },
{ "-no-trails", ".trails", XrmoptionNoArg, "False" },
{ "-db", ".doubleBuffer", XrmoptionNoArg, "True" },
{ "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" },
+# ifdef DEBUG
{ "-debug", ".debug", XrmoptionNoArg, "True" },
+ { "-debug-metrics", ".debugMetrics", XrmoptionNoArg, "True" },
+# endif /* DEBUG */
{ 0, 0, 0, 0 }
};
-void
-screenhack (Display *dpy, Window window)
-{
- state *s = init_fontglide (dpy, window);
- int delay = get_integer_resource ("delay", "Integer");
-
- while (1)
- {
- handle_events (s);
- draw_fontglide (s);
- XSync (dpy, False);
- if (delay) usleep (delay);
- }
-}
+XSCREENSAVER_MODULE ("FontGlide", fontglide)