From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / noseguy.c
index 144e6859bd0f00d4e0d88620fac6ef91edb06d45..c5cc9c08d491e5ff7639e4d3a61493533be68780 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1992-2008 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1992-2014 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
 
 #include "screenhack.h"
 #include "xpm-pixmap.h"
-#include <stdio.h>
+#include "textclient.h"
+#include "xft.h"
 
-#ifdef HAVE_COCOA
+#ifdef HAVE_JWXYZ
 # define HAVE_XPM
 #endif
 
-extern FILE *popen (const char *, const char *);
-extern int pclose (FILE *);
-
-#define font_height(font)              (font->ascent + font->descent)
+#define font_height(font) (font->ascent + font->descent)
 
 
 struct state {
@@ -33,15 +31,17 @@ struct state {
   Window window;
   int Width, Height;
   GC fg_gc, bg_gc, text_fg_gc, text_bg_gc;
-  char *words;
   int x, y;
-  XFontStruct *font;
+
+  XftFont  *xftfont;
+  XftColor  xftcolor;
+  XftDraw  *xftdraw;
 
   unsigned long interval;
   Pixmap left1, left2, right1, right2;
   Pixmap left_front, right_front, front, down;
 
-  char *program, *orig_program;
+  text_data *tc;
 
   int state;   /* indicates states: walking or getting passwd */
   int first_time;
@@ -60,10 +60,11 @@ struct state {
     int x, y, width, height;
   } s_rect;
 
-  char word_buf[BUFSIZ];
+  char words[10240];
+  int lines;
 };
 
-static char *get_words (struct state *);
+static void fill_words (struct state *);
 static void walk (struct state *, int dir);
 static void talk (struct state *, int erase);
 static void talk_1 (struct state *);
@@ -326,18 +327,12 @@ think (struct state *st)
     if (random() & 1)
        walk(st, FRONT);
     if (random() & 1)
-    {
-      st->words = get_words(st);
       return 1;
-    }
     return 0;
 }
 
-#define MAXLINES 25
-
-#undef BUFSIZ
-#define BUFSIZ ((MAXLINES + 1) * 100)
-
+#define MAXLINES 10
+#define LINELEN 256
 
 static void
 talk (struct state *st, int force_erase)
@@ -348,8 +343,7 @@ talk (struct state *st, int force_erase)
                     total = 0;
     register char  *p,
                    *p2;
-    char            buf[BUFSIZ],
-                    args[MAXLINES][256];
+    char            args[MAXLINES][LINELEN];
 
     /* clear what we've written */
     if (st->talking || force_erase)
@@ -371,18 +365,30 @@ talk (struct state *st, int force_erase)
        }
        return;
     }
+    p = st->words;
+    /* If there is actually no words, just return */
+    if (!*p)
+    {
+      st->talking = 0;
+      return;
+    }
     st->talking = 1;
     walk(st, FRONT);
-    p = strcpy(buf, st->words);
 
     for (p2 = p; *p2; p2++)
       if (*p2 == '\t') *p2 = ' ';
 
     if (!(p2 = strchr(p, '\n')) || !p2[1])
       {
+        XGlyphInfo extents;
+
        total = strlen (st->words);
-       strcpy (args[0], st->words);
-       width = XTextWidth(st->font, st->words, total);
+       strncpy (args[0], st->words, LINELEN);
+       args[0][LINELEN - 1] = 0;
+        XftTextExtentsUtf8 (st->dpy, st->xftfont, 
+                            (FcChar8 *) st->words, total,
+                            &extents);
+        width = extents.xOff;
        height = 0;
       }
     else
@@ -390,12 +396,20 @@ talk (struct state *st, int force_erase)
       for (height = 0; p; height++)
        {
          int             w;
+          XGlyphInfo extents;
          *p2 = 0;
-         if ((w = XTextWidth(st->font, p, p2 - p)) > width)
-           width = w;
+
+          XftTextExtentsUtf8 (st->dpy, st->xftfont, 
+                              (FcChar8 *) p, p2 - p,
+                              &extents);
+          w = extents.xOff;
+         if (w > width)
+            width = w;
+
          total += p2 - p;      /* total chars; count to determine reading
                                 * time */
-         (void) strcpy(args[height], p);
+         (void) strncpy(args[height], p, LINELEN);
+         args[height][LINELEN - 1] = 0;
          if (height == MAXLINES - 1)
            {
              /* puts("Message too long!"); */
@@ -412,7 +426,7 @@ talk (struct state *st, int force_erase)
      * new box by 15 pixels on the sides (30 total) top and bottom.
      */
     st->s_rect.width = width + 30;
-    st->s_rect.height = height * font_height(st->font) + 30;
+    st->s_rect.height = height * font_height(st->xftfont) + 30;
     if (st->x - st->s_rect.width - 10 < 5)
        st->s_rect.x = 5;
     else if ((st->s_rect.x = st->x + 32 - (st->s_rect.width + 15) / 2)
@@ -435,21 +449,26 @@ talk (struct state *st, int force_erase)
         st->s_rect.x + 7, st->s_rect.y + 7, st->s_rect.width - 15, st->s_rect.height - 15);
 
     st->X = 15;
-    st->Y = 15 + font_height(st->font);
+    st->Y = 15 + font_height(st->xftfont);
 
     /* now print each string in reverse order (start at bottom of box) */
     for (Z = 0; Z < height; Z++)
     {
         int L = strlen(args[Z]);
-        if (args[Z][L-1] == '\r' || args[Z][L-1] == '\n')
+        /* If there are continuous new lines, L can be 0 */
+        if (L && (args[Z][L-1] == '\r' || args[Z][L-1] == '\n'))
           args[Z][--L] = 0;
-       XDrawString(st->dpy, st->window, st->text_fg_gc, st->s_rect.x + st->X, st->s_rect.y + st->Y,
-                   args[Z], L);
-       st->Y += font_height(st->font);
+        XftDrawStringUtf8 (st->xftdraw, &st->xftcolor, st->xftfont,
+                           st->s_rect.x + st->X, st->s_rect.y + st->Y,
+                           (FcChar8 *) args[Z], L);
+
+       st->Y += font_height(st->xftfont);
     }
     st->interval = (total / 15) * 1000;
     if (st->interval < 2000) st->interval = 2000;
     st->next_fn = talk_1;
+    *st->words = 0;
+    st->lines = 0;
 }
 
 static void
@@ -485,74 +504,31 @@ look (struct state *st)
 
 
 static void
-init_words (struct state *st)
+fill_words (struct state *st)
 {
-  st->program = get_string_resource (st->dpy, "program", "Program");
+  char *p = st->words + strlen(st->words);
+  char *c;
+  int lines = 0;
+  int max = MAXLINES;
 
-  if (st->program)     /* get stderr on stdout, so it shows up on the window */
+  for (c = st->words; c < p; c++)
+    if (*c == '\n')
+      lines++;
+
+  while (p < st->words + sizeof(st->words) - 1 &&
+         lines < max)
     {
-      st->orig_program = st->program;
-      st->program = (char *) malloc (strlen (st->program) + 10);
-      strcpy (st->program, "( ");
-      strcat (st->program, st->orig_program);
-      strcat (st->program, " ) 2>&1");
+      int c = textclient_getc (st->tc);
+      if (c == '\n')
+        lines++;
+      if (c > 0)
+        *p++ = (char) c;
+      else
+        break;
     }
+  *p = 0;
 
-  st->words = get_words(st);   
-}
-
-static char *
-get_words (struct state *st)
-{
-    FILE           *pp;
-    register char  *p = st->word_buf;
-
-    st->word_buf[0] = '\0';
-
-       if ((pp = popen(st->program, "r")))
-       {
-           while (fgets(p, sizeof(st->word_buf) - strlen(st->word_buf), pp))
-           {
-               if (strlen(st->word_buf) + 1 < sizeof(st->word_buf))
-                   p = st->word_buf + strlen(st->word_buf);
-               else
-                   break;
-           }
-           (void) pclose(pp);
-           if (! st->word_buf[0])
-             sprintf (st->word_buf, "\"%s\" produced no output!", st->orig_program);
-           else if (!st->first_time &&
-                    (strstr (st->word_buf, ": not found") ||
-                     strstr (st->word_buf, ": Not found") ||
-                      strstr (st->word_buf, ": command not found") ||
-                      strstr (st->word_buf, ": Command not found")))
-             switch (random () % 20)
-               {
-               case 1: strcat (st->word_buf, "( Get with the st->program, bub. )\n");
-                 break;
-               case 2: strcat (st->word_buf,
-                 "( I blow my nose at you, you silly person! ) \n"); break;
-               case 3: strcat (st->word_buf,
-                 "\nThe resource you want to\nset is `noseguy.program'\n");
-                 break;
-               case 4:
-                 strcat(st->word_buf,"\nHelp!!  Help!!\nAAAAAAGGGGHHH!!  \n\n"); break;
-               case 5: strcpy (st->word_buf, "You have new mail.\n"); break;
-               case 6:
-                 strcat(st->word_buf,"( Hello?  Are you paying attention? )\n");break;
-               case 7:
-                 strcat (st->word_buf, "sh: what kind of fool do you take me for? \n");
-                 break;
-               }
-           st->first_time = 0;
-           p = st->word_buf;
-       }
-       else
-       {
-           perror(st->program);
-       }
-
-    return p;
+  st->lines = lines;
 }
 
 
@@ -562,9 +538,10 @@ static const char *noseguy_defaults [] = {
   ".foreground:            #CCCCCC",
   "*textForeground: black",
   "*textBackground: #CCCCCC",
-  "*fpsSolid:  true",
-  "*program:    xscreensaver-text --cols 40 | head -n15",
-  ".font:       -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
+  "*fpsSolid:   true",
+  "*program:    xscreensaver-text",
+  "*usePty:      False",
+  ".font:       -*-helvetica-medium-r-*-*-*-140-*-*-*-*-*-*",
   0
 };
 
@@ -596,16 +573,26 @@ noseguy_init (Display *d, Window w)
   st->Height = xgwa.height + 2;
   cmap = xgwa.colormap;
 
-  init_words(st);
+  st->tc = textclient_open (st->dpy);
+  {
+    int w = 40;
+    int h = 15;
+    textclient_reshape (st->tc, w, h, w, h,
+                        /* Passing MAXLINES isn't actually necessary */
+                        0);
+  }
+
   init_images(st);
 
-  if (!fontname || !*fontname)
-    fprintf (stderr, "%s: no font specified.\n", progname);
-  st->font = XLoadQueryFont(st->dpy, fontname);
-  if (!st->font) {
-    fprintf (stderr, "%s: could not load font %s.\n", progname, fontname);
-    exit(1);
-  }
+  st->xftfont = XftFontOpenXlfd (st->dpy, screen_number (xgwa.screen),
+                                 fontname);
+  XftColorAllocName (st->dpy, xgwa.visual, xgwa.colormap,
+                     get_string_resource (st->dpy,
+                                          "textForeground", "Foreground"),
+                     &st->xftcolor);
+  st->xftdraw = XftDrawCreate (st->dpy, st->window, xgwa.visual,
+                               xgwa.colormap);
+
 
   fg = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
   bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
@@ -617,26 +604,25 @@ noseguy_init (Display *d, Window w)
   if (! get_string_resource (st->dpy, "textBackground", "Background"))
     text_bg = fg;
 
-  gcvalues.font = st->font->fid;
   gcvalues.foreground = fg;
   gcvalues.background = bg;
   st->fg_gc = XCreateGC (st->dpy, st->window,
-                         GCForeground|GCBackground|GCFont,
+                         GCForeground|GCBackground,
                     &gcvalues);
   gcvalues.foreground = bg;
   gcvalues.background = fg;
   st->bg_gc = XCreateGC (st->dpy, st->window,
-                         GCForeground|GCBackground|GCFont,
+                         GCForeground|GCBackground,
                     &gcvalues);
   gcvalues.foreground = text_fg;
   gcvalues.background = text_bg;
   st->text_fg_gc = XCreateGC (st->dpy, st->window,
-                              GCForeground|GCBackground|GCFont,
+                              GCForeground|GCBackground,
                          &gcvalues);
   gcvalues.foreground = text_bg;
   gcvalues.background = text_fg;
   st->text_bg_gc = XCreateGC (st->dpy, st->window, 
-                              GCForeground|GCBackground|GCFont,
+                              GCForeground|GCBackground,
                          &gcvalues);
   st->x = st->Width / 2;
   st->y = st->Height / 2;
@@ -650,6 +636,7 @@ static unsigned long
 noseguy_draw (Display *dpy, Window window, void *closure)
 {
   struct state *st = (struct state *) closure;
+  fill_words(st);
   st->next_fn(st);
   return (st->interval * 1000);
 }
@@ -658,6 +645,9 @@ static void
 noseguy_reshape (Display *dpy, Window window, void *closure, 
                  unsigned int w, unsigned int h)
 {
+  struct state *st = (struct state *) closure;
+  st->Width = w + 2;
+  st->Height = h + 2;
 }
 
 static Bool
@@ -669,6 +659,9 @@ noseguy_event (Display *dpy, Window window, void *closure, XEvent *event)
 static void
 noseguy_free (Display *dpy, Window window, void *closure)
 {
+  struct state *st = (struct state *) closure;
+  textclient_close (st->tc);
+  free (st);
 }
 
 XSCREENSAVER_MODULE ("NoseGuy", noseguy)