X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Ffontglide.c;h=73e65d49b1b52f1fd31629d14beae94bfb75e12c;hp=c1e4bdd343623857a64e3696620194851132a6e9;hb=49f5b54f312fe4ac2e9bc47581a72451bd0e8439;hpb=ccb7f4903325f92555a9722bba74b58346654ba0 diff --git a/hacks/fontglide.c b/hacks/fontglide.c index c1e4bdd3..73e65d49 100644 --- a/hacks/fontglide.c +++ b/hacks/fontglide.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2003, 2005 Jamie Zawinski +/* xscreensaver, Copyright (c) 2003, 2005, 2006 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -13,17 +13,26 @@ * Requires a system with scalable fonts. (X's font handing sucks. A lot.) */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + #include + +#ifndef HAVE_COCOA +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + #include "screenhack.h" -#include #ifdef HAVE_DOUBLE_BUFFER_EXTENSION #include "xdbe.h" #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ -extern XtAppContext app; - - typedef struct { char *text; int x, y, width, height; @@ -69,10 +78,10 @@ typedef struct { #ifdef HAVE_DOUBLE_BUFFER_EXTENSION XdbeBackBuffer backb; + Bool dbeclear_p; #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ Bool dbuf; /* Whether we're using double buffering. */ - Bool dbeclear_p; /* ? */ int border_width; /* size of the font outline */ char *charset; /* registry and encoding for font lookups */ @@ -80,14 +89,16 @@ typedef struct { 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; char *font_override; /* if -font was specified on the cmd line */ FILE *pipe; + XtIntervalId timer_id; XtInputId pipe_id; Time subproc_relaunch_delay; - Bool input_available_p; + /* Bool input_available_p; */ char buf [40]; /* this only needs to be as big as one "word". */ int buf_tail; @@ -97,6 +108,10 @@ typedef struct { Bool spawn_p; /* whether it is time to create a new sentence */ int latest_sentence; + unsigned long frame_delay; + + int id_tick; + } state; @@ -104,6 +119,29 @@ static void launch_text_generator (state *); static void drain_input (state *s); +static int +pick_font_size (state *s) +{ + double scale = s->xgwa.height / 1024.0; /* shrink for small windows */ + int min, max, r, pixel; + + min = scale * 24; + max = scale * 260; + + if (min < 10) min = 10; + if (max < 30) max = 30; + + r = ((max-min)/3)+1; + + pixel = min + ((random() % r) + (random() % r) + (random() % r)); + + if (s->mode == SCROLL) /* scroll mode likes bigger fonts */ + pixel *= 1.5; + + return pixel; +} + + /* Finds the set of scalable fonts on the system; picks one; and loads that font in a random pixel size. Returns False if something went wrong. @@ -111,13 +149,15 @@ static void drain_input (state *s); static Bool pick_font_1 (state *s, sentence *se) { + Bool ok = False; char pattern[1024]; + +# ifndef HAVE_COCOA /* real Xlib */ char **names = 0; char **names2 = 0; XFontStruct *info = 0; int count = 0, count2 = 0; int i; - Bool ok = False; if (se->font) { @@ -212,23 +252,7 @@ pick_font_1 (state *s, sentence *se) #undef INT #undef STR - { - double scale = s->xgwa.height / 1024.0; /* shrink for small windows */ - int min, max, r; - - min = scale * 24; - max = scale * 260; - - if (min < 10) min = 10; - if (max < 30) max = 30; - - r = ((max-min)/3)+1; - - pixel = min + ((random() % r) + (random() % r) + (random() % r)); - - if (s->mode == SCROLL) /* scroll mode likes bigger fonts */ - pixel *= 1.5; - } + pixel = pick_font_size (s); #if 0 /* Occasionally change the aspect ratio of the font, by increasing @@ -279,6 +303,21 @@ pick_font_1 (state *s, sentence *se) XFreeFontInfo (names2, info, count2); XFreeFontNames (names); +# else /* HAVE_COCOA */ + + if (s->font_override) + sprintf (pattern, "%.200s", s->font_override); + else + { + const char *family = "random"; + const char *weight = ((random() % 2) ? "normal" : "bold"); + const char *slant = ((random() % 2) ? "o" : "r"); + int size = 10 * pick_font_size (s); + sprintf (pattern, "*-%s-%s-%s-*-%d-*", family, weight, slant, size); + } + ok = True; +# endif /* HAVE_COCOA */ + if (! ok) return False; se->font = XLoadQueryFont (s->dpy, pattern); @@ -344,11 +383,13 @@ get_word_text (state *s) char *result = 0; int lfs = 0; + drain_input (s); + if (unread_word_text) { - char *s = unread_word_text; + start = unread_word_text; unread_word_text = 0; - return s; + return start; } /* Skip over whitespace at the beginning of the buffer, @@ -399,9 +440,6 @@ get_word_text (state *s) s->buf_tail -= n; } - /* See if there is more to be read, now that there's room in the buffer. */ - drain_input (s); - return result; } @@ -422,6 +460,12 @@ new_word (state *s, sentence *se, char *txt, Bool alloc_p) 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; @@ -482,6 +526,39 @@ new_word (state *s, sentence *se, char *txt, Bool alloc_p) 0, 0, w->width-1, w->height-1); } +#if 0 + if (s->debug_p) + { + /* bounding box (behind *each* character) */ + char *ss; + int x = 0; + for (ss = txt; *ss; ss++) + { + XTextExtents (se->font, ss, 1, &dir, &ascent, &descent, &overall); + XDrawRectangle (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1), + x, w->ascent - overall.ascent, + overall.width, + overall.ascent + overall.descent); + XDrawRectangle (s->dpy, w->mask, gc1, + x, w->ascent - overall.ascent, + overall.width, + overall.ascent + overall.descent); + + XDrawRectangle (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1), + x - overall.lbearing, w->ascent - overall.ascent, + overall.rbearing, + overall.ascent + overall.descent); + XDrawRectangle (s->dpy, w->mask, gc1, + x - overall.lbearing, w->ascent - overall.ascent, + overall.rbearing, + overall.ascent + overall.descent); + + + x += overall.width; + } + } +#endif + /* Draw foreground text */ XDrawString (s->dpy, w->pixmap, gc1, -w->lbearing, w->ascent, txt, strlen(txt)); @@ -498,7 +575,7 @@ new_word (state *s, sentence *se, char *txt, Bool alloc_p) if (s->debug_p) { - XSetFunction (s->dpy, gc1, GXset); + XSetFunction (s->dpy, gc1, GXcopy); if (w->ascent != w->height) { /* baseline (on top of the characters) */ @@ -546,14 +623,13 @@ free_word (state *s, word *w) static sentence * -new_sentence (state *s) +new_sentence (state *st, state *s) { - static int id = 0; XGCValues gcv; sentence *se = (sentence *) calloc (1, sizeof (*se)); se->fg_gc = XCreateGC (s->dpy, s->b, 0, &gcv); se->anim_state = IN; - se->id = ++id; + se->id = ++st->id_tick; return se; } @@ -1116,7 +1192,7 @@ more_sentences (state *s) sentence *se = s->sentences[i]; if (! se) { - se = new_sentence (s); + se = new_sentence (s, s); populate_sentence (s, se); if (se->nwords > 0) s->spawn_p = False, any = True; @@ -1265,14 +1341,99 @@ draw_sentence (state *s, sentence *se) } +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; + int dir, ascent, descent; + int x, y; + GC gc; + unsigned long red = 0xFFFF0000; /* so shoot me */ + unsigned long green = 0xFF00FF00; + unsigned long blue = 0xFF6666FF; + int i; + + txt[0] = s->debug_metrics_p; + txt[1] = 0; + + gc = XCreateGC (s->dpy, s->window, 0, 0); + XSetFont (s->dpy, gc, font->fid); + +#ifdef HAVE_COCOA + jwxyz_XSetAntiAliasing (s->dpy, gc, False); +#endif + + XTextExtents (font, txt, strlen(txt), + &dir, &ascent, &descent, &overall); + c = font->per_char[((unsigned char *) txt)[0] - font->min_char_or_byte2]; + + XClearWindow (s->dpy, s->window); + + x = (s->xgwa.width - overall.width) / 2; + y = (s->xgwa.height - (2 * (ascent + descent))) / 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; + + 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); + + 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); + + /* 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); + + /* 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)); + + y += (ascent + descent) * 2; + } + + XFreeGC (s->dpy, gc); + XFreeFont (s->dpy, font); + return s->frame_delay; +} + + /* Render all the words to the screen, and run the animation one step. Clear screen first, swap buffers after. */ -static void -draw_fontglide (state *s) +static unsigned long +fontglide_draw (Display *dpy, Window window, void *closure) { + state *s = (state *) closure; int i; + if (s->debug_metrics_p) + return fontglide_draw_metrics (closure); + if (s->spawn_p) more_sentences (s); @@ -1298,38 +1459,12 @@ draw_fontglide (state *s) XCopyArea (s->dpy, s->b, s->window, s->bg_gc, 0, 0, s->xgwa.width, s->xgwa.height, 0, 0); } -} - - -static void -handle_events (state *s) -{ - while (XPending (s->dpy)) - { - XEvent event; - XNextEvent (s->dpy, &event); - - if (event.xany.type == ConfigureNotify) - { - XGetWindowAttributes (s->dpy, s->window, &s->xgwa); - if (s->dbuf && (s->ba)) - { - XFreePixmap (s->dpy, s->ba); - s->ba = XCreatePixmap (s->dpy, s->window, - s->xgwa.width, s->xgwa.height, - s->xgwa.depth); - XFillRectangle (s->dpy, s->ba, s->bg_gc, 0, 0, - s->xgwa.width, s->xgwa.height); - s->b = s->ba; - } - } - - screenhack_handle_event (s->dpy, &event); - } + return s->frame_delay; } + /* Subprocess. (This bit mostly cribbed from phosphor.c) @@ -1338,15 +1473,15 @@ handle_events (state *s) static void subproc_cb (XtPointer closure, int *source, XtInputId *id) { - state *s = (state *) closure; - s->input_available_p = True; + /* state *s = (state *) closure; */ + /* s->input_available_p = True; */ } static void launch_text_generator (state *s) { - char *oprogram = get_string_resource ("program", "Program"); + char *oprogram = get_string_resource (s->dpy, "program", "Program"); char *program = (char *) malloc (strlen (oprogram) + 10); strcpy (program, "( "); strcat (program, oprogram); @@ -1358,7 +1493,8 @@ launch_text_generator (state *s) if ((s->pipe = popen (program, "r"))) { s->pipe_id = - XtAppAddInput (app, fileno (s->pipe), + XtAppAddInput (XtDisplayToApplicationContext (s->dpy), + fileno (s->pipe), (XtPointer) (XtInputReadMask | XtInputExceptMask), subproc_cb, (XtPointer) s); } @@ -1373,26 +1509,46 @@ static void relaunch_generator_timer (XtPointer closure, XtIntervalId *id) { state *s = (state *) closure; + if (!s->timer_id) abort(); + s->timer_id = 0; launch_text_generator (s); } +/* whether there is data available to be read on the file descriptor + */ +static int +input_available_p (int fd) +{ + struct timeval tv = { 0, }; + fd_set fds; +# if 0 + /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */ + FD_ZERO (&fds); +# else + memset (&fds, 0, sizeof(fds)); +# endif + FD_SET (fd, &fds); + return select (fd+1, &fds, NULL, NULL, &tv); +} + + /* When the subprocess has generated some output, this reads as much as it can into s->buf at s->buf_tail. */ static void drain_input (state *s) { - /* allow subproc_cb() to run, if the select() down in Xt says that - input is available. This tells us whether we can read() without - blocking. */ - if (! s->input_available_p) - if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput)) - XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput); + XtAppContext app = XtDisplayToApplicationContext (s->dpy); if (! s->pipe) return; - if (! s->input_available_p) return; - s->input_available_p = False; + + /* if (! s->input_available_p) return; */ + /* s->input_available_p = False; */ + + if (! input_available_p (fileno (s->pipe))) + return; + if (s->buf_tail < sizeof(s->buf) - 2) { @@ -1427,9 +1583,10 @@ drain_input (state *s) s->buf[s->buf_tail] = 0; /* Set up a timer to re-launch the subproc in a bit. */ - XtAppAddTimeOut (app, s->subproc_relaunch_delay, - relaunch_generator_timer, - (XtPointer) s); + if (s->timer_id) abort(); + s->timer_id = XtAppAddTimeOut (app, s->subproc_relaunch_delay, + relaunch_generator_timer, + (XtPointer) s); } } } @@ -1437,43 +1594,49 @@ drain_input (state *s) /* Window setup and resource loading */ -static state * -init_fontglide (Display *dpy, Window window) +static void * +fontglide_init (Display *dpy, Window window) { XGCValues gcv; state *s = (state *) calloc (1, sizeof(*s)); s->dpy = dpy; s->window = window; + s->frame_delay = get_integer_resource (dpy, "delay", "Integer"); XGetWindowAttributes (s->dpy, s->window, &s->xgwa); - s->font_override = get_string_resource ("font", "Font"); + s->font_override = get_string_resource (dpy, "font", "Font"); if (s->font_override && (!*s->font_override || *s->font_override == '(')) s->font_override = 0; - s->charset = get_string_resource ("fontCharset", "FontCharset"); - s->border_width = get_integer_resource ("fontBorderWidth", "Integer"); + s->charset = get_string_resource (dpy, "fontCharset", "FontCharset"); + s->border_width = get_integer_resource (dpy, "fontBorderWidth", "Integer"); if (s->border_width < 0 || s->border_width > 20) s->border_width = 1; - s->speed = get_float_resource ("speed", "Float"); + s->speed = get_float_resource (dpy, "speed", "Float"); if (s->speed <= 0 || s->speed > 200) s->speed = 1; - s->linger = get_float_resource ("linger", "Float"); + s->linger = get_float_resource (dpy, "linger", "Float"); if (s->linger <= 0 || s->linger > 200) s->linger = 1; - s->debug_p = get_boolean_resource ("debug", "Debug"); - s->trails_p = get_boolean_resource ("trails", "Trails"); + s->trails_p = get_boolean_resource (dpy, "trails", "Trails"); + s->debug_p = get_boolean_resource (dpy, "debug", "Debug"); + s->debug_metrics_p = (get_boolean_resource (dpy, "debugMetrics", "Debug") + ? 'y' : 0); + + s->dbuf = get_boolean_resource (dpy, "doubleBuffer", "Boolean"); - s->dbuf = get_boolean_resource ("doubleBuffer", "Boolean"); - s->dbeclear_p = get_boolean_resource ("useDBEClear", "Boolean"); +# ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */ + s->dbuf = False; +# endif if (s->trails_p) s->dbuf = False; /* don't need it in this case */ { - char *ss = get_string_resource ("mode", "Mode"); + 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")) @@ -1491,6 +1654,7 @@ init_fontglide (Display *dpy, Window window) if (s->dbuf) { #ifdef HAVE_DOUBLE_BUFFER_EXTENSION + s->dbeclear_p = get_boolean_resource (dpy, "useDBEClear", "Boolean"); if (s->dbeclear_p) s->b = xdbe_get_backbuffer (dpy, window, XdbeBackground); else @@ -1511,13 +1675,14 @@ init_fontglide (Display *dpy, Window window) s->b = s->window; } - gcv.foreground = get_pixel_resource ("background", "Background", - s->dpy, s->xgwa.colormap); + gcv.foreground = get_pixel_resource (s->dpy, s->xgwa.colormap, + "background", "Background"); s->bg_gc = XCreateGC (s->dpy, s->b, GCForeground, &gcv); s->subproc_relaunch_delay = 2 * 1000; - launch_text_generator (s); + if (! s->debug_metrics_p) + launch_text_generator (s); s->nsentences = 5; /* #### */ s->sentences = (sentence **) calloc (s->nsentences, sizeof (sentence *)); @@ -1527,10 +1692,75 @@ init_fontglide (Display *dpy, Window window) } - -char *progclass = "FontGlide"; +static Bool +fontglide_event (Display *dpy, Window window, void *closure, XEvent *event) +{ + 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) + { + KeySym keysym; + char c = 0; + XLookupString (&event->xkey, &c, 1, &keysym, 0); + if (c) + s->debug_metrics_p = (unsigned char) c; + return !!c; + } + else + return False; +} -char *defaults [] = { + +static void +fontglide_reshape (Display *dpy, Window window, void *closure, + unsigned int w, unsigned int h) +{ + state *s = (state *) closure; + XGetWindowAttributes (s->dpy, s->window, &s->xgwa); + + if (s->dbuf && (s->ba)) + { + XFreePixmap (s->dpy, s->ba); + s->ba = XCreatePixmap (s->dpy, s->window, + s->xgwa.width, s->xgwa.height, + s->xgwa.depth); + XFillRectangle (s->dpy, s->ba, s->bg_gc, 0, 0, + s->xgwa.width, s->xgwa.height); + s->b = s->ba; + } +} + +static void +fontglide_free (Display *dpy, Window window, void *closure) +{ + state *s = (state *) closure; + + if (s->pipe_id) + XtRemoveInput (s->pipe_id); + if (s->pipe) + pclose (s->pipe); + if (s->timer_id) + XtRemoveTimeOut (s->timer_id); + + /* #### there's more to free here */ + + free (s); +} + + +static const char *fontglide_defaults [] = { ".background: #000000", ".foreground: #DDDDDD", ".borderColor: #555555", @@ -1544,6 +1774,7 @@ char *defaults [] = { "*linger: 1.0", "*trails: False", "*debug: False", + "*debugMetrics: False", "*doubleBuffer: True", #ifdef HAVE_DOUBLE_BUFFER_EXTENSION "*useDBE: True", @@ -1552,7 +1783,8 @@ char *defaults [] = { 0 }; -XrmOptionDescRec options [] = { +static XrmOptionDescRec fontglide_options [] = { + { "-mode", ".mode", XrmoptionSepArg, 0 }, { "-scroll", ".mode", XrmoptionNoArg, "scroll" }, { "-page", ".mode", XrmoptionNoArg, "page" }, { "-random", ".mode", XrmoptionNoArg, "random" }, @@ -1568,21 +1800,9 @@ XrmOptionDescRec options [] = { { "-db", ".doubleBuffer", XrmoptionNoArg, "True" }, { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" }, { "-debug", ".debug", XrmoptionNoArg, "True" }, + { "-debug-metrics", ".debugMetrics", XrmoptionNoArg, "True" }, { 0, 0, 0, 0 } }; -void -screenhack (Display *dpy, Window window) -{ - state *s = init_fontglide (dpy, window); - int delay = get_integer_resource ("delay", "Integer"); - - while (1) - { - handle_events (s); - draw_fontglide (s); - XSync (dpy, False); - if (delay) usleep (delay); - } -} +XSCREENSAVER_MODULE ("FontGlide", fontglide)