1 /* xscreensaver, Copyright (c) 2003-2014 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * fontglide -- reads text from a subprocess and puts it on the screen using
12 * large characters that glide in from the edges, assemble, then disperse.
13 * Requires a system with scalable fonts. (X's font handing sucks. A lot.)
18 #endif /* HAVE_CONFIG_H */
25 # include <X11/Intrinsic.h>
32 #include "screenhack.h"
33 #include "textclient.h"
35 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
37 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
41 int x, y, width, height;
42 int ascent, lbearing, rbearing;
46 int target_x, target_y;
67 enum { IN, PAUSE, OUT } anim_state;
68 enum { LEFT, CENTER, RIGHT } alignment;
77 XWindowAttributes xgwa;
79 Pixmap b, ba; /* double-buffer to reduce flicker */
82 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
85 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
87 Bool dbuf; /* Whether we're using double buffering. */
89 int border_width; /* size of the font outline */
90 char *charset; /* registry and encoding for font lookups */
91 double speed; /* frame rate multiplier */
92 double linger; /* multiplier for how long to leave words on screen */
94 enum { PAGE, SCROLL } mode;
96 char *font_override; /* if -font was specified on the cmd line */
98 char buf [40]; /* this only needs to be as big as one "word". */
104 sentence **sentences;
105 Bool spawn_p; /* whether it is time to create a new sentence */
107 unsigned long frame_delay;
113 int debug_metrics_p, debug_metrics_antialiasing_p;
120 static void drain_input (state *s);
124 pick_font_size (state *s)
126 double scale = s->xgwa.height / 1024.0; /* shrink for small windows */
127 int min, max, r, pixel;
132 if (min < 10) min = 10;
133 if (max < 30) max = 30;
137 pixel = min + ((random() % r) + (random() % r) + (random() % r));
139 if (s->mode == SCROLL) /* scroll mode likes bigger fonts */
146 /* Finds the set of scalable fonts on the system; picks one;
147 and loads that font in a random pixel size.
148 Returns False if something went wrong.
151 pick_font_1 (state *s, sentence *se)
156 # ifndef HAVE_COCOA /* real Xlib */
159 XFontStruct *info = 0;
160 int count = 0, count2 = 0;
165 XFreeFont (s->dpy, se->font);
166 free (se->font_name);
171 if (s->font_override)
172 sprintf (pattern, "%.200s", s->font_override);
174 sprintf (pattern, "-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s",
181 "0", /* pixel size */
182 "0", /* point size */
183 "0", /* resolution x */
184 "0", /* resolution y */
187 s->charset); /* registry + encoding */
189 names = XListFonts (s->dpy, pattern, 1000, &count);
193 if (s->font_override)
194 fprintf (stderr, "%s: -font option bogus: %s\n", progname, pattern);
196 fprintf (stderr, "%s: no scalable fonts found! (pattern: %s)\n",
201 i = random() % count;
203 names2 = XListFontsWithInfo (s->dpy, names[i], 1000, &count2, &info);
206 fprintf (stderr, "%s: pattern %s\n"
207 " gave unusable %s\n\n",
208 progname, pattern, names[i]);
213 XFontStruct *font = &info[0];
214 unsigned long value = 0;
215 char *foundry=0, *family=0, *weight=0, *slant=0, *setwidth=0, *add_style=0;
216 unsigned long pixel=0, point=0, res_x=0, res_y=0;
218 unsigned long avg_width=0;
219 char *registry=0, *encoding=0;
221 char *bogus = "\"?\"";
223 # define STR(ATOM,VAR) \
225 a = XInternAtom (s->dpy, (ATOM), False); \
226 if (XGetFontProperty (font, a, &value)) \
227 VAR = XGetAtomName (s->dpy, value); \
231 # define INT(ATOM,VAR) \
233 a = XInternAtom (s->dpy, (ATOM), False); \
234 if (!XGetFontProperty (font, a, &VAR) || \
238 STR ("FOUNDRY", foundry);
239 STR ("FAMILY_NAME", family);
240 STR ("WEIGHT_NAME", weight);
241 STR ("SLANT", slant);
242 STR ("SETWIDTH_NAME", setwidth);
243 STR ("ADD_STYLE_NAME", add_style);
244 INT ("PIXEL_SIZE", pixel);
245 INT ("POINT_SIZE", point);
246 INT ("RESOLUTION_X", res_x);
247 INT ("RESOLUTION_Y", res_y);
248 STR ("SPACING", spacing);
249 INT ("AVERAGE_WIDTH", avg_width);
250 STR ("CHARSET_REGISTRY", registry);
251 STR ("CHARSET_ENCODING", encoding);
256 pixel = pick_font_size (s);
259 /* Occasionally change the aspect ratio of the font, by increasing
260 either the X or Y resolution (while leaving the other alone.)
262 #### Looks like this trick doesn't really work that well: the
263 metrics of the individual characters are ok, but the
264 overall font ascent comes out wrong (unscaled.)
266 if (! (random() % 8))
269 double scale = 1 + (frand(n) + frand(n) + frand(n));
278 "-%s-%s-%s-%s-%s-%s-%ld-%s-%ld-%ld-%s-%s-%s-%s",
279 foundry, family, weight, slant, setwidth, add_style,
280 pixel, "*", /* point, */
281 res_x, res_y, spacing,
288 fprintf (stderr, "%s: font has bogus %s property: %s\n",
289 progname, bogus, names[i]);
291 if (foundry) XFree (foundry);
292 if (family) XFree (family);
293 if (weight) XFree (weight);
294 if (slant) XFree (slant);
295 if (setwidth) XFree (setwidth);
296 if (add_style) XFree (add_style);
297 if (spacing) XFree (spacing);
298 if (registry) XFree (registry);
299 if (encoding) XFree (encoding);
304 XFreeFontInfo (names2, info, count2);
305 XFreeFontNames (names);
307 # else /* HAVE_COCOA */
309 if (s->font_override)
310 sprintf (pattern, "%.200s", s->font_override);
313 const char *family = "random";
314 const char *weight = ((random() % 2) ? "normal" : "bold");
315 const char *slant = ((random() % 2) ? "o" : "r");
316 int size = 10 * pick_font_size (s);
317 sprintf (pattern, "*-%s-%s-%s-*-%d-*", family, weight, slant, size);
320 # endif /* HAVE_COCOA */
322 if (! ok) return False;
324 se->font = XLoadQueryFont (s->dpy, pattern);
330 fprintf (stderr, "%s: unable to load font %s\n",
335 if (se->font->min_bounds.width == se->font->max_bounds.width &&
338 /* This is to weed out
339 "-urw-nimbus mono l-medium-o-normal--*-*-*-*-p-*-iso8859-1" and
340 "-urw-courier-medium-r-normal--*-*-*-*-p-*-iso8859-1".
341 We asked for only proportional fonts, but this fixed-width font
342 shows up anyway -- but it has goofy metrics (see below) so it
343 looks terrible anyway.
347 "%s: skipping bogus monospace non-charcell font: %s\n",
353 fprintf(stderr, "%s: %s\n", progname, pattern);
356 se->font_name = strdup (pattern);
357 XSetFont (s->dpy, se->fg_gc, se->font->fid);
362 /* Finds the set of scalable fonts on the system; picks one;
363 and loads that font in a random pixel size.
366 pick_font (state *s, sentence *se)
369 for (i = 0; i < 20; i++)
370 if (pick_font_1 (s, se))
372 fprintf (stderr, "%s: too many font-loading failures: giving up!\n", progname);
377 static char *unread_word_text = 0;
379 /* Returns a newly-allocated string with one word in it, or NULL if there
380 is no complete word available.
383 get_word_text (state *s)
385 const char *start = s->buf;
392 /* If we just launched, and haven't had any text yet, and it has been
393 more than 2 seconds since we launched, then push out "Loading..."
394 as our first text. So if the text source is speedy, just use that.
395 But if we'd display a blank screen for a while, give 'em something
401 s->start_time < ((time ((time_t *) 0) - 2)))
403 unread_word_text = "Loading...";
407 if (unread_word_text)
409 start = unread_word_text;
410 unread_word_text = 0;
414 /* Skip over whitespace at the beginning of the buffer,
415 and count up how many linebreaks we see while doing so.
423 if (*start == '\n' || (*start == '\r' && start[1] != '\n'))
430 /* If we saw a blank line, then return NULL (treat it as a temporary "eof",
431 to trigger a sentence break here.) */
435 /* Skip forward to the end of this word (find next whitespace.) */
443 /* If we have a word, allocate a string for it */
446 result = malloc ((end - start) + 1);
447 strncpy (result, start, (end-start));
448 result [end-start] = 0;
453 /* Make room in the buffer by compressing out any bytes we've processed.
457 int n = end - s->buf;
458 memmove (s->buf, end, sizeof(s->buf) - n);
466 /* Gets some random text, and creates a "word" object from it.
469 new_word (state *s, sentence *se, const char *txt, Bool alloc_p)
473 int dir, ascent, descent;
474 int bw = s->border_width;
480 w = (word *) calloc (1, sizeof(*w));
481 XTextExtents (se->font, txt, strlen(txt), &dir, &ascent, &descent, &overall);
483 /* Leave a little more slack. Not entirely clear on what's going on here,
484 but maybe it's fonts with goofy metrics. */
485 slack = (overall.ascent + overall.descent) * 0.25;
486 if (slack < bw*2) slack = bw*2;
487 overall.lbearing -= slack;
488 overall.rbearing += slack;
489 overall.ascent += slack;
490 overall.descent += slack;
492 w->width = overall.rbearing - overall.lbearing;
493 w->height = overall.ascent + overall.descent;
494 w->ascent = overall.ascent + bw;
495 w->lbearing = overall.lbearing - bw;
496 w->rbearing = overall.width + bw;
499 /* The metrics on some fonts are strange -- e.g.,
500 "-urw-nimbus mono l-medium-o-normal--*-*-*-*-p-*-iso8859-1" and
501 "-urw-courier-medium-r-normal--*-*-*-*-p-*-iso8859-1" both have
502 an rbearing so wide that it looks like there are two spaces after
503 each letter. If this character says it has an rbearing that is to
504 the right of its ink, ignore that.
506 #### Of course, this hack only helps when we're in `move_chars_p' mode
507 and drawing a char at a time -- when we draw the whole word at once,
508 XDrawString believes the bogus metrics and spaces the font out
511 Sigh, this causes some text to mis-render in, e.g.,
512 "-adobe-utopia-medium-i-normal--114-*-100-100-p-*-iso8859-1"
513 (in "ux", we need the rbearing on "r" or we get too much overlap.)
515 if (w->rbearing > w->width)
516 w->rbearing = w->width;
519 if (s->mode == SCROLL && !alloc_p) abort();
527 if (w->width <= 0) w->width = 1;
528 if (w->height <= 0) w->height = 1;
530 w->pixmap = XCreatePixmap (s->dpy, s->b, w->width, w->height, 1L);
531 w->mask = XCreatePixmap (s->dpy, s->b, w->width, w->height, 1L);
533 gcv.font = se->font->fid;
536 gc0 = XCreateGC (s->dpy, w->pixmap, GCFont|GCForeground|GCBackground,
540 gc1 = XCreateGC (s->dpy, w->pixmap, GCFont|GCForeground|GCBackground,
543 XFillRectangle (s->dpy, w->mask, gc0, 0, 0, w->width, w->height);
544 XFillRectangle (s->dpy, w->pixmap, gc0, 0, 0, w->width, w->height);
549 /* bounding box (behind the characters) */
550 XDrawRectangle (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
551 0, 0, w->width-1, w->height-1);
552 XDrawRectangle (s->dpy, w->mask, gc1,
553 0, 0, w->width-1, w->height-1);
558 /* bounding box (behind *each* character) */
561 for (ss = txt; *ss; ss++)
563 XTextExtents (se->font, ss, 1, &dir, &ascent, &descent, &overall);
564 XDrawRectangle (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
565 x, w->ascent - overall.ascent,
567 overall.ascent + overall.descent);
568 XDrawRectangle (s->dpy, w->mask, gc1,
569 x, w->ascent - overall.ascent,
571 overall.ascent + overall.descent);
573 XDrawRectangle (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
574 x - overall.lbearing, w->ascent - overall.ascent,
576 overall.ascent + overall.descent);
577 XDrawRectangle (s->dpy, w->mask, gc1,
578 x - overall.lbearing, w->ascent - overall.ascent,
580 overall.ascent + overall.descent);
588 /* Draw foreground text */
589 XDrawString (s->dpy, w->pixmap, gc1, -w->lbearing, w->ascent,
592 /* Cheesy hack to draw a border */
593 /* (I should be able to do this in i*2 time instead of i*i time,
594 but I can't get it right, so fuck it.) */
595 XSetFunction (s->dpy, gc1, GXor);
596 for (i = -bw; i <= bw; i++)
597 for (j = -bw; j <= bw; j++)
598 XCopyArea (s->dpy, w->pixmap, w->mask, gc1,
599 0, 0, w->width, w->height,
605 XSetFunction (s->dpy, gc1, GXcopy);
606 if (w->ascent != w->height)
608 /* baseline (on top of the characters) */
609 XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
610 0, w->ascent, w->width-1, w->ascent);
611 XDrawLine (s->dpy, w->mask, gc1,
612 0, w->ascent, w->width-1, w->ascent);
615 if (w->lbearing != 0)
617 /* left edge of charcell */
618 XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
619 w->lbearing, 0, w->lbearing, w->height-1);
620 XDrawLine (s->dpy, w->mask, gc1,
621 w->lbearing, 0, w->lbearing, w->height-1);
624 if (w->rbearing != w->width)
626 /* right edge of charcell */
627 XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
628 w->rbearing, 0, w->rbearing, w->height-1);
629 XDrawLine (s->dpy, w->mask, gc1,
630 w->rbearing, 0, w->rbearing, w->height-1);
635 XFreeGC (s->dpy, gc0);
636 XFreeGC (s->dpy, gc1);
639 w->text = strdup (txt);
645 free_word (state *s, word *w)
647 if (w->text) free (w->text);
648 if (w->pixmap) XFreePixmap (s->dpy, w->pixmap);
649 if (w->mask) XFreePixmap (s->dpy, w->mask);
654 new_sentence (state *st, state *s)
657 sentence *se = (sentence *) calloc (1, sizeof (*se));
658 se->fg_gc = XCreateGC (s->dpy, s->b, 0, &gcv);
660 se->id = ++st->id_tick;
666 free_sentence (state *s, sentence *se)
669 for (i = 0; i < se->nwords; i++)
670 free_word (s, se->words[i]);
671 if (se->words) free (se->words);
674 XFreeColors (s->dpy, s->xgwa.colormap, &se->fg.pixel, 1, 0);
676 XFreeColors (s->dpy, s->xgwa.colormap, &se->bg.pixel, 1, 0);
678 if (se->font_name) free (se->font_name);
679 if (se->font) XFreeFont (s->dpy, se->font);
680 if (se->fg_gc) XFreeGC (s->dpy, se->fg_gc);
686 /* free the word, and put its text back at the front of the input queue,
687 to be read next time. */
689 unread_word (state *s, word *w)
691 if (unread_word_text)
693 unread_word_text = w->text;
699 /* Divide each of the words in the sentence into one character words,
700 without changing the positions of those characters.
703 split_words (state *s, sentence *se)
708 for (i = 0; i < se->nwords; i++)
709 nwords2 += strlen (se->words[i]->text);
711 words2 = (word **) calloc (nwords2, sizeof(*words2));
713 for (i = 0, j = 0; i < se->nwords; i++)
715 word *ow = se->words[i];
716 int L = strlen (ow->text);
721 int sx = ow->start_x;
722 int sy = ow->start_y;
723 int tx = ow->target_x;
724 int ty = ow->target_y;
726 for (k = 0; k < L; k++)
734 w2 = new_word (s, se, t2, True);
737 xoff = (w2->lbearing - ow->lbearing);
738 yoff = (ow->ascent - w2->ascent);
742 w2->start_x = sx + xoff;
743 w2->start_y = sy + yoff;
744 w2->target_x = tx + xoff;
745 w2->target_y = ty + yoff;
758 se->nwords = nwords2;
762 /* Set the source or destination position of the words to be somewhere
766 scatter_sentence (state *s, sentence *se)
771 int flock_p = ((random() % 4) == 0);
772 int mode = (flock_p ? (random() % 12) : 0);
774 for (i = 0; i < se->nwords; i++)
776 word *w = se->words[i];
778 int r = (flock_p ? mode : (random() % 4));
781 /* random positions on the edges */
785 y = random() % s->xgwa.height;
788 x = off + s->xgwa.width;
789 y = random() % s->xgwa.height;
792 x = random() % s->xgwa.width;
793 y = -off - w->height;
796 x = random() % s->xgwa.width;
797 y = off + s->xgwa.height;
800 /* straight towards the edges */
807 x = off + s->xgwa.width;
812 y = -off - w->height;
816 y = off + s->xgwa.height;
823 y = -off - w->height;
827 y = off + s->xgwa.height;
830 x = off + s->xgwa.width;
831 y = off + s->xgwa.height;
834 x = off + s->xgwa.width;
835 y = -off - w->height;
843 if (se->anim_state == IN)
856 w->nticks = ((100 + ((random() % 140) +
867 /* Set the source position of the words to be off the right side,
868 and the destination to be off the left side.
871 aim_sentence (state *s, sentence *se)
877 if (se->nwords <= 0) abort();
879 /* Have the sentence shift up or down a little bit; not too far, and
880 never let it fall off the top or bottom of the screen before its
881 last character has reached the left edge.
883 for (i = 0; i < 10; i++)
885 int ty = random() % (s->xgwa.height - se->words[0]->ascent);
886 yoff = ty - se->words[0]->target_y;
887 if (yoff < s->xgwa.height/3) /* this one is ok */
891 for (i = 0; i < se->nwords; i++)
893 word *w = se->words[i];
894 w->start_x = w->target_x + s->xgwa.width;
895 w->target_x -= se->width;
896 w->start_y = w->target_y;
900 nticks = ((se->words[0]->start_x - se->words[0]->target_x)
902 nticks *= (frand(0.9) + frand(0.9) + frand(0.9));
907 for (i = 0; i < se->nwords; i++)
909 word *w = se->words[i];
916 /* Randomize the order of the words in the list (since that changes
917 which ones are "on top".)
920 shuffle_words (state *s, sentence *se)
923 for (i = 0; i < se->nwords-1; i++)
925 int j = i + (random() % (se->nwords - i));
926 word *swap = se->words[i];
927 se->words[i] = se->words[j];
933 /* qsort comparitor */
935 cmp_sentences (const void *aa, const void *bb)
937 const sentence *a = *(sentence **) aa;
938 const sentence *b = *(sentence **) bb;
939 return ((a ? a->id : 999999) - (b ? b->id : 999999));
943 /* Sort the sentences by id, so that sentences added later are on top.
946 sort_sentences (state *s)
948 qsort (s->sentences, s->nsentences, sizeof(*s->sentences), cmp_sentences);
952 /* Re-pick the colors of the text and border
955 recolor (state *s, sentence *se)
958 XFreeColors (s->dpy, s->xgwa.colormap, &se->fg.pixel, 1, 0);
960 XFreeColors (s->dpy, s->xgwa.colormap, &se->bg.pixel, 1, 0);
962 se->fg.flags = DoRed|DoGreen|DoBlue;
963 se->bg.flags = DoRed|DoGreen|DoBlue;
965 switch (random() % 2)
967 case 0: /* bright fg, dim bg */
968 se->fg.red = (random() % 0x8888) + 0x8888;
969 se->fg.green = (random() % 0x8888) + 0x8888;
970 se->fg.blue = (random() % 0x8888) + 0x8888;
971 se->bg.red = (random() % 0x5555);
972 se->bg.green = (random() % 0x5555);
973 se->bg.blue = (random() % 0x5555);
976 case 1: /* bright bg, dim fg */
977 se->fg.red = (random() % 0x4444);
978 se->fg.green = (random() % 0x4444);
979 se->fg.blue = (random() % 0x4444);
980 se->bg.red = (random() % 0x4444) + 0xCCCC;
981 se->bg.green = (random() % 0x4444) + 0xCCCC;
982 se->bg.blue = (random() % 0x4444) + 0xCCCC;
992 se->dark_p = (se->fg.red*2 + se->fg.green*3 + se->fg.blue <
993 se->bg.red*2 + se->bg.green*3 + se->bg.blue);
996 if (XAllocColor (s->dpy, s->xgwa.colormap, &se->fg))
997 XSetForeground (s->dpy, se->fg_gc, se->fg.pixel);
998 if (XAllocColor (s->dpy, s->xgwa.colormap, &se->bg))
999 XSetBackground (s->dpy, se->fg_gc, se->bg.pixel);
1004 align_line (state *s, sentence *se, int line_start, int x, int right)
1007 switch (se->alignment)
1009 case LEFT: off = 0; break;
1010 case CENTER: off = (right - x) / 2; break;
1011 case RIGHT: off = (right - x); break;
1012 default: abort(); break;
1016 for (j = line_start; j < se->nwords; j++)
1017 se->words[j]->target_x += off;
1021 /* Fill the sentence with new words: in "page" mode, fills the page
1022 with text; in "scroll" mode, just makes one long horizontal sentence.
1023 The sentence might have *no* words in it, if no text is currently
1027 populate_sentence (state *s, sentence *se)
1030 int left, right, top, x, y;
1035 int array_size = 100;
1037 se->move_chars_p = (s->mode == SCROLL ? False :
1038 (random() % 3) ? False : True);
1039 se->alignment = (random() % 3);
1045 for (i = 0; i < se->nwords; i++)
1046 free_word (s, se->words[i]);
1050 se->words = (word **) calloc (array_size, sizeof(*se->words));
1056 left = random() % (s->xgwa.width / 3);
1057 right = s->xgwa.width - (random() % (s->xgwa.width / 3));
1058 top = random() % (s->xgwa.height * 2 / 3);
1062 right = s->xgwa.width;
1063 top = random() % s->xgwa.height;
1075 const char *txt = get_word_text (s);
1079 if (se->nwords == 0)
1080 return; /* If the stream is empty, bail. */
1082 break; /* If EOF after some words, end of sentence. */
1085 if (! se->font) /* Got a word: need a font now */
1088 if (y < se->font->ascent)
1089 y += se->font->ascent;
1090 space = XTextWidth (se->font, " ", 1);
1093 w = new_word (s, se, txt, !se->move_chars_p);
1095 /* If we have a few words, let punctuation terminate the sentence:
1096 stop gathering more words if the last word ends in a period, etc. */
1097 if (se->nwords >= 4)
1099 char c = w->text[strlen(w->text)-1];
1100 if (c == '.' || c == '?' || c == '!')
1104 /* If the sentence is kind of long already, terminate at commas, etc. */
1105 if (se->nwords >= 12)
1107 char c = w->text[strlen(w->text)-1];
1108 if (c == ',' || c == ';' || c == ':' || c == '-' ||
1109 c == ')' || c == ']' || c == '}')
1113 if (se->nwords >= 25) /* ok that's just about enough out of you */
1116 if (s->mode == PAGE &&
1117 x + w->rbearing > right) /* wrap line */
1119 align_line (s, se, line_start, x, right);
1120 line_start = se->nwords;
1123 y += se->font->ascent;
1125 /* If we're close to the bottom of the screen, stop, and
1126 unread the current word. (But not if this is the first
1127 word, otherwise we might just get stuck on it.)
1129 if (se->nwords > 0 &&
1130 y + se->font->ascent > s->xgwa.height)
1138 w->target_x = x + w->lbearing;
1139 w->target_y = y - w->ascent;
1141 x += w->rbearing + space;
1144 if (se->nwords >= (array_size - 1))
1147 se->words = (word **) realloc (se->words,
1148 array_size * sizeof(*se->words));
1151 fprintf (stderr, "%s: out of memory (%d words)\n",
1152 progname, array_size);
1157 se->words[se->nwords++] = w;
1165 align_line (s, se, line_start, x, right);
1166 if (se->move_chars_p)
1167 split_words (s, se);
1168 scatter_sentence (s, se);
1169 shuffle_words (s, se);
1172 aim_sentence (s, se);
1182 fprintf (stderr, "%s: sentence %d:", progname, se->id);
1183 for (i = 0; i < se->nwords; i++)
1184 fprintf (stderr, " %s", se->words[i]->text);
1185 fprintf (stderr, "\n");
1191 /* Render a single word object to the screen.
1194 draw_word (state *s, sentence *se, word *w)
1196 if (! w->pixmap) return;
1198 if (w->x + w->width < 0 ||
1199 w->y + w->height < 0 ||
1200 w->x > s->xgwa.width ||
1201 w->y > s->xgwa.height)
1204 XSetClipMask (s->dpy, se->fg_gc, w->mask);
1205 XSetClipOrigin (s->dpy, se->fg_gc, w->x, w->y);
1206 XCopyPlane (s->dpy, w->pixmap, s->b, se->fg_gc,
1207 0, 0, w->width, w->height,
1213 /* If there is room for more sentences, add one.
1216 more_sentences (state *s)
1220 for (i = 0; i < s->nsentences; i++)
1222 sentence *se = s->sentences[i];
1225 se = new_sentence (s, s);
1226 populate_sentence (s, se);
1228 s->spawn_p = False, any = True;
1231 free_sentence (s, se);
1234 s->sentences[i] = se;
1236 s->latest_sentence = se->id;
1241 if (any) sort_sentences (s);
1245 /* Render all the words to the screen, and run the animation one step.
1248 draw_sentence (state *s, sentence *se)
1255 for (i = 0; i < se->nwords; i++)
1257 word *w = se->words[i];
1262 if (se->anim_state != PAUSE &&
1263 w->tick <= w->nticks)
1265 int dx = w->target_x - w->start_x;
1266 int dy = w->target_y - w->start_y;
1267 double r = sin (w->tick * M_PI / (2 * w->nticks));
1268 w->x = w->start_x + (dx * r);
1269 w->y = w->start_y + (dy * r);
1272 if (se->anim_state == OUT && s->mode == PAGE)
1273 w->tick++; /* go out faster */
1279 int dx = w->target_x - w->start_x;
1280 int dy = w->target_y - w->start_y;
1281 double r = (double) w->tick / w->nticks;
1282 w->x = w->start_x + (dx * r);
1283 w->y = w->start_y + (dy * r);
1285 moved = (w->tick <= w->nticks);
1287 /* Launch a new sentence when:
1288 - the front of this sentence is almost off the left edge;
1289 - the end of this sentence is almost on screen.
1292 if (se->anim_state != OUT &&
1294 se->id == s->latest_sentence)
1296 Bool new_p = (w->x < (s->xgwa.width * 0.4) &&
1297 w->x + se->width < (s->xgwa.width * 2.1));
1298 Bool rand_p = (new_p ? 0 : !(random() % 2000));
1300 if (new_p || rand_p)
1302 se->anim_state = OUT;
1306 fprintf (stderr, "%s: OUT %d (x2 = %d%s)\n",
1308 se->words[0]->x + se->width,
1309 rand_p ? " randomly" : "");
1320 draw_word (s, se, w);
1323 if (moved && se->anim_state == PAUSE)
1328 switch (se->anim_state)
1331 se->anim_state = PAUSE;
1332 se->pause_tick = (se->nwords * 7 * s->linger);
1333 if (se->move_chars_p)
1334 se->pause_tick /= 5;
1335 scatter_sentence (s, se);
1336 shuffle_words (s, se);
1339 fprintf (stderr, "%s: PAUSE %d\n", progname, se->id);
1343 if (--se->pause_tick <= 0)
1345 se->anim_state = OUT;
1349 fprintf (stderr, "%s: OUT %d\n", progname, se->id);
1356 fprintf (stderr, "%s: DEAD %d\n", progname, se->id);
1360 for (j = 0; j < s->nsentences; j++)
1361 if (s->sentences[j] == se)
1362 s->sentences[j] = 0;
1363 free_sentence (s, se);
1377 scale_ximage (Screen *screen, Window window, XImage *img, int scale,
1380 Display *dpy = DisplayOfScreen (screen);
1382 unsigned width = img->width, height = img->height;
1386 p2 = XCreatePixmap (dpy, window, width*scale, height*scale, img->depth);
1387 gc = XCreateGC (dpy, p2, 0, 0);
1389 XSetForeground (dpy, gc, BlackPixelOfScreen (screen));
1390 XFillRectangle (dpy, p2, gc, 0, 0, width*scale, height*scale);
1391 for (y = 0; y < height; y++)
1392 for (x = 0; x < width; x++)
1394 XSetForeground (dpy, gc, XGetPixel (img, x, y));
1395 XFillRectangle (dpy, p2, gc, x*scale, y*scale, scale, scale);
1400 XWindowAttributes xgwa;
1402 c.red = c.green = c.blue = 0x4444;
1403 c.flags = DoRed|DoGreen|DoBlue;
1404 XGetWindowAttributes (dpy, window, &xgwa);
1405 if (! XAllocColor (dpy, xgwa.colormap, &c)) abort();
1406 XSetForeground (dpy, gc, c.pixel);
1407 XDrawRectangle (dpy, p2, gc, 0, 0, width*scale-1, height*scale-1);
1408 XDrawRectangle (dpy, p2, gc, margin*scale, margin*scale,
1409 width*scale-1, height*scale-1);
1410 for (y = 0; y <= height - 2*margin; y++)
1411 XDrawLine (dpy, p2, gc,
1412 margin*scale, (y+margin)*scale-1,
1413 (width-margin)*scale, (y+margin)*scale-1);
1414 for (x = 0; x <= width - 2*margin; x++)
1415 XDrawLine (dpy, p2, gc,
1416 (x+margin)*scale-1, margin*scale,
1417 (x+margin)*scale-1, (height-margin)*scale);
1418 XFreeColors (dpy, xgwa.colormap, &c.pixel, 1, 0);
1426 static int check_edge (Display *dpy, Drawable p, GC gc,
1427 unsigned msg_x, unsigned msg_y, const char *msg,
1429 unsigned x, unsigned y, unsigned dim, unsigned end)
1440 XDrawString (dpy, p, gc, msg_x, msg_y, msg, strlen (msg));
1444 if (XGetPixel(img, pt[0], pt[1]) & 0xffffff)
1454 static unsigned long
1455 fontglide_draw_metrics (state *s)
1457 unsigned int margin = (s->debug_metrics_antialiasing_p ? 2 : 0);
1459 char txt[2], txt2[80];
1460 const char *fn = (s->font_override ? s->font_override : "fixed");
1461 XFontStruct *font = XLoadQueryFont (s->dpy, fn);
1462 XFontStruct *font2 = XLoadQueryFont (s->dpy, "fixed");
1463 XCharStruct c, overall;
1464 int dir, ascent, descent;
1466 int sc = s->debug_scale;
1468 unsigned long red = 0xFFFF0000; /* so shoot me */
1469 unsigned long green = 0xFF00FF00;
1470 unsigned long blue = 0xFF6666FF;
1471 unsigned long yellow = 0xFFFFFF00;
1472 unsigned long cyan = 0xFF004040;
1474 Drawable dest = s->b ? s->b : s->window;
1478 txt[0] = s->debug_metrics_p;
1481 gc = XCreateGC (s->dpy, dest, 0, 0);
1482 XSetFont (s->dpy, gc, font->fid);
1485 jwxyz_XSetAntiAliasing (s->dpy, gc, False);
1488 XTextExtents (font, txt, strlen(txt),
1489 &dir, &ascent, &descent, &overall);
1490 c = (s->debug_metrics_p >= font->min_char_or_byte2
1491 ? font->per_char[s->debug_metrics_p - font->min_char_or_byte2]
1494 XSetForeground (s->dpy, gc, BlackPixelOfScreen (s->xgwa.screen));
1495 XFillRectangle (s->dpy, dest, gc, 0, 0, s->xgwa.width, s->xgwa.height);
1497 x = (s->xgwa.width - overall.width) / 2;
1498 y = (s->xgwa.height - (2 * sc * (ascent + descent))) / 2;
1500 for (i = 0; i < 2; i++)
1502 XCharStruct cc = (i == 0 ? c : overall);
1503 int x1 = s->xgwa.width * 0.15;
1504 int x2 = s->xgwa.width * 0.85;
1505 int x3 = s->xgwa.width;
1507 int pixw = margin * 2 + cc.rbearing - cc.lbearing;
1508 int pixh = margin * 2 + cc.ascent + cc.descent;
1509 Pixmap p = (pixw > 0 && pixh > 0
1510 ? XCreatePixmap (s->dpy, dest, pixw, pixh, s->xgwa.depth)
1516 GC gc2 = XCreateGC (s->dpy, p, 0, 0);
1518 jwxyz_XSetAntiAliasing (s->dpy, gc2, False);
1520 XSetFont (s->dpy, gc2, font->fid);
1521 XSetForeground (s->dpy, gc2, BlackPixelOfScreen (s->xgwa.screen));
1522 XFillRectangle (s->dpy, p, gc2, 0, 0, pixw, pixh);
1523 XSetForeground (s->dpy, gc, WhitePixelOfScreen (s->xgwa.screen));
1524 XSetForeground (s->dpy, gc2, WhitePixelOfScreen (s->xgwa.screen));
1526 jwxyz_XSetAntiAliasing (s->dpy, gc2, s->debug_metrics_antialiasing_p);
1528 XDrawString (s->dpy, p, gc2,
1529 -cc.lbearing + margin,
1534 XImage *img = XGetImage (s->dpy, p, 0, 0, pixw, pixh,
1540 unsigned w = pixw - margin * 2, h = pixh - margin * 2;
1544 /* Check for ink escape. */
1545 unsigned long ink = 0;
1546 for (y2 = 0; y2 != pixh; ++y2)
1547 for (x2 = 0; x2 != pixw; ++x2)
1550 if (! (x2 >= margin &&
1551 x2 < pixw - margin &&
1553 y2 < pixh - margin))
1554 ink |= XGetPixel (img, x2, y2);
1559 XSetFont (s->dpy, gc, font2->fid);
1560 XDrawString (s->dpy, dest, gc, 10, 40, "Ink escape!", 11);
1564 /* ...And wasted space. */
1567 if (check_edge (s->dpy, dest, gc, 120, 60, "left",
1568 img, margin, margin, 1, h) |
1569 check_edge (s->dpy, dest, gc, 160, 60, "right",
1570 img, margin + w - 1, margin, 1, h) |
1571 check_edge (s->dpy, dest, gc, 200, 60, "top",
1572 img, margin, margin, 0, w) |
1573 check_edge (s->dpy, dest, gc, 240, 60, "bottom",
1574 img, margin, margin + h - 1, 0, w))
1576 XSetFont (s->dpy, gc, font2->fid);
1577 XDrawString (s->dpy, dest, gc, 10, 60,
1578 "Wasted space: ", 14);
1583 if (s->debug_metrics_antialiasing_p)
1585 /* Draw a dark cyan boundary around antialiased glyphs */
1586 img2 = XCreateImage (s->dpy, s->xgwa.visual, img->depth,
1588 img->width, img->height,
1589 img->bitmap_pad, 0);
1590 img2->data = malloc (img->bytes_per_line * img->height);
1592 for (y2 = 0; y2 != pixh; ++y2)
1593 for (x2 = 0; x2 != pixw; ++x2)
1595 unsigned long px = XGetPixel (img, x2, y2);
1596 if ((px & 0xffffff) == 0)
1598 unsigned long neighbors = 0;
1600 neighbors |= XGetPixel (img, x2 - 1, y2);
1602 neighbors |= XGetPixel (img, x2 + 1, y2);
1604 neighbors |= XGetPixel (img, x2, y2 - 1);
1606 neighbors |= XGetPixel (img, x2, y2 + 1);
1607 XPutPixel (img2, x2, y2,
1608 (neighbors & 0xffffff
1610 : BlackPixelOfScreen (s->xgwa.screen)));
1614 XPutPixel (img2, x2, y2, px);
1624 p2 = scale_ximage (s->xgwa.screen, s->window, img2, sc, margin);
1626 XDestroyImage (img);
1627 XDestroyImage (img2);
1630 XCopyArea (s->dpy, p2, dest, gc,
1631 0, 0, sc*pixw, sc*pixh,
1632 x + sc * (cc.lbearing - margin),
1633 y - sc * (cc.ascent + margin));
1634 XFreePixmap (s->dpy, p);
1635 XFreePixmap (s->dpy, p2);
1636 XFreeGC (s->dpy, gc2);
1641 XSetFont (s->dpy, gc, font->fid);
1642 XSetForeground (s->dpy, gc, WhitePixelOfScreen (s->xgwa.screen));
1644 jwxyz_XSetAntiAliasing (s->dpy, gc, s->debug_metrics_antialiasing_p);
1646 sprintf (txt2, "%c [XX%cXX] [%c%c%c%c]",
1647 s->debug_metrics_p, s->debug_metrics_p,
1648 s->debug_metrics_p, s->debug_metrics_p,
1649 s->debug_metrics_p, s->debug_metrics_p);
1650 XDrawString (s->dpy, dest, gc,
1651 x + (sc*cc.rbearing/2) - cc.rbearing/2, ascent + 10,
1652 txt2, strlen(txt2));
1654 jwxyz_XSetAntiAliasing (s->dpy, gc, False);
1656 XSetFont (s->dpy, gc, font2->fid);
1657 sprintf (txt2, "%c %3d 0%03o 0x%02x%s",
1658 s->debug_metrics_p, s->debug_metrics_p,
1659 s->debug_metrics_p, s->debug_metrics_p,
1660 (txt[0] < font->min_char_or_byte2 ? " *" : ""));
1661 XDrawString (s->dpy, dest, gc,
1663 txt2, strlen(txt2));
1667 jwxyz_XSetAntiAliasing (s->dpy, gc, True);
1670 XSetFont (s->dpy, gc, font2->fid);
1672 XSetForeground (s->dpy, gc, red);
1674 sprintf (txt2, "%s ascent %d",
1675 (i == 0 ? "char" : "overall"),
1677 XDrawString (s->dpy, dest, gc, 10, y - sc*ascent - 2,
1678 txt2, strlen(txt2));
1679 XDrawLine (s->dpy, dest, gc, 0, y - sc*ascent, x3, y - sc*ascent);
1681 sprintf (txt2, "%s descent %d",
1682 (i == 0 ? "char" : "overall"),
1684 XDrawString (s->dpy, dest, gc, 10, y + sc*descent - 2,
1685 txt2, strlen(txt2));
1686 XDrawLine (s->dpy, dest, gc, 0, y + sc*descent, x3, y + sc*descent);
1689 /* ascent, descent, baseline */
1691 XSetForeground (s->dpy, gc, green);
1693 sprintf (txt2, "ascent %d", cc.ascent);
1695 XDrawString (s->dpy, dest, gc, x1, y - sc*cc.ascent - 2,
1696 txt2, strlen(txt2));
1697 XDrawLine (s->dpy, dest, gc,
1698 x1, y - sc*cc.ascent, x2, y - sc*cc.ascent);
1700 sprintf (txt2, "descent %d", cc.descent);
1701 if (cc.descent != 0)
1702 XDrawString (s->dpy, dest, gc, x1, y + sc*cc.descent - 2,
1703 txt2, strlen(txt2));
1704 XDrawLine (s->dpy, dest, gc,
1705 x1, y + sc*cc.descent, x2, y + sc*cc.descent);
1707 XSetForeground (s->dpy, gc, yellow);
1708 strcpy (txt2, "baseline");
1709 XDrawString (s->dpy, dest, gc, x1, y - 2,
1710 txt2, strlen(txt2));
1711 XDrawLine (s->dpy, dest, gc, x1, y, x2, y);
1716 XSetForeground (s->dpy, gc, blue);
1718 strcpy (txt2, "origin");
1719 XDrawString (s->dpy, dest, gc,
1720 x + 2, y + sc*(descent + 10),
1721 txt2, strlen(txt2));
1722 XDrawLine (s->dpy, dest, gc,
1723 x, y - sc*(ascent - 10),
1724 x, y + sc*(descent + 10));
1726 sprintf (txt2, "width %d", cc.width);
1727 XDrawString (s->dpy, dest, gc,
1728 x + sc*cc.width + 2,
1729 y + sc*(descent + 10) + 10,
1730 txt2, strlen(txt2));
1731 XDrawLine (s->dpy, dest, gc,
1732 x + sc*cc.width, y - sc*(ascent - 10),
1733 x + sc*cc.width, y + sc*(descent + 10));
1736 /* lbearing, rbearing */
1738 XSetForeground (s->dpy, gc, green);
1740 sprintf (txt2, "lbearing %d", cc.lbearing);
1741 XDrawString (s->dpy, dest, gc, x + sc*cc.lbearing + 2,
1742 y + sc * descent + 30,
1743 txt2, strlen(txt2));
1744 XDrawLine (s->dpy, dest, gc,
1745 x + sc*cc.lbearing, y - sc*ascent,
1746 x + sc*cc.lbearing, y + sc*descent + 20);
1748 sprintf (txt2, "rbearing %d", cc.rbearing);
1749 XDrawString (s->dpy, dest, gc, x + sc*cc.rbearing + 2,
1750 y + sc * descent + 40,
1751 txt2, strlen(txt2));
1752 XDrawLine (s->dpy, dest, gc,
1753 x + sc*cc.rbearing, y - sc*ascent,
1754 x + sc*cc.rbearing, y + sc*descent + 40);
1756 y += sc * (ascent + descent) * 2;
1759 if (dest != s->window)
1760 XCopyArea (s->dpy, dest, s->window, s->bg_gc,
1761 0, 0, s->xgwa.width, s->xgwa.height, 0, 0);
1763 XFreeGC (s->dpy, gc);
1764 XFreeFont (s->dpy, font);
1765 XFreeFont (s->dpy, font2);
1766 return s->frame_delay;
1772 /* Render all the words to the screen, and run the animation one step.
1773 Clear screen first, swap buffers after.
1775 static unsigned long
1776 fontglide_draw (Display *dpy, Window window, void *closure)
1778 state *s = (state *) closure;
1782 if (s->debug_metrics_p)
1783 return fontglide_draw_metrics (closure);
1790 XFillRectangle (s->dpy, s->b, s->bg_gc,
1791 0, 0, s->xgwa.width, s->xgwa.height);
1793 for (i = 0; i < s->nsentences; i++)
1794 draw_sentence (s, s->sentences[i]);
1796 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1799 XdbeSwapInfo info[1];
1800 info[0].swap_window = s->window;
1801 info[0].swap_action = (s->dbeclear_p ? XdbeBackground : XdbeUndefined);
1802 XdbeSwapBuffers (s->dpy, info, 1);
1805 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
1808 XCopyArea (s->dpy, s->b, s->window, s->bg_gc,
1809 0, 0, s->xgwa.width, s->xgwa.height, 0, 0);
1812 return s->frame_delay;
1817 /* When the subprocess has generated some output, this reads as much as it
1818 can into s->buf at s->buf_tail.
1821 drain_input (state *s)
1823 while (s->buf_tail < sizeof(s->buf) - 2)
1825 int c = textclient_getc (s->tc);
1827 s->buf[s->buf_tail++] = (char) c;
1834 /* Window setup and resource loading */
1837 fontglide_init (Display *dpy, Window window)
1840 state *s = (state *) calloc (1, sizeof(*s));
1843 s->frame_delay = get_integer_resource (dpy, "delay", "Integer");
1845 XGetWindowAttributes (s->dpy, s->window, &s->xgwa);
1847 s->font_override = get_string_resource (dpy, "font", "Font");
1848 if (s->font_override && (!*s->font_override || *s->font_override == '('))
1849 s->font_override = 0;
1851 s->charset = get_string_resource (dpy, "fontCharset", "FontCharset");
1852 s->border_width = get_integer_resource (dpy, "fontBorderWidth", "Integer");
1853 if (s->border_width < 0 || s->border_width > 20)
1854 s->border_width = 1;
1856 s->speed = get_float_resource (dpy, "speed", "Float");
1857 if (s->speed <= 0 || s->speed > 200)
1860 s->linger = get_float_resource (dpy, "linger", "Float");
1861 if (s->linger <= 0 || s->linger > 200)
1864 s->trails_p = get_boolean_resource (dpy, "trails", "Trails");
1867 s->debug_p = get_boolean_resource (dpy, "debug", "Debug");
1868 s->debug_metrics_p = (get_boolean_resource (dpy, "debugMetrics", "Debug")
1873 s->dbuf = get_boolean_resource (dpy, "doubleBuffer", "Boolean");
1875 # ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
1880 if (s->debug_metrics_p) s->trails_p = False;
1883 if (s->trails_p) s->dbuf = False; /* don't need it in this case */
1886 const char *ss = get_string_resource (dpy, "mode", "Mode");
1887 if (!ss || !*ss || !strcasecmp (ss, "random"))
1888 s->mode = ((random() % 2) ? SCROLL : PAGE);
1889 else if (!strcasecmp (ss, "scroll"))
1891 else if (!strcasecmp (ss, "page"))
1896 "%s: `mode' must be `scroll', `page', or `random', not `%s'\n",
1903 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1904 s->dbeclear_p = get_boolean_resource (dpy, "useDBEClear", "Boolean");
1906 s->b = xdbe_get_backbuffer (dpy, window, XdbeBackground);
1908 s->b = xdbe_get_backbuffer (dpy, window, XdbeUndefined);
1910 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
1914 s->ba = XCreatePixmap (s->dpy, s->window,
1915 s->xgwa.width, s->xgwa.height,
1925 gcv.foreground = get_pixel_resource (s->dpy, s->xgwa.colormap,
1926 "background", "Background");
1927 s->bg_gc = XCreateGC (s->dpy, s->b, GCForeground, &gcv);
1929 s->nsentences = 5; /* #### */
1930 s->sentences = (sentence **) calloc (s->nsentences, sizeof (sentence *));
1934 s->start_time = time ((time_t *) 0);
1935 s->tc = textclient_open (dpy);
1942 fontglide_event (Display *dpy, Window window, void *closure, XEvent *event)
1945 state *s = (state *) closure;
1947 if (! s->debug_metrics_p)
1949 if (event->xany.type == KeyPress)
1953 XLookupString (&event->xkey, &c, 1, &keysym, 0);
1955 s->debug_metrics_antialiasing_p ^= True;
1956 else if (c == 3 || c == 27)
1959 s->debug_metrics_p = (unsigned char) c;
1960 else if (keysym == XK_Left || keysym == XK_Right)
1962 s->debug_metrics_p += (keysym == XK_Left ? -1 : 1);
1963 if (s->debug_metrics_p > 255)
1964 s->debug_metrics_p = 1;
1965 else if (s->debug_metrics_p <= 0)
1966 s->debug_metrics_p = 255;
1969 else if (keysym == XK_Up)
1971 else if (keysym == XK_Down)
1972 s->debug_scale = (s->debug_scale > 1 ? s->debug_scale-1 : 1);
1984 fontglide_reshape (Display *dpy, Window window, void *closure,
1985 unsigned int w, unsigned int h)
1987 state *s = (state *) closure;
1988 XGetWindowAttributes (s->dpy, s->window, &s->xgwa);
1990 if (s->dbuf && s->ba)
1992 XFreePixmap (s->dpy, s->ba);
1993 s->ba = XCreatePixmap (s->dpy, s->window,
1994 s->xgwa.width, s->xgwa.height,
1996 XFillRectangle (s->dpy, s->ba, s->bg_gc, 0, 0,
1997 s->xgwa.width, s->xgwa.height);
2003 fontglide_free (Display *dpy, Window window, void *closure)
2005 state *s = (state *) closure;
2006 textclient_close (s->tc);
2008 /* #### there's more to free here */
2014 static const char *fontglide_defaults [] = {
2015 ".background: #000000",
2016 ".foreground: #DDDDDD",
2017 ".borderColor: #555555",
2019 "*program: xscreensaver-text",
2023 "*fontCharset: iso8859-1",
2024 "*fontBorderWidth: 2",
2030 "*debugMetrics: False",
2032 "*doubleBuffer: True",
2033 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2035 "*useDBEClear: True",
2036 # endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
2040 static XrmOptionDescRec fontglide_options [] = {
2041 { "-mode", ".mode", XrmoptionSepArg, 0 },
2042 { "-scroll", ".mode", XrmoptionNoArg, "scroll" },
2043 { "-page", ".mode", XrmoptionNoArg, "page" },
2044 { "-random", ".mode", XrmoptionNoArg, "random" },
2045 { "-delay", ".delay", XrmoptionSepArg, 0 },
2046 { "-speed", ".speed", XrmoptionSepArg, 0 },
2047 { "-linger", ".linger", XrmoptionSepArg, 0 },
2048 { "-program", ".program", XrmoptionSepArg, 0 },
2049 { "-font", ".font", XrmoptionSepArg, 0 },
2050 { "-fn", ".font", XrmoptionSepArg, 0 },
2051 { "-bw", ".fontBorderWidth", XrmoptionSepArg, 0 },
2052 { "-trails", ".trails", XrmoptionNoArg, "True" },
2053 { "-no-trails", ".trails", XrmoptionNoArg, "False" },
2054 { "-db", ".doubleBuffer", XrmoptionNoArg, "True" },
2055 { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" },
2057 { "-debug", ".debug", XrmoptionNoArg, "True" },
2058 { "-debug-metrics", ".debugMetrics", XrmoptionNoArg, "True" },
2064 XSCREENSAVER_MODULE ("FontGlide", fontglide)