-/* xscreensaver, Copyright (c) 2003, 2005, 2006 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2003-2016 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
# include "config.h"
#endif /* HAVE_CONFIG_H */
+
+/* If you turn on DEBUG, this program also masquerades as a tool for
+ debugging font metrics issues, which is probably only if interest
+ if you are doing active development on libjwxyz.a itself.
+ */
+/* #define DEBUG */
+
#include <math.h>
+#include <time.h>
-#ifndef HAVE_COCOA
+#ifndef HAVE_JWXYZ
# include <X11/Intrinsic.h>
#endif
#include "screenhack.h"
#include "textclient.h"
+#include "xft.h"
+#include "utf8wc.h"
#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
#include "xdbe.h"
typedef struct {
char *text;
- int x, y, width, height;
- int ascent, lbearing, rbearing;
+
+ int x, y; /* Position of origin of first character in word */
+
+ /* These have the same meanings as in XCharStruct: */
+ int lbearing; /* origin to leftmost pixel */
+ int rbearing; /* origin to rightmost pixel */
+ int ascent; /* origin to topmost pixel */
+ int descent; /* origin to bottommost pixel */
+ int width; /* origin to next word's origin */
int nticks, tick;
int start_x, start_y;
typedef struct {
int id;
- XColor fg;
- XColor bg;
Bool dark_p;
Bool move_chars_p;
int width;
char *font_name;
- XFontStruct *font;
-
GC fg_gc;
+ XftFont *xftfont;
+ XftColor xftcolor_fg, xftcolor_bg;
int nwords;
word **words;
double speed; /* frame rate multiplier */
double linger; /* multiplier for how long to leave words on screen */
Bool trails_p;
- Bool debug_p;
- int debug_metrics_p;
- enum { PAGE, SCROLL } mode;
+ enum { PAGE, SCROLL, CHARS } mode;
char *font_override; /* if -font was specified on the cmd line */
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;
int id_tick;
text_data *tc;
+# ifdef DEBUG
+ Bool debug_p;
+ unsigned long debug_metrics_p;
+ int debug_metrics_antialiasing_p;
+ int debug_scale;
+ unsigned entering_unicode_p; /* 0 = No, 1 = Just started, 2 = in progress */
+ XFontStruct *metrics_font1;
+ XFontStruct *metrics_font2;
+ XftFont *metrics_xftfont;
+ GC label_gc;
+ char *prev_font_name;
+ char *next_font_name;
+# endif /* DEBUG */
+
} state;
{
Bool ok = False;
char pattern[1024];
+ char pattern2[1024];
-# ifndef HAVE_COCOA /* real Xlib */
+#ifndef HAVE_JWXYZ /* real Xlib */
char **names = 0;
char **names2 = 0;
XFontStruct *info = 0;
int count = 0, count2 = 0;
int i;
- if (se->font)
+ if (se->xftfont)
{
- XFreeFont (s->dpy, se->font);
+ XftFontClose (s->dpy, se->xftfont);
+ XftColorFree (s->dpy, s->xgwa.visual, s->xgwa.colormap,
+ &se->xftcolor_fg);
+ XftColorFree (s->dpy, s->xgwa.visual, s->xgwa.colormap,
+ &se->xftcolor_bg);
+
free (se->font_name);
- se->font = 0;
+ se->xftfont = 0;
se->font_name = 0;
}
names2 = XListFontsWithInfo (s->dpy, names[i], 1000, &count2, &info);
if (count2 <= 0)
{
- fprintf (stderr, "%s: pattern %s\n"
- " gave unusable %s\n\n",
- progname, pattern, names[i]);
+# ifdef DEBUG
+ if (s->debug_p)
+ fprintf (stderr, "%s: pattern %s\n"
+ " gave unusable %s\n\n",
+ progname, pattern, names[i]);
+# endif /* DEBUG */
goto FAIL;
}
XFreeFontInfo (names2, info, count2);
XFreeFontNames (names);
-# else /* HAVE_COCOA */
+# else /* HAVE_JWXYZ */
if (s->font_override)
sprintf (pattern, "%.200s", s->font_override);
else
{
const char *family = "random";
- const char *weight = ((random() % 2) ? "normal" : "bold");
+ const char *weight = ((random() % 2) ? "regular" : "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);
+ sprintf (pattern, "*-%s-%s-%s-*-*-*-%d-*", family, weight, slant, size);
}
ok = True;
-# endif /* HAVE_COCOA */
+# endif /* HAVE_JWXYZ */
if (! ok) return False;
- se->font = XLoadQueryFont (s->dpy, pattern);
- if (! se->font)
+ se->xftfont = XftFontOpenXlfd (s->dpy, screen_number (s->xgwa.screen),
+ pattern);
+
+ if (! se->xftfont)
{
+# ifdef DEBUG
if (s->debug_p)
fprintf (stderr, "%s: unable to load font %s\n",
progname, pattern);
+#endif
return False;
}
- if (se->font->min_bounds.width == se->font->max_bounds.width &&
- !s->font_override)
- {
- /* This is to weed out
- "-urw-nimbus mono l-medium-o-normal--*-*-*-*-p-*-iso8859-1" and
- "-urw-courier-medium-r-normal--*-*-*-*-p-*-iso8859-1".
- We asked for only proportional fonts, but this fixed-width font
- shows up anyway -- but it has goofy metrics (see below) so it
- looks terrible anyway.
- */
- if (s->debug_p)
- fprintf (stderr,
- "%s: skipping bogus monospace non-charcell font: %s\n",
- progname, pattern);
- return False;
- }
+ strcpy (pattern2, pattern);
+# ifdef HAVE_JWXYZ
+ {
+ float s;
+ const char *n = jwxyz_nativeFontName (se->xftfont->xfont->fid, &s);
+ sprintf (pattern2 + strlen(pattern2), " (%s %.1f)", n, s);
+ }
+# endif
+
+# ifdef DEBUG
+ if (s->prev_font_name) free (s->prev_font_name);
+ s->prev_font_name = s->next_font_name;
+ s->next_font_name = strdup (pattern2);
+# endif
+
+ /* Sometimes we get fonts with screwed up metrics. For example:
+ -b&h-lucida-medium-r-normal-sans-40-289-100-100-p-0-iso8859-1
+
+ When using XDrawString, XTextExtents and XTextExtents16, it is rendered
+ as a scaled-up bitmap font. The character M has rbearing 70, ascent 68
+ and width 78, which is correct for the glyph as rendered.
+
+ But when using XftDrawStringUtf8 and XftTextExtentsUtf8, it is rendered
+ at the original, smaller, un-scaled size, with rbearing 26, ascent 25
+ and... width 77!
+
+ So it's taking the *size* from the unscaled font, the *advancement* from
+ the scaled-up version, and then *not* actually scaling it up. Awesome.
+
+ So, after loading the font, measure the M, and if its advancement is more
+ than 20% larger than its rbearing, reject the font.
+
+ ------------------------------------------------------------------------
+
+ Some observations on this nonsense from Dave Odell:
+
+ 1. -*-lucidatypewriter-bold-r-normal-*-*-480-*-*-*-*-iso8859-1 normally
+ resolves to /usr/share/fonts/X11/100dpi/lutBS24-ISO8859-1.pcf.gz.
+
+ -*-lucidatypewriter-* is from the 'xfonts-100dpi' package in
+ Debian/Ubuntu. It's usually (54.46% of systems), but not always,
+ installed whenever an X.org server (57.96% of systems) is. It might
+ be a good idea for this and xfonts-75dpi to be recommended
+ dependencies of XScreenSaver in Debian, but that's neither here nor
+ there. https://qa.debian.org/popcon.php?package=xorg
+ https://qa.debian.org/popcon.php?package=xfonts-100dpi
+
+ 2. It normally resolves to the PCF font... but not always.
+
+ Fontconfig has /etc/fonts/conf.d/ (it's /opt/local/etc/fonts/conf.d/
+ with MacPorts) containing symlinks to configuration files. And both
+ Debian and Ubuntu normally has a 70-no-bitmaps.conf, installed as part
+ of the 'fontconfig-config' package. And the 70-no-bitmaps.conf
+ symlink... disables bitmap fonts.
+ Without bitmap fonts, I get DejaVu Sans.
+
+ 3. There's another symlink of interest here:
+ /etc/fonts/conf.d/10-scale-bitmap-fonts.conf. This adds space to the
+ right of glyphs of bitmap fonts when the requested size of the font is
+ larger than the actual bitmap font. Ubuntu and MacPorts has this one.
+
+ This specifically is causing text to have excessive character spacing.
+
+ (jwz asks: WHY WOULD ANYONE EVER WANT THIS BEHAVIOR?)
+
+ 4. Notice that I'm only talking about Debian and Ubuntu. Other distros
+ will probably have different symlinks in /etc/fonts/conf.d/. So yes,
+ this can be an issue on Linux as well as MacOS.
+ */
+ {
+ XGlyphInfo extents;
+ int rbearing, width;
+ float ratio;
+ float min = 0.8;
+
+ XftTextExtentsUtf8 (s->dpy, se->xftfont, (FcChar8 *) "M", 1, &extents);
+ rbearing = extents.width - extents.x;
+ width = extents.xOff;
+ ratio = rbearing / (float) width;
+
+# ifdef DEBUG
+ if (s->debug_p)
+ fprintf (stderr, "%s: M ratio %.2f (%d %d): %s\n", progname,
+ ratio, rbearing, width, pattern2);
+# endif
+
+ if (ratio < min && !s->font_override)
+ {
+# ifdef DEBUG
+ if (s->debug_p)
+ fprintf (stderr, "%s: skipping font with broken metrics: %s\n",
+ progname, pattern2);
+# endif
+ return False;
+ }
+ }
+
+
+# ifdef DEBUG
if (s->debug_p)
- fprintf(stderr, "%s: %s\n", progname, pattern);
+ fprintf(stderr, "%s: %s\n", progname, pattern2);
+# endif /* DEBUG */
se->font_name = strdup (pattern);
- XSetFont (s->dpy, se->fg_gc, se->font->fid);
return True;
}
pick_font (state *s, sentence *se)
{
int i;
- for (i = 0; i < 20; i++)
+ for (i = 0; i < 50; i++)
if (pick_font_1 (s, se))
return;
- fprintf (stderr, "%s: too many font-loading failures: giving up!\n", progname);
+ fprintf (stderr, "%s: too many font-loading failures: giving up!\n",
+ progname);
exit (1);
}
static 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)
{
- start = unread_word_text;
+ result = unread_word_text;
unread_word_text = 0;
- return start;
+ return strdup (result);
}
/* Skip over whitespace at the beginning of the buffer,
}
+/* Returns a 1-bit pixmap of the same size as the drawable,
+ with a 0 wherever the drawable is black.
+ */
+static Pixmap
+make_mask (Screen *screen, Visual *visual, Drawable drawable)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ unsigned long black = BlackPixelOfScreen (screen);
+ Window r;
+ int x, y;
+ unsigned int w, h, bw, d;
+ XImage *out, *in;
+ Pixmap mask;
+ GC gc;
+
+ XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bw, &d);
+ in = XGetImage (dpy, drawable, 0, 0, w, h, ~0L, ZPixmap);
+ out = XCreateImage (dpy, visual, 1, XYPixmap, 0, 0, w, h, 8, 0);
+ out->data = (char *) malloc (h * out->bytes_per_line);
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++)
+ XPutPixel (out, x, y, (black != XGetPixel (in, x, y)));
+ mask = XCreatePixmap (dpy, drawable, w, h, 1L);
+ gc = XCreateGC (dpy, mask, 0, 0);
+ XPutImage (dpy, mask, gc, out, 0, 0, 0, 0, w, h);
+ XFreeGC (dpy, gc);
+ free (in->data);
+ free (out->data);
+ in->data = out->data = 0;
+ XDestroyImage (in);
+ XDestroyImage (out);
+ return mask;
+}
+
+
/* 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;
+ XGlyphInfo extents;
int bw = s->border_width;
if (!txt)
return 0;
w = (word *) calloc (1, sizeof(*w));
- XTextExtents (se->font, txt, strlen(txt), &dir, &ascent, &descent, &overall);
-
- /* Leave a little more slack */
- overall.lbearing -= (bw * 2);
- overall.rbearing += (bw * 2);
- overall.ascent += (bw * 2);
- overall.descent += (bw * 2);
-
- w->width = overall.rbearing - overall.lbearing + bw + bw;
- w->height = overall.ascent + overall.descent + bw + bw;
- w->ascent = overall.ascent + bw;
- w->lbearing = overall.lbearing - bw;
- w->rbearing = overall.width + bw;
-
-# if 0
- /* The metrics on some fonts are strange -- e.g.,
- "-urw-nimbus mono l-medium-o-normal--*-*-*-*-p-*-iso8859-1" and
- "-urw-courier-medium-r-normal--*-*-*-*-p-*-iso8859-1" both have
- an rbearing so wide that it looks like there are two spaces after
- each letter. If this character says it has an rbearing that is to
- the right of its ink, ignore that.
-
- #### Of course, this hack only helps when we're in `move_chars_p' mode
- and drawing a char at a time -- when we draw the whole word at once,
- XDrawString believes the bogus metrics and spaces the font out
- crazily anyway.
-
- Sigh, this causes some text to mis-render in, e.g.,
- "-adobe-utopia-medium-i-normal--114-*-100-100-p-*-iso8859-1"
- (in "ux", we need the rbearing on "r" or we get too much overlap.)
- */
- if (w->rbearing > w->width)
- w->rbearing = w->width;
-# endif /* 0 */
+ XftTextExtentsUtf8 (s->dpy, se->xftfont, (FcChar8 *) txt, strlen(txt),
+ &extents);
+
+ w->lbearing = -extents.x;
+ w->rbearing = extents.width - extents.x;
+ w->ascent = extents.y;
+ w->descent = extents.height - extents.y;
+ w->width = extents.xOff;
+
+ w->lbearing -= bw;
+ w->rbearing += bw;
+ w->descent += bw;
+ w->ascent += bw;
if (s->mode == SCROLL && !alloc_p) abort();
{
int i, j;
XGCValues gcv;
- GC gc0, gc1;
+ GC gc_fg, gc_bg, gc_black;
+ XftDraw *xftdraw;
+ int width = w->rbearing - w->lbearing;
+ int height = w->ascent + w->descent;
- w->pixmap = XCreatePixmap (s->dpy, s->b, w->width, w->height, 1L);
- w->mask = XCreatePixmap (s->dpy, s->b, w->width, w->height, 1L);
+ if (width <= 0) width = 1;
+ if (height <= 0) height = 1;
- gcv.font = se->font->fid;
- gcv.foreground = 0L;
- gcv.background = 1L;
- gc0 = XCreateGC (s->dpy, w->pixmap, GCFont|GCForeground|GCBackground,
- &gcv);
- gcv.foreground = 1L;
- gcv.background = 0L;
- gc1 = XCreateGC (s->dpy, w->pixmap, GCFont|GCForeground|GCBackground,
- &gcv);
+ w->pixmap = XCreatePixmap (s->dpy, s->b, width, height, s->xgwa.depth);
+ xftdraw = XftDrawCreate (s->dpy, w->pixmap, s->xgwa.visual,
+ s->xgwa.colormap);
- XFillRectangle (s->dpy, w->mask, gc0, 0, 0, w->width, w->height);
- XFillRectangle (s->dpy, w->pixmap, gc0, 0, 0, w->width, w->height);
+ gcv.foreground = se->xftcolor_fg.pixel;
+ gc_fg = XCreateGC (s->dpy, w->pixmap, GCForeground, &gcv);
+ gcv.foreground = se->xftcolor_bg.pixel;
+ gc_bg = XCreateGC (s->dpy, w->pixmap, GCForeground, &gcv);
+
+ gcv.foreground = BlackPixelOfScreen (s->xgwa.screen);
+ gc_black = XCreateGC (s->dpy, w->pixmap, GCForeground, &gcv);
+
+ XFillRectangle (s->dpy, w->pixmap, gc_black, 0, 0, width, height);
+
+# ifdef DEBUG
if (s->debug_p)
{
/* bounding box (behind the characters) */
- XDrawRectangle (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
- 0, 0, w->width-1, w->height-1);
- XDrawRectangle (s->dpy, w->mask, gc1,
- 0, 0, w->width-1, w->height-1);
+ XDrawRectangle (s->dpy, w->pixmap, (se->dark_p ? gc_bg : gc_fg),
+ 0, 0, width-1, height-1);
}
+# endif /* DEBUG */
- 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;
- }
- }
-
- /* Draw foreground text */
- XDrawString (s->dpy, w->pixmap, gc1, -w->lbearing, w->ascent,
- txt, strlen(txt));
-
- /* Cheesy hack to draw a border */
- /* (I should be able to do this in i*2 time instead of i*i time,
- but I can't get it right, so fuck it.) */
- XSetFunction (s->dpy, gc1, GXor);
+ /* Draw background text for border */
for (i = -bw; i <= bw; i++)
for (j = -bw; j <= bw; j++)
- XCopyArea (s->dpy, w->pixmap, w->mask, gc1,
- 0, 0, w->width, w->height,
- i, j);
+ XftDrawStringUtf8 (xftdraw, &se->xftcolor_bg, se->xftfont,
+ -w->lbearing + i, w->ascent + j,
+ (FcChar8 *) txt, strlen(txt));
+ /* Draw foreground text */
+ XftDrawStringUtf8 (xftdraw, &se->xftcolor_fg, se->xftfont,
+ -w->lbearing, w->ascent,
+ (FcChar8 *) txt, strlen(txt));
+
+# ifdef DEBUG
if (s->debug_p)
{
- XSetFunction (s->dpy, gc1, GXcopy);
- if (w->ascent != w->height)
+ if (w->ascent != height)
{
/* baseline (on top of the characters) */
- XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
- 0, w->ascent, w->width-1, w->ascent);
- XDrawLine (s->dpy, w->mask, gc1,
- 0, w->ascent, w->width-1, w->ascent);
+ XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc_bg : gc_fg),
+ 0, w->ascent, width-1, w->ascent);
}
- if (w->lbearing != 0)
+ if (w->lbearing < 0)
{
/* left edge of charcell */
- XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
- w->lbearing, 0, w->lbearing, w->height-1);
- XDrawLine (s->dpy, w->mask, gc1,
- w->lbearing, 0, w->lbearing, w->height-1);
+ XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc_bg : gc_fg),
+ -w->lbearing, 0,
+ -w->lbearing, height-1);
}
if (w->rbearing != w->width)
{
/* right edge of charcell */
- XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
- w->rbearing, 0, w->rbearing, w->height-1);
- XDrawLine (s->dpy, w->mask, gc1,
- w->rbearing, 0, w->rbearing, w->height-1);
+ XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc_bg : gc_fg),
+ w->width - w->lbearing, 0,
+ w->width - w->lbearing, height-1);
}
}
+# endif /* DEBUG */
- XFreeGC (s->dpy, gc0);
- XFreeGC (s->dpy, gc1);
+ w->mask = make_mask (s->xgwa.screen, s->xgwa.visual, w->pixmap);
+
+ XftDrawDestroy (xftdraw);
+ XFreeGC (s->dpy, gc_fg);
+ XFreeGC (s->dpy, gc_bg);
+ XFreeGC (s->dpy, gc_black);
}
- w->text = txt;
+ w->text = strdup (txt);
return w;
}
int i;
for (i = 0; i < se->nwords; i++)
free_word (s, se->words[i]);
- if (se->words) free (se->words);
-
- if (se->fg.flags)
- XFreeColors (s->dpy, s->xgwa.colormap, &se->fg.pixel, 1, 0);
- if (se->bg.flags)
- XFreeColors (s->dpy, s->xgwa.colormap, &se->bg.pixel, 1, 0);
+ if (se->words)
+ free (se->words);
+ if (se->font_name)
+ free (se->font_name);
+ if (se->fg_gc)
+ XFreeGC (s->dpy, se->fg_gc);
- if (se->font_name) free (se->font_name);
- if (se->font) XFreeFont (s->dpy, se->font);
- if (se->fg_gc) XFreeGC (s->dpy, se->fg_gc);
+ if (se->xftfont)
+ {
+ XftFontClose (s->dpy, se->xftfont);
+ XftColorFree (s->dpy, s->xgwa.visual, s->xgwa.colormap,
+ &se->xftcolor_fg);
+ XftColorFree (s->dpy, s->xgwa.visual, s->xgwa.colormap,
+ &se->xftcolor_bg);
+ }
free (se);
}
word **words2;
int nwords2 = 0;
int i, j;
+
+ char ***word_chars = (char ***) malloc (se->nwords * sizeof(*word_chars));
for (i = 0; i < se->nwords; i++)
- nwords2 += strlen (se->words[i]->text);
+ {
+ int L;
+ word *ow = se->words[i];
+ word_chars[i] = utf8_split (ow->text, &L);
+ nwords2 += L;
+ }
words2 = (word **) calloc (nwords2, sizeof(*words2));
for (i = 0, j = 0; i < se->nwords; i++)
{
- word *ow = se->words[i];
- int L = strlen (ow->text);
+ char **chars = word_chars[i];
+ word *parent = se->words[i];
+ int x = parent->x;
+ int y = parent->y;
+ int sx = parent->start_x;
+ int sy = parent->start_y;
+ int tx = parent->target_x;
+ int ty = parent->target_y;
int k;
- int x = ow->x;
- int y = ow->y;
- int sx = ow->start_x;
- int sy = ow->start_y;
- int tx = ow->target_x;
- int ty = ow->target_y;
-
- for (k = 0; k < L; k++)
+ for (k = 0; chars[k]; k++)
{
- char *t2 = malloc (2);
- word *w2;
- int xoff, yoff;
-
- t2[0] = ow->text[k];
- t2[1] = 0;
- w2 = new_word (s, se, t2, True);
+ char *t2 = chars[k];
+ word *w2 = new_word (s, se, t2, True);
words2[j++] = w2;
- xoff = (w2->lbearing - ow->lbearing);
- yoff = (ow->ascent - w2->ascent);
-
- w2->x = x + xoff;
- w2->y = y + yoff;
- w2->start_x = sx + xoff;
- w2->start_y = sy + yoff;
- w2->target_x = tx + xoff;
- w2->target_y = ty + yoff;
+ w2->x = x;
+ w2->y = y;
+ w2->start_x = sx;
+ w2->start_y = sy;
+ w2->target_x = tx;
+ w2->target_y = ty;
- x += w2->rbearing;
- sx += w2->rbearing;
- tx += w2->rbearing;
+ x += w2->width;
+ sx += w2->width;
+ tx += w2->width;
}
- free_word (s, ow);
- se->words[i] = 0;
+ /* This is not invariant when kerning is involved! */
+ /* if (x != parent->x + parent->width) abort(); */
+
+ free (chars); /* but we retain its contents */
+ free_word (s, parent);
}
+ if (j != nwords2) abort();
+ free (word_chars);
free (se->words);
se->words = words2;
scatter_sentence (state *s, sentence *se)
{
int i = 0;
- int off = 100;
+ int off = s->border_width * 4 + 2;
int flock_p = ((random() % 4) == 0);
int mode = (flock_p ? (random() % 12) : 0);
word *w = se->words[i];
int x, y;
int r = (flock_p ? mode : (random() % 4));
- switch (r)
- {
- /* random positions on the edges */
-
- case 0:
- x = -off - w->width;
- y = random() % s->xgwa.height;
- break;
- case 1:
- x = off + s->xgwa.width;
- y = random() % s->xgwa.height;
- break;
- case 2:
- x = random() % s->xgwa.width;
- y = -off - w->height;
- break;
- case 3:
- x = random() % s->xgwa.width;
- y = off + s->xgwa.height;
- break;
-
- /* straight towards the edges */
-
- case 4:
- x = -off - w->width;
- y = w->target_y;
- break;
- case 5:
- x = off + s->xgwa.width;
- y = w->target_y;
- break;
- case 6:
- x = w->target_x;
- y = -off - w->height;
- break;
- case 7:
- x = w->target_x;
- y = off + s->xgwa.height;
- break;
-
- /* corners */
-
- case 8:
- x = -off - w->width;
- y = -off - w->height;
- break;
- case 9:
- x = -off - w->width;
- y = off + s->xgwa.height;
- break;
- case 10:
- x = off + s->xgwa.width;
- y = off + s->xgwa.height;
- break;
- case 11:
- x = off + s->xgwa.width;
- y = -off - w->height;
- break;
-
- default:
- abort();
- break;
- }
+ int left = -(off + w->rbearing);
+ int top = -(off + w->descent);
+ int right = off - w->lbearing + s->xgwa.width;
+ int bottom = off + w->ascent + s->xgwa.height;
+
+ switch (r) {
+ /* random positions on the edges */
+ case 0: x = left; y = random() % s->xgwa.height; break;
+ case 1: x = right; y = random() % s->xgwa.height; break;
+ case 2: x = random() % s->xgwa.width; y = top; break;
+ case 3: x = random() % s->xgwa.width; y = bottom; break;
+
+ /* straight towards the edges */
+ case 4: x = left; y = w->target_y; break;
+ case 5: x = right; y = w->target_y; break;
+ case 6: x = w->target_x; y = top; break;
+ case 7: x = w->target_x; y = bottom; break;
+
+ /* corners */
+ case 8: x = left; y = top; break;
+ case 9: x = left; y = bottom; break;
+ case 10: x = right; y = top; break;
+ case 11: x = right; y = bottom; break;
+
+ default: abort(); break;
+ }
if (se->anim_state == IN)
{
}
nticks = ((se->words[0]->start_x - se->words[0]->target_x)
- / (s->speed * 10));
+ / (s->speed * 7));
nticks *= (frand(0.9) + frand(0.9) + frand(0.9));
if (nticks < 2)
static void
recolor (state *s, sentence *se)
{
- if (se->fg.flags)
- XFreeColors (s->dpy, s->xgwa.colormap, &se->fg.pixel, 1, 0);
- if (se->bg.flags)
- XFreeColors (s->dpy, s->xgwa.colormap, &se->bg.pixel, 1, 0);
-
- se->fg.flags = DoRed|DoGreen|DoBlue;
- se->bg.flags = DoRed|DoGreen|DoBlue;
-
- switch (random() % 2)
+ XRenderColor fg, bg;
+
+ fg.red = (random() % 0x5555) + 0xAAAA;
+ fg.green = (random() % 0x5555) + 0xAAAA;
+ fg.blue = (random() % 0x5555) + 0xAAAA;
+ fg.alpha = 0xFFFF;
+ bg.red = (random() % 0x5555);
+ bg.green = (random() % 0x5555);
+ bg.blue = (random() % 0x5555);
+ bg.alpha = 0xFFFF;
+ se->dark_p = False;
+
+ if (random() & 1)
{
- case 0: /* bright fg, dim bg */
- se->fg.red = (random() % 0x8888) + 0x8888;
- se->fg.green = (random() % 0x8888) + 0x8888;
- se->fg.blue = (random() % 0x8888) + 0x8888;
- se->bg.red = (random() % 0x5555);
- se->bg.green = (random() % 0x5555);
- se->bg.blue = (random() % 0x5555);
- break;
-
- case 1: /* bright bg, dim fg */
- se->fg.red = (random() % 0x4444);
- se->fg.green = (random() % 0x4444);
- se->fg.blue = (random() % 0x4444);
- se->bg.red = (random() % 0x4444) + 0xCCCC;
- se->bg.green = (random() % 0x4444) + 0xCCCC;
- se->bg.blue = (random() % 0x4444) + 0xCCCC;
- break;
-
- default:
- abort();
- break;
+ XRenderColor swap = fg; fg = bg; bg = swap;
+ se->dark_p = True;
}
- 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);
+ if (se->xftfont)
+ {
+ XftColorFree (s->dpy, s->xgwa.visual, s->xgwa.colormap,
+ &se->xftcolor_fg);
+ XftColorFree (s->dpy, s->xgwa.visual, s->xgwa.colormap,
+ &se->xftcolor_bg);
+ }
- if (XAllocColor (s->dpy, s->xgwa.colormap, &se->fg))
- XSetForeground (s->dpy, se->fg_gc, se->fg.pixel);
- if (XAllocColor (s->dpy, s->xgwa.colormap, &se->bg))
- XSetBackground (s->dpy, se->fg_gc, se->bg.pixel);
+ XftColorAllocValue (s->dpy, s->xgwa.visual, s->xgwa.colormap, &fg,
+ &se->xftcolor_fg);
+ XftColorAllocValue (s->dpy, s->xgwa.visual, s->xgwa.colormap, &bg,
+ &se->xftcolor_bg);
}
int array_size = 100;
- se->move_chars_p = (s->mode == SCROLL ? False :
+ se->move_chars_p = (s->mode == CHARS ? True :
+ s->mode == SCROLL ? False :
(random() % 3) ? False : True);
se->alignment = (random() % 3);
switch (s->mode)
{
case PAGE:
+ case CHARS:
left = random() % (s->xgwa.width / 3);
right = s->xgwa.width - (random() % (s->xgwa.width / 3));
top = random() % (s->xgwa.height * 2 / 3);
break; /* If EOF after some words, end of sentence. */
}
- if (! se->font) /* Got a word: need a font now */
+ if (! se->xftfont) /* Got a word: need a font now */
{
+ XGlyphInfo extents;
pick_font (s, se);
- if (y < se->font->ascent)
- y += se->font->ascent;
- space = XTextWidth (se->font, " ", 1);
+ if (y < se->xftfont->ascent)
+ y += se->xftfont->ascent;
+
+ /* Measure the space character to figure out how much room to
+ leave between words (since we don't actually render that.) */
+ XftTextExtentsUtf8 (s->dpy, se->xftfont, (FcChar8 *) " ", 1,
+ &extents);
+ space = extents.xOff;
}
w = new_word (s, se, txt, !se->move_chars_p);
+ free (txt);
+ txt = 0;
/* If we have a few words, let punctuation terminate the sentence:
stop gathering more words if the last word ends in a period, etc. */
if (se->nwords >= 25) /* ok that's just about enough out of you */
done = True;
- if (s->mode == PAGE &&
+ if ((s->mode == PAGE || s->mode == CHARS) &&
x + w->rbearing > right) /* wrap line */
{
align_line (s, se, line_start, x, right);
line_start = se->nwords;
x = left;
- y += se->font->ascent;
+ y += se->xftfont->ascent + se->xftfont->descent;
/* If we're close to the bottom of the screen, stop, and
unread the current word. (But not if this is the first
word, otherwise we might just get stuck on it.)
*/
if (se->nwords > 0 &&
- y + se->font->ascent > s->xgwa.height)
+ y + se->xftfont->ascent + se->xftfont->descent > s->xgwa.height)
{
unread_word (s, w);
+ free (w);
/* done = True; */
break;
}
}
- w->target_x = x + w->lbearing;
- w->target_y = y - w->ascent;
+ w->target_x = x;
+ w->target_y = y;
- x += w->rbearing + space;
+ x += w->width + space;
se->width = x;
if (se->nwords >= (array_size - 1))
{
array_size += 100;
- se->words = (word **) realloc (se->words,
- array_size * sizeof(*se->words));
+ se->words = (word **)
+ realloc (se->words, array_size * sizeof(*se->words));
if (!se->words)
{
fprintf (stderr, "%s: out of memory (%d words)\n",
switch (s->mode)
{
case PAGE:
+ case CHARS:
align_line (s, se, line_start, x, right);
if (se->move_chars_p)
split_words (s, se);
fprintf (stderr, " %s", se->words[i]->text);
fprintf (stderr, "\n");
}
-# endif
+# endif /* DEBUG */
}
/* Render a single word object to the screen.
*/
static void
-draw_word (state *s, sentence *se, word *w)
+draw_word (state *s, sentence *se, word *word)
{
- if (! w->pixmap) return;
-
- if (w->x + w->width < 0 ||
- w->y + w->height < 0 ||
- w->x > s->xgwa.width ||
- w->y > s->xgwa.height)
+ int x, y, w, h;
+ if (! word->pixmap) return;
+
+ x = word->x + word->lbearing;
+ y = word->y - word->ascent;
+ w = word->rbearing - word->lbearing;
+ h = word->ascent + word->descent;
+
+ if (x + w < 0 ||
+ y + h < 0 ||
+ x > s->xgwa.width ||
+ y > s->xgwa.height)
return;
- XSetClipMask (s->dpy, se->fg_gc, w->mask);
- XSetClipOrigin (s->dpy, se->fg_gc, w->x, w->y);
- XCopyPlane (s->dpy, w->pixmap, s->b, se->fg_gc,
- 0, 0, w->width, w->height,
- w->x, w->y,
- 1L);
+ XSetClipMask (s->dpy, se->fg_gc, word->mask);
+ XSetClipOrigin (s->dpy, se->fg_gc, x, y);
+ XCopyArea (s->dpy, word->pixmap, s->b, se->fg_gc,
+ 0, 0, w, h, x, y);
}
switch (s->mode)
{
case PAGE:
+ case CHARS:
if (se->anim_state != PAUSE &&
w->tick <= w->nticks)
{
w->y = w->start_y + (dy * r);
w->tick++;
- if (se->anim_state == OUT && s->mode == PAGE)
+ if (se->anim_state == OUT &&
+ (s->mode == PAGE || s->mode == CHARS))
w->tick++; /* go out faster */
moved = 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++)
}
+#ifdef DEBUG /* All of this stuff is for -debug-metrics mode. */
+
+
+static Pixmap
+scale_ximage (Screen *screen, Window window, XImage *img, int scale,
+ int margin)
+{
+ 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 (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);
+ }
+
+ XFreeGC (dpy, gc);
+ return p2;
+}
+
+
+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 (;;)
+ {
+ 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 unsigned long
fontglide_draw_metrics (state *s)
{
- char txt[2];
- char *fn = (s->font_override ? s->font_override : "fixed");
- XFontStruct *font = XLoadQueryFont (s->dpy, fn);
- XCharStruct c, overall;
+ unsigned int margin = (s->debug_metrics_antialiasing_p ? 2 : 0);
+
+ char txt[2], utxt[3], txt2[80];
+ XChar2b *txt3 = 0;
+ const char *fn = (s->font_override ? s->font_override : "fixed");
+ char fn2[1024];
+ XCharStruct c, overall, fake_c;
int dir, ascent, descent;
int x, y;
+ XGlyphInfo extents;
+ XftColor xftcolor;
+ XftDraw *xftdraw;
+ int sc = s->debug_scale;
GC gc;
- unsigned long red = 0xFFFF0000; /* so shoot me */
- unsigned long green = 0xFF00FF00;
- unsigned long blue = 0xFF6666FF;
- int i;
+ 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, j;
+ Drawable dest = s->b ? s->b : s->window;
+
+ if (sc < 1) sc = 1;
+
+ /* Self-test these macros to make sure they're symmetrical. */
+ for (i = 0; i < 1000; i++)
+ {
+ XGlyphInfo g, g2;
+ XRectangle r;
+ c.lbearing = (random()%50)-100;
+ c.rbearing = (random()%50)-100;
+ c.ascent = (random()%50)-100;
+ c.descent = (random()%50)-100;
+ c.width = (random()%50)-100;
+ XCharStruct_to_XGlyphInfo (c, g);
+ XGlyphInfo_to_XCharStruct (g, overall);
+ if (c.lbearing != overall.lbearing) abort();
+ if (c.rbearing != overall.rbearing) abort();
+ if (c.ascent != overall.ascent) abort();
+ if (c.descent != overall.descent) abort();
+ if (c.width != overall.width) abort();
+ XCharStruct_to_XGlyphInfo (overall, g2);
+ if (g.x != g2.x) abort();
+ if (g.y != g2.y) abort();
+ if (g.xOff != g2.xOff) abort();
+ if (g.yOff != g2.yOff) abort();
+ if (g.width != g2.width) abort();
+ if (g.height != g2.height) abort();
+ XCharStruct_to_XmbRectangle (overall, r);
+ XmbRectangle_to_XCharStruct (r, c, c.width);
+ if (c.lbearing != overall.lbearing) abort();
+ if (c.rbearing != overall.rbearing) abort();
+ if (c.ascent != overall.ascent) abort();
+ if (c.descent != overall.descent) abort();
+ if (c.width != overall.width) abort();
+ }
txt[0] = s->debug_metrics_p;
txt[1] = 0;
- gc = XCreateGC (s->dpy, s->window, 0, 0);
- XSetFont (s->dpy, gc, font->fid);
+ /* Convert Unicode code point to UTF-8. */
+ utxt[utf8_encode(s->debug_metrics_p, utxt, 4)] = 0;
+
+ txt3 = utf8_to_XChar2b (utxt, 0);
+
+ if (! s->metrics_font1)
+ s->metrics_font1 = XLoadQueryFont (s->dpy, fn);
+ if (! s->metrics_font2)
+ s->metrics_font2 = XLoadQueryFont (s->dpy, "fixed");
+ if (! s->metrics_font1)
+ s->metrics_font1 = s->metrics_font2;
-#ifdef HAVE_COCOA
+ gc = XCreateGC (s->dpy, dest, 0, 0);
+ XSetFont (s->dpy, gc, s->metrics_font1->fid);
+
+# if defined(HAVE_JWXYZ)
jwxyz_XSetAntiAliasing (s->dpy, gc, False);
-#endif
+# endif
+
+ if (! s->metrics_xftfont)
+ {
+ s->metrics_xftfont =
+ XftFontOpenXlfd (s->dpy, screen_number(s->xgwa.screen), fn);
+ if (! s->metrics_xftfont)
+ {
+ const char *fn2 = "fixed";
+ s->metrics_xftfont =
+ XftFontOpenName (s->dpy, screen_number(s->xgwa.screen), fn2);
+ if (s->metrics_xftfont)
+ fn = fn2;
+ else
+ {
+ fprintf (stderr, "%s: XftFontOpen failed on \"%s\" and \"%s\"\n",
+ progname, fn, fn2);
+ exit (1);
+ }
+ }
+ }
+
+ strcpy (fn2, fn);
+# ifdef HAVE_JWXYZ
+ {
+ float ss;
+ const char *n = jwxyz_nativeFontName (s->metrics_xftfont->xfont->fid, &ss);
+ sprintf (fn2, "%s %.1f", n, ss);
+ }
+# endif
- XTextExtents (font, txt, strlen(txt),
+ xftdraw = XftDrawCreate (s->dpy, dest, s->xgwa.visual,
+ s->xgwa.colormap);
+ XftColorAllocName (s->dpy, s->xgwa.visual, s->xgwa.colormap, "white",
+ &xftcolor);
+ XftTextExtentsUtf8 (s->dpy, s->metrics_xftfont,
+ (FcChar8 *) utxt, strlen(utxt),
+ &extents);
+
+
+ XTextExtents (s->metrics_font1, txt, strlen(txt),
&dir, &ascent, &descent, &overall);
- c = font->per_char[((unsigned char *) txt)[0] - font->min_char_or_byte2];
+ c = ((s->debug_metrics_p >= s->metrics_font1->min_char_or_byte2 &&
+ s->debug_metrics_p <= s->metrics_font1->max_char_or_byte2)
+ ? s->metrics_font1->per_char[s->debug_metrics_p -
+ s->metrics_font1->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);
+
+ XSetForeground (s->dpy, gc, WhitePixelOfScreen (s->xgwa.screen));
+ XSetFont (s->dpy, gc, s->metrics_font2->fid);
+ XDrawString (s->dpy, dest, gc,
+ s->xgwa.width / 2,
+ s->xgwa.height - 5,
+ fn2, strlen(fn2));
+
+# ifdef HAVE_JWXYZ
+ {
+ char *name = jwxyz_unicode_character_name (
+ s->dpy, s->metrics_font1->fid, s->debug_metrics_p);
+ if (!name || !*name) name = strdup("unknown character name");
+ XDrawString (s->dpy, dest, gc,
+ 10,
+ 10 + 2 * (s->metrics_font2->ascent +
+ s->metrics_font2->descent),
+ name, strlen(name));
+ free (name);
+ }
+# endif
- XClearWindow (s->dpy, s->window);
+ /* i 0, j 0: top left, XDrawString, char metrics
+ i 1, j 0: bot left, XDrawString, overall metrics, ink escape
+ i 0, j 1: top right, XftDrawStringUtf8, utf8 metrics
+ i 1, j 1: bot right, XDrawString16, 16 metrics, ink escape
+ */
+ for (j = 0; j < 2; j++) {
+ Bool xft_p = (j != 0);
+ int ww = s->xgwa.width / 2 - 20;
+ int xoff = (j == 0 ? 0 : ww + 20);
- x = (s->xgwa.width - overall.width) / 2;
- y = (s->xgwa.height - (2 * (ascent + descent))) / 2;
+ /* XDrawString only does 8-bit characters, so skip it outside Latin-1. */
+ if (s->debug_metrics_p >= 256)
+ {
+ if (!xft_p)
+ continue;
+ xoff = 0;
+ ww = s->xgwa.width;
+ }
- for (i = 0; i < 2; i++)
+ x = (ww - overall.width) / 2;
+
+ for (i = 0; i < 2; i++)
{
- XCharStruct cc = (i == 0 ? c : overall);
- int x1 = 20;
- int x2 = s->xgwa.width - 40;
- int x3 = s->xgwa.width;
+ XCharStruct cc;
+ int x1 = xoff + ww * 0.18;
+ int x2 = xoff + ww * 0.82;
+ int x3 = xoff + ww;
+ int pixw, pixh;
+ Pixmap p;
+
+ y = 80;
+ {
+ int h = sc * (ascent + descent);
+ int min = (ascent + descent) * 4;
+ if (h < min) h = min;
+ if (i == 1) h *= 3;
+ y += h;
+ }
+
+ memset (&fake_c, 0, sizeof(fake_c));
+
+ if (!xft_p && i == 0)
+ cc = c;
+ else if (!xft_p && i == 1)
+ cc = overall;
+ else if (xft_p && i == 0)
+ {
+ /* Measure the glyph in the Xft way */
+ XGlyphInfo extents;
+ XftTextExtentsUtf8 (s->dpy,
+ s->metrics_xftfont,
+ (FcChar8 *) utxt, strlen(utxt),
+ &extents);
+ XGlyphInfo_to_XCharStruct (extents, fake_c);
+ cc = fake_c;
+ }
+ else if (xft_p)
+ {
+ /* Measure the glyph in the 16-bit way */
+ int dir, ascent, descent;
+ XTextExtents16 (s->metrics_font1, txt3, 1, &dir, &ascent, &descent,
+ &fake_c);
+ cc = fake_c;
+ }
+
+ pixw = margin * 2 + cc.rbearing - cc.lbearing;
+ pixh = margin * 2 + cc.ascent + cc.descent;
+ p = (pixw > 0 && pixh > 0
+ ? XCreatePixmap (s->dpy, dest, pixw, pixh, s->xgwa.depth)
+ : 0);
+
+ if (p)
+ {
+ Pixmap p2;
+ GC gc2 = XCreateGC (s->dpy, p, 0, 0);
+# ifdef HAVE_JWXYZ
+ jwxyz_XSetAntiAliasing (s->dpy, gc2, False);
+# endif
+ XSetFont (s->dpy, gc2, s->metrics_font1->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_JWXYZ
+ jwxyz_XSetAntiAliasing (s->dpy, gc2,
+ s->debug_metrics_antialiasing_p);
+# endif
+
+ if (xft_p && i == 0)
+ {
+ XftDraw *xftdraw2 = XftDrawCreate (s->dpy, p, s->xgwa.visual,
+ s->xgwa.colormap);
+ XftDrawStringUtf8 (xftdraw2, &xftcolor,
+ s->metrics_xftfont,
+ -cc.lbearing + margin,
+ cc.ascent + margin,
+ (FcChar8 *) utxt, strlen(utxt));
+ XftDrawDestroy (xftdraw2);
+ }
+ else if (xft_p)
+ XDrawString16 (s->dpy, p, gc2,
+ -cc.lbearing + margin,
+ cc.ascent + margin,
+ txt3, 1);
+ else
+ 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 (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, s->metrics_font2->fid);
+ XDrawString (s->dpy, dest, gc,
+ xoff + 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, s->metrics_font2->fid);
+ XDrawString (s->dpy, dest, gc,
+ xoff + 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,
+ xoff + 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, s->metrics_font1->fid);
+ XSetForeground (s->dpy, gc, WhitePixelOfScreen (s->xgwa.screen));
+# ifdef HAVE_JWXYZ
+ jwxyz_XSetAntiAliasing (s->dpy, gc, s->debug_metrics_antialiasing_p);
+# endif
+ sprintf (txt2, "%s [XX%sXX] [%s%s%s%s]",
+ (xft_p ? utxt : txt),
+ (xft_p ? utxt : txt),
+ (xft_p ? utxt : txt),
+ (xft_p ? utxt : txt),
+ (xft_p ? utxt : txt),
+ (xft_p ? utxt : txt));
+
+ if (xft_p)
+ XftDrawStringUtf8 (xftdraw, &xftcolor,
+ s->metrics_xftfont,
+ xoff + x/2 + (sc*cc.rbearing/2) - cc.rbearing/2
+ + 40,
+ ascent + 10,
+ (FcChar8 *) txt2, strlen(txt2));
+ else
+ XDrawString (s->dpy, dest, gc,
+ xoff + x/2 + (sc*cc.rbearing/2) - cc.rbearing/2,
+ ascent + 10,
+ txt2, strlen(txt2));
+# ifdef HAVE_JWXYZ
+ jwxyz_XSetAntiAliasing (s->dpy, gc, False);
+# endif
+ XSetFont (s->dpy, gc, s->metrics_font2->fid);
+ if (xft_p)
+ {
+ char *uptr;
+ char *tptr = txt2 + sprintf(txt2, "U+%04lX", s->debug_metrics_p);
+ *tptr++ = " ?_"[s->entering_unicode_p];
+ *tptr++ = ' ';
+
+ uptr = utxt;
+ while (*uptr)
+ {
+ tptr += sprintf (tptr, "0%03o ", (unsigned char) *uptr);
+ ++uptr;
+ }
+ *tptr++ = ' ';
+ uptr = utxt;
+ while (*uptr)
+ {
+ tptr += sprintf (tptr, "%02x ", (unsigned char) *uptr);
+ ++uptr;
+ }
+ }
+ else
+ sprintf (txt2, "%c %3ld 0%03lo 0x%02lx%s",
+ (char)s->debug_metrics_p, s->debug_metrics_p,
+ s->debug_metrics_p, s->debug_metrics_p,
+ (txt[0] < s->metrics_font1->min_char_or_byte2
+ ? " *" : ""));
+ XDrawString (s->dpy, dest, gc,
+ xoff + 10, 20,
+ txt2, strlen(txt2));
+ }
+
+# ifdef HAVE_JWXYZ
+ jwxyz_XSetAntiAliasing (s->dpy, gc, True);
+# endif
- XSetForeground (s->dpy, gc, red);
- XDrawLine (s->dpy, s->window, gc, 0, y - ascent, x3, y - ascent);
- XDrawLine (s->dpy, s->window, gc, 0, y + descent, x3, y + descent);
+ {
+ const char *ss = (j == 0
+ ? (i == 0 ? "char" : "overall")
+ : (i == 0 ? "utf8" : "16 bit"));
+ XSetFont (s->dpy, gc, s->metrics_font2->fid);
+
+ XSetForeground (s->dpy, gc, red);
+
+ sprintf (txt2, "%s ascent %d", ss, ascent);
+ XDrawString (s->dpy, dest, gc,
+ xoff + 10,
+ y - sc*ascent - 2,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ xoff, y - sc*ascent,
+ x3, y - sc*ascent);
+
+ sprintf (txt2, "%s descent %d", ss, descent);
+ XDrawString (s->dpy, dest, gc,
+ xoff + 10,
+ y + sc*descent - 2,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ xoff, y + sc*descent,
+ x3, y + sc*descent);
+ }
+
+
+ /* ascent, descent, baseline */
XSetForeground (s->dpy, gc, green);
- /* ascent, baseline, descent */
- XDrawLine (s->dpy, s->window, gc, x1, y - cc.ascent, x2, y - cc.ascent);
- XDrawLine (s->dpy, s->window, gc, x1, y, x2, y);
- XDrawLine (s->dpy, s->window, gc, x1, y + cc.descent, x2, y + cc.descent);
+
+ 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);
- XDrawLine (s->dpy, s->window, gc,
- x, y - ascent - 10,
- x, y + descent + 10);
- XDrawLine (s->dpy, s->window, gc,
- x + cc.width, y - ascent - 10,
- x + cc.width, y + descent + 10);
+
+ strcpy (txt2, "origin");
+ XDrawString (s->dpy, dest, gc,
+ xoff + x + 2,
+ y + sc*descent + 50,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ xoff + x, y - sc*(ascent - 10),
+ xoff + x, y + sc*(descent + 10));
+
+ sprintf (txt2, "width %d", cc.width);
+ XDrawString (s->dpy, dest, gc,
+ xoff + x + sc*cc.width + 2,
+ y + sc*descent + 60,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ xoff + x + sc*cc.width, y - sc*(ascent - 10),
+ xoff + x + sc*cc.width, y + sc*(descent + 10));
+
/* lbearing, rbearing */
- XSetForeground (s->dpy, gc, green);
- XDrawLine (s->dpy, s->window, gc,
- x + cc.lbearing, y - ascent,
- x + cc.lbearing, y + descent);
- XDrawLine (s->dpy, s->window, gc,
- x + cc.rbearing, y - ascent,
- x + cc.rbearing, y + descent);
- XSetForeground (s->dpy, gc, WhitePixelOfScreen (s->xgwa.screen));
- XDrawString (s->dpy, s->window, gc, x, y, txt, strlen(txt));
+ XSetForeground (s->dpy, gc, green);
- y += (ascent + descent) * 2;
+ sprintf (txt2, "lbearing %d", cc.lbearing);
+ XDrawString (s->dpy, dest, gc,
+ xoff + x + sc*cc.lbearing + 2,
+ y + sc * descent + 30,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ xoff + x + sc*cc.lbearing, y - sc*ascent,
+ xoff + x + sc*cc.lbearing, y + sc*descent + 20);
+
+ sprintf (txt2, "rbearing %d", cc.rbearing);
+ XDrawString (s->dpy, dest, gc,
+ xoff + x + sc*cc.rbearing + 2,
+ y + sc * descent + 40,
+ txt2, strlen(txt2));
+ XDrawLine (s->dpy, dest, gc,
+ xoff + x + sc*cc.rbearing, y - sc*ascent,
+ xoff + 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);
+ XftColorFree (s->dpy, s->xgwa.visual, s->xgwa.colormap, &xftcolor);
+ XftDrawDestroy (xftdraw);
+ free (txt3);
+
return s->frame_delay;
}
+# endif /* DEBUG */
+
/* Render all the words to the screen, and run the animation one step.
Clear screen first, swap buffers after.
state *s = (state *) closure;
int i;
+# ifdef DEBUG
if (s->debug_metrics_p)
return fontglide_draw_metrics (closure);
+# endif /* DEBUG */
if (s->spawn_p)
more_sentences (s);
for (i = 0; i < s->nsentences; i++)
draw_sentence (s, s->sentences[i]);
+# ifdef DEBUG
+ if (s->debug_p && (s->prev_font_name || s->next_font_name))
+ {
+ if (! s->label_gc)
+ {
+ if (! s->metrics_font2)
+ s->metrics_font2 = XLoadQueryFont (s->dpy, "fixed");
+ s->label_gc = XCreateGC (dpy, s->b, 0, 0);
+ XSetFont (s->dpy, s->label_gc, s->metrics_font2->fid);
+ }
+ if (s->prev_font_name)
+ XDrawString (s->dpy, s->b, s->label_gc,
+ 10, 10 + s->metrics_font2->ascent,
+ s->prev_font_name, strlen(s->prev_font_name));
+ if (s->next_font_name)
+ XDrawString (s->dpy, s->b, s->label_gc,
+ 10, 10 + s->metrics_font2->ascent * 2,
+ s->next_font_name, strlen(s->next_font_name));
+ }
+# endif /* DEBUG */
+
#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
if (s->backb)
{
{
while (s->buf_tail < sizeof(s->buf) - 2)
{
- char c = textclient_getc (s->tc);
+ int c = textclient_getc (s->tc);
if (c > 0)
- s->buf[s->buf_tail++] = c;
+ s->buf[s->buf_tail++] = (char) c;
else
break;
}
s->linger = 1;
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")
- ? 'y' : 0);
+ ? 199 : 0);
+ s->debug_scale = 6;
+
+# ifdef HAVE_JWXYZ
+ if (s->debug_metrics_p && !s->font_override)
+ s->font_override = "Helvetica Bold 16";
+# endif
+
+# endif /* DEBUG */
s->dbuf = get_boolean_resource (dpy, "doubleBuffer", "Boolean");
-# ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
+# ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */
s->dbuf = False;
# endif
+# 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 (dpy, "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"))
s->mode = SCROLL;
else if (!strcasecmp (ss, "page"))
s->mode = PAGE;
+ else if (!strcasecmp (ss, "chars") || !strcasecmp (ss, "char"))
+ s->mode = CHARS;
else
{
fprintf (stderr,
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;
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;
- else if (event->xany.type == ButtonPress)
- {
- s->debug_metrics_p++;
- if (s->debug_metrics_p > 255)
- s->debug_metrics_p = ' ';
- else if (s->debug_metrics_p > 127 &&
- s->debug_metrics_p < 159)
- s->debug_metrics_p = 160;
- return True;
- }
- else if (event->xany.type == KeyPress)
+ if (event->xany.type == KeyPress)
{
+ static const unsigned long max = 0x110000;
KeySym keysym;
char c = 0;
XLookupString (&event->xkey, &c, 1, &keysym, 0);
- if (c)
+
+ if (s->entering_unicode_p > 0)
+ {
+ unsigned digit;
+ unsigned long new_char = 0;
+
+ if (c >= 'a' && c <= 'f')
+ digit = c + 0xa - 'a';
+ else if (c >= 'A' && c <= 'F')
+ digit = c + 0xa - 'A';
+ else if (c >= '0' && c <= '9')
+ digit = c + 0 - '0';
+ else
+ {
+ s->entering_unicode_p = 0;
+ return True;
+ }
+
+ if (s->entering_unicode_p == 1)
+ new_char = 0;
+ else if (s->entering_unicode_p == 2)
+ new_char = s->debug_metrics_p;
+
+ new_char = (new_char << 4) | digit;
+ if (new_char > 0 && new_char < max)
+ {
+ s->debug_metrics_p = new_char;
+ s->entering_unicode_p = 2;
+ }
+ else
+ s->entering_unicode_p = 0;
+ return True;
+ }
+
+ 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;
- return !!c;
+ else if (keysym == XK_Left || keysym == XK_Right)
+ {
+ s->debug_metrics_p += (keysym == XK_Left ? -1 : 1);
+ if (s->debug_metrics_p >= max)
+ s->debug_metrics_p = 1;
+ else if (s->debug_metrics_p <= 0)
+ s->debug_metrics_p = max - 1;
+ return True;
+ }
+ else if (keysym == XK_Prior)
+ s->debug_metrics_p = (s->debug_metrics_p + max - 0x80) % max;
+ else if (keysym == XK_Next)
+ s->debug_metrics_p = (s->debug_metrics_p + 0x80) % max;
+ 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 if (keysym == XK_F1)
+ s->entering_unicode_p = 1;
+ else
+ return False;
+ return True;
}
- else
- return False;
+# endif /* DEBUG */
+
+ return False;
}
state *s = (state *) closure;
XGetWindowAttributes (s->dpy, s->window, &s->xgwa);
- if (s->dbuf && (s->ba))
+ if (s->dbuf && s->ba)
{
XFreePixmap (s->dpy, s->ba);
s->ba = XCreatePixmap (s->dpy, s->window,
state *s = (state *) closure;
textclient_close (s->tc);
+#ifdef DEBUG
+ if (s->metrics_xftfont)
+ XftFontClose (s->dpy, s->metrics_xftfont);
+ if (s->metrics_font1)
+ XFreeFont (s->dpy, s->metrics_font1);
+ if (s->metrics_font2 && s->metrics_font1 != s->metrics_font2)
+ XFreeFont (s->dpy, s->metrics_font2);
+ if (s->prev_font_name) free (s->prev_font_name);
+ if (s->next_font_name) free (s->next_font_name);
+ if (s->label_gc) XFreeGC (dpy, s->label_gc);
+#endif
+
/* #### there's more to free here */
free (s);
"*usePty: false",
"*mode: random",
".font: (default)",
+
+ /* I'm not entirely clear on whether the charset of an XLFD has any
+ meaning when Xft is being used. */
"*fontCharset: iso8859-1",
+/*"*fontCharset: iso10646-1", */
+/*"*fontCharset: *-*",*/
+
"*fontBorderWidth: 2",
"*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
};
{ "-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 }
};