643aff104e536d692dd534b377ee6c746dda6dbf
[xscreensaver] / hacks / flag.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * flag --- a waving flag
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)flag.c        4.02 97/04/01 xlockmore";
6 #endif
7
8 /* Copyright (c) 1996 Charles Vidal <vidalc@univ-mlv.fr>.
9  * PEtite demo X11 de charles vidal 15 05 96
10  * tourne sous Linux et SOLARIS
11  * thank's to Bas van Gaalen, Holland, PD, for his sources
12  * in pascal vous devez rajouter une ligne dans mode.c
13  *
14  * Permission to use, copy, modify, and distribute this software and its
15  * documentation for any purpose and without fee is hereby granted,
16  * provided that the above copyright notice appear in all copies and that
17  * both that copyright notice and this permission notice appear in
18  * supporting documentation.
19  *
20  * This file is provided AS IS with no warranties of any kind.  The author
21  * shall have no liability with respect to the infringement of copyrights,
22  * trade secrets or any patents by this file or any part thereof.  In no
23  * event will the author be liable for any lost revenue or profits or
24  * other special, indirect and consequential damages.
25  *
26  * Revision History: 
27  * 22-Jan-98: jwz: made the flag wigglier; added xpm support.
28  *            (I tried to do this by re-porting from xlockmore, but the
29  *            current xlockmore version is completely inscrutable.)
30  * 13-May-97: jwz@netscape.com: turned into a standalone program.
31  *                        Made it able to animate arbitrary (runtime) text or bitmaps.
32  * 01-May-96: written.
33  */
34
35 #ifdef STANDALONE
36 # define PROGCLASS                                      "Flag"
37 # define HACK_INIT                                      init_flag
38 # define HACK_DRAW                                      draw_flag
39 # define flag_opts                                      xlockmore_opts
40 # define DEFAULTS       "*delay:                50000   \n"                     \
41                                         "*cycles:               1000    \n"                     \
42                                         "*size:                 -7      \n"                     \
43                                         "*ncolors:              200     \n"
44 # define BRIGHT_COLORS
45 # define UNIFORM_COLORS
46 # define DEF_FONT                                       "-*-helvetica-bold-r-*-240-*"
47 # define DEF_BITMAP                                     ""
48 # define DEF_TEXT                                       ""
49 # include "xlockmore.h"                         /* from the xscreensaver distribution */
50
51 # ifdef HAVE_XPM
52 #  include <X11/xpm.h>
53 #  ifndef PIXEL_ALREADY_TYPEDEFED
54 #   define PIXEL_ALREADY_TYPEDEFED /* Sigh, Xmu/Drawing.h needs this... */
55 #  endif
56 # endif
57
58 #ifdef HAVE_XMU
59 # ifndef VMS
60 #  include <X11/Xmu/Drawing.h>
61 # else  /* VMS */
62 #  include <Xmu/Drawing.h>
63 # endif /* VMS */
64 #endif /* HAVE_XMU */
65
66 #include "images/bob.xbm"
67
68 #else  /* !STANDALONE */
69 # include "xlock.h"                                     /* from the xlockmore distribution */
70 # include "flag.h"
71 #endif /* !STANDALONE */
72
73
74 #if defined(VMS) && !defined(HAVE_UNAME) && (__VMS_VER >= 70000000)
75 # define HAVE_UNAME 1
76 #endif
77
78 #ifdef HAVE_UNAME
79 # include <sys/utsname.h>
80 #endif /* HAVE_UNAME */
81
82 #ifdef STANDALONE
83 static XrmOptionDescRec opts[] =
84 {
85   { "-bitmap", ".flag.bitmap", XrmoptionSepArg, 0 }
86 };
87
88 #endif /* STANDALONE */
89
90 ModeSpecOpt flag_opts = {
91 #ifdef STANDALONE
92   1, opts, 0, NULL, NULL
93 #else  /* !STANDALONE */
94   0, NULL, 0, NULL, NULL
95 #endif /* STANDALONE */
96 };
97
98 #include <string.h>
99 #include <X11/Xutil.h>
100
101 #define MINSIZE 1
102 #define MAXSCALE 8
103 #define MINSCALE 2
104 #define MAXINITSIZE 6
105 #define MININITSIZE 2
106 #define MINAMP 5
107 #define MAXAMP 20
108 #define MAXW(fp) (MAXSCALE * (fp)->image->width + 2 * MAXAMP + (fp)->pointsize)
109 #define MAXH(fp) (MAXSCALE * (fp)->image->height+ 2 * MAXAMP + (fp)->pointsize)
110 #define MINW(fp) (MINSCALE * (fp)->image->width + 2 * MINAMP + (fp)->pointsize)
111 #define MINH(fp) (MINSCALE * (fp)->image->height+ 2 * MINAMP + (fp)->pointsize)
112 #define ANGLES          360
113
114 typedef struct {
115         int         samp;
116         int         sofs;
117         int         sidx;
118         int         x_flag, y_flag;
119         int         timer;
120         int         initialized;
121         int         stab[ANGLES];
122         Pixmap      cache;
123         int         width, height;
124         int         pointsize;
125         float      size;
126         float      inctaille;
127         int         startcolor;
128     XImage     *image;
129 } flagstruct;
130
131 static flagstruct *flags = NULL;
132
133 static int
134 random_num(int n)
135 {
136         return ((int) (((float) LRAND() / MAXRAND) * (n + 1.0)));
137 }
138
139 static void
140 initSintab(ModeInfo * mi)
141 {
142         flagstruct *fp = &flags[MI_SCREEN(mi)];
143         int         i;
144
145   /*-
146    * change the periodicity of the sin formula : the maximum of the
147    * periocity seem to be 16 ( 2^4 ), after the drawing isn't good looking
148    */
149         int         periodicity = random_num(4);
150         int         puissance = 1;
151
152         /* for (i=0;i<periodicity;i++) puissance*=2; */
153         puissance <<= periodicity;
154         for (i = 0; i < ANGLES; i++)
155                 fp->stab[i] = (int) (SINF(i * puissance * M_PI / ANGLES) * fp->samp) +
156                         fp->sofs;
157 }
158
159 static void
160 affiche(ModeInfo * mi)
161 {
162         Display    *display = MI_DISPLAY(mi);
163         int         x, y, xp, yp;
164         flagstruct *fp = &flags[MI_SCREEN(mi)];
165
166         for (x = 0; x < fp->image->width; x++)
167                 for (y = fp->image->height-1; y >= 0; y--) {
168                         xp = (int) (fp->size * (float) x) +
169                                 fp->stab[(fp->sidx + x + y) % ANGLES];
170                         yp = (int) (fp->size * (float) y) +
171                                 fp->stab[(fp->sidx + 4 * x + y + y) % ANGLES];
172
173                         if (fp->image->depth > 1)
174                           XSetForeground(display, MI_GC(mi),
175                                                          XGetPixel(fp->image, x, y));
176                         else if (XGetPixel(fp->image, x, y))
177                                 XSetForeground(display, MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
178                         else if (MI_NPIXELS(mi) <= 2)
179                                 XSetForeground(display, MI_GC(mi), MI_WIN_WHITE_PIXEL(mi));
180                         else
181                                 XSetForeground(display, MI_GC(mi),
182                                                MI_PIXEL(mi, (y + x + fp->sidx + fp->startcolor) % MI_NPIXELS(mi)));
183
184                         if (fp->pointsize <= 1)
185                                 XDrawPoint(display, fp->cache, MI_GC(mi), xp, yp);
186                         else if (fp->pointsize < 6)
187                                 XFillRectangle(display, fp->cache, MI_GC(mi), xp, yp,
188                                                            fp->pointsize, fp->pointsize);
189                         else
190                                 XFillArc(display, fp->cache, MI_GC(mi), xp, yp,
191                                                  fp->pointsize, fp->pointsize, 0, 360*64);
192                 }
193 }
194
195 #ifdef STANDALONE
196
197 static void
198 make_flag_bits(ModeInfo *mi)
199 {
200   Display *dpy = MI_DISPLAY(mi);
201   flagstruct *fp = &flags[MI_SCREEN(mi)];
202   char *bitmap_name = get_string_resource ("bitmap", "Bitmap");
203   char *text = get_string_resource ("text", "Text");
204
205   /* If neither a bitmap nor text are specified, randomly select either
206          the builtin bitmap or builtin text. */
207   if ((!bitmap_name || !*bitmap_name) && (!text || !*text))
208         {
209           if (random() & 1)
210                 {
211                   free(bitmap_name);
212                   bitmap_name = strdup("(default)");
213                 }
214           else
215                 {
216                   free(text);
217                   text = strdup("(default)");
218                 }
219         }
220
221   if (bitmap_name &&
222           *bitmap_name &&
223           !!strcmp(bitmap_name, "(default)"))
224         {
225 #ifdef HAVE_XPM
226           Window window = MI_WINDOW(mi);
227           XWindowAttributes xgwa;
228           XpmAttributes xpmattrs;
229           int result;
230           Pixmap bitmap = 0;
231           int width = 0, height = 0;
232           xpmattrs.valuemask = 0;
233
234           XGetWindowAttributes (dpy, window, &xgwa);
235
236 # ifdef XpmCloseness
237           xpmattrs.valuemask |= XpmCloseness;
238           xpmattrs.closeness = 40000;
239 # endif
240 # ifdef XpmVisual
241           xpmattrs.valuemask |= XpmVisual;
242           xpmattrs.visual = xgwa.visual;
243 # endif
244 # ifdef XpmDepth
245           xpmattrs.valuemask |= XpmDepth;
246           xpmattrs.depth = xgwa.depth;
247 # endif
248 # ifdef XpmColormap
249           xpmattrs.valuemask |= XpmColormap;
250           xpmattrs.colormap = xgwa.colormap;
251 # endif
252
253           /* Uh, we don't need these now.  We use the colors from the xpm.
254                  It kinda sucks that we already allocated them. */
255           XFreeColors(dpy, xgwa.colormap, mi->pixels, mi->npixels, 0L);
256
257           result = XpmReadFileToPixmap (dpy, window, bitmap_name, &bitmap, 0,
258                                                                         &xpmattrs);
259           switch (result)
260                 {
261                 case XpmColorError:
262                   fprintf (stderr, "%s: warning: xpm color substitution performed\n",
263                                    progname);
264                   /* fall through */
265                 case XpmSuccess:
266                   width = xpmattrs.width;
267                   height = xpmattrs.height;
268                   break;
269                 case XpmFileInvalid:
270                 case XpmOpenFailed:
271                   bitmap = 0;
272                   break;
273                 case XpmColorFailed:
274                   fprintf (stderr, "%s: xpm: color allocation failed\n", progname);
275                   exit (-1);
276                 case XpmNoMemory:
277                   fprintf (stderr, "%s: xpm: out of memory\n", progname);
278                   exit (-1);
279                 default:
280                   fprintf (stderr, "%s: xpm: unknown error code %d\n", progname,
281                                    result);
282                   exit (-1);
283                 }
284
285           if (bitmap)
286                 {
287                   fp->image = XGetImage(dpy, bitmap, 0, 0, width, height, ~0L,
288                                                                 ZPixmap);
289                   XFreePixmap(dpy, bitmap);
290                 }
291           else
292 #endif /* HAVE_XPM */
293
294 #ifdef HAVE_XMU
295                 {
296                   int width, height, xh, yh;
297                   Pixmap bitmap =
298                         XmuLocateBitmapFile (DefaultScreenOfDisplay (dpy),
299                                                                  bitmap_name, 0, 0, &width, &height, &xh, &yh);
300                   if (!bitmap)
301                         {
302                           fprintf(stderr, "%s: unable to load bitmap file %s\n",
303                                           progname, bitmap_name);
304                           exit (1);
305                         }
306                   fp->image = XGetImage(dpy, bitmap, 0, 0, width, height,
307                                                                 1L, XYPixmap);
308                   XFreePixmap(dpy, bitmap);
309                 }
310
311 #else  /* !XMU */
312       fprintf (stderr,
313                            "%s: your vendor doesn't ship the standard Xmu library.\n",
314                            progname);
315       fprintf (stderr, "\tWe can't load XBM files without it.\n");
316       exit (1);
317 #endif /* !XMU */
318
319         }
320   else if (text && *text)
321         {
322           char *text2;
323           char *fn = get_string_resource ("font", "Font");
324           char *def_fn = "fixed";
325           char *line, *token;
326           int width, height;
327           int lines;
328           int margin = 2;
329           int fg = 1;
330           int bg = 0;
331           Pixmap bitmap;
332           XFontStruct *font;
333           XCharStruct overall;
334       XGCValues gcv;
335           GC gc;
336
337           if (!strcmp(text, "(default)"))
338                 {
339 # ifdef HAVE_UNAME
340                   struct utsname uts;
341                   if (uname (&uts) < 0)
342                         {
343                           text = strdup("uname() failed");
344                         }
345                   else
346                         {
347                           char *s;
348                           if ((s = strchr(uts.nodename, '.')))
349                                 *s = 0;
350                           text = (char *) malloc(strlen(uts.nodename) +
351                                                                          strlen(uts.sysname) +
352                                                                          strlen(uts.release) + 10);
353                           sprintf(text, "%s\n%s %s",
354                                           uts.nodename, uts.sysname, uts.release);
355                         }
356 #else   /* !HAVE_UNAME */
357 # ifdef VMS
358                   text = strdup(getenv("SYS$NODE"));
359 # else
360                   text = strdup("X\nScreen\nSaver");
361 # endif
362 #endif  /* !HAVE_UNAME */
363                 }
364
365           while (*text &&
366                          (text[strlen(text)-1] == '\r' ||
367                           text[strlen(text)-1] == '\n'))
368                 text[strlen(text)-1] = 0;
369
370           text2 = strdup(text);
371
372           if (!fn) fn = def_fn;
373       font = XLoadQueryFont (dpy, fn);
374       if (! font)
375                 {
376                   fprintf(stderr, "%s: unable to load font %s; using %s\n",
377                                   progname, fn, def_fn);
378                   font = XLoadQueryFont (dpy, def_fn);
379                 }
380
381           memset(&overall, 0, sizeof(overall));
382           token = text;
383           lines = 0;
384           while ((line = strtok(token, "\r\n")))
385                 {
386                   XCharStruct o2;
387                   int ascent, descent, direction;
388                   token = 0;
389                   XTextExtents(font, line, strlen(line),
390                                            &direction, &ascent, &descent, &o2);
391                   overall.lbearing = MAX(overall.lbearing, o2.lbearing);
392                   overall.rbearing = MAX(overall.rbearing, o2.rbearing);
393                   lines++;
394                 }
395
396           width = overall.lbearing + overall.rbearing + margin + margin + 1;
397           height = ((font->ascent + font->descent) * lines) + margin + margin;
398
399           bitmap = XCreatePixmap(dpy, MI_WINDOW(mi), width, height, 1);
400
401       gcv.font = font->fid;
402       gcv.foreground = bg;
403       gc = XCreateGC (dpy, bitmap, (GCFont | GCForeground), &gcv);
404           XFillRectangle(dpy, bitmap, gc, 0, 0, width, height);
405           XSetForeground(dpy, gc, fg);
406
407           token = text2;
408           lines = 0;
409           while ((line = strtok(token, "\r\n")))
410                 {
411                   XCharStruct o2;
412                   int ascent, descent, direction, xoff;
413                   token = 0;
414
415                   XTextExtents(font, line, strlen(line),
416                                            &direction, &ascent, &descent, &o2);
417                   xoff = ((overall.lbearing + overall.rbearing) -
418                                   (o2.lbearing + o2.rbearing)) / 2;
419
420                   XDrawString(dpy, bitmap, gc,
421                                           overall.lbearing + margin + xoff,
422                                           ((font->ascent * (lines + 1)) +
423                                            (font->descent * lines) +
424                                            margin),
425                                           line, strlen(line));
426                   lines++;
427                 }
428           free(text2);
429           XUnloadFont(dpy, font->fid);
430           XFree((XPointer) font);
431           XFreeGC(dpy, gc);
432
433           fp->image = XGetImage(dpy, bitmap, 0, 0, width, height, 1L, XYPixmap);
434           XFreePixmap(dpy, bitmap);
435         }
436   else
437         {
438           fp->image = XCreateImage (dpy, MI_VISUAL(mi), 1, XYBitmap, 0,
439                                                                 (char *) bob_bits, bob_width, bob_height,
440                                                                 8, 0);
441           fp->image->byte_order = LSBFirst;
442           fp->image->bitmap_bit_order = LSBFirst;
443         }
444
445   if (bitmap_name)
446         free (bitmap_name);
447   if (text)
448         free (text);
449 }
450
451 #else  /* !STANDALONE */
452
453 static void
454 make_flag_bits(ModeInfo *mi)
455 {
456   flagstruct *fp = &flags[MI_SCREEN(mi)];
457   int x, y;
458   int w = flag_width;
459   int h = flag_height;
460   int i = 0;
461   fp->image =
462         XCreateImage(MI_DISPLAY(mi), MI_VISUAL(mi),
463                                  1, XYBitmap, 0,                                        /* dpth, fmt, offset */
464                                  (char *) calloc ((w+8) / 8, h),        /* data */
465                                  w, h, 8, 0);                                           /* w, h, pad, bpl */
466   /* Geez, what kinda goofy bit order is this?? */
467   for (x = 0; x < w; x++)
468         for (y = h-1; y >= 0; y--)
469           XPutPixel (fp->image, x, y, flag_bits[i++]);
470 }
471
472 #endif /* !STANDALONE */
473
474
475 void
476 init_flag(ModeInfo * mi)
477 {
478         Display    *display = MI_DISPLAY(mi);
479         int         size = MI_SIZE(mi);
480         flagstruct *fp;
481
482         if (flags == NULL) {
483                 if ((flags = (flagstruct *) calloc(MI_NUM_SCREENS(mi),
484                                                sizeof (flagstruct))) == NULL)
485                         return;
486         }
487         fp = &flags[MI_SCREEN(mi)];
488
489         make_flag_bits(mi);
490
491         fp->width = MI_WIN_WIDTH(mi);
492         fp->height = MI_WIN_HEIGHT(mi);
493
494         fp->samp = MAXAMP;      /* Amplitude */
495         fp->sofs = 20;          /* ???????? */
496         fp->pointsize = size;
497         if (size < -MINSIZE)
498                 fp->pointsize = NRAND(-size - MINSIZE + 1) + MINSIZE;
499         if (fp->pointsize < MINSIZE ||
500         fp->width <= MAXW(fp) || fp->height <= MAXH(fp))
501                 fp->pointsize = MINSIZE;
502         fp->size = MAXINITSIZE; /* Initial distance between pts */
503         fp->inctaille = 0.05;
504         fp->timer = 0;
505         fp->sidx = fp->x_flag = fp->y_flag = 0;
506
507         if (!fp->initialized) {
508                 fp->initialized = True;
509                 if (!(fp->cache = XCreatePixmap(display, MI_WINDOW(mi),
510                 MAXW(fp), MAXH(fp), MI_WIN_DEPTH(mi))))
511 #ifdef STANDALONE
512                   exit(-1);
513 #else   /* !STANDALONE */
514                         error("%s: catastrophe memoire\n");
515 #endif /* !STANDALONE */
516         }
517         XSetForeground(display, MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
518         XFillRectangle(display, fp->cache, MI_GC(mi),
519                        0, 0, MAXW(fp), MAXH(fp));
520         /* don't want any exposure events from XCopyArea */
521         XSetGraphicsExposures(display, MI_GC(mi), False);
522         if (MI_NPIXELS(mi) > 2)
523                 fp->startcolor = NRAND(MI_NPIXELS(mi));
524         if (fp->width <= MAXW(fp) || fp->height <= MAXH(fp)) {
525                 fp->samp = MINAMP;
526                 fp->sofs = 0;
527                 fp->x_flag = random_num(fp->width - MINW(fp));
528                 fp->y_flag = random_num(fp->height - MINH(fp));
529         } else {
530                 fp->samp = MAXAMP;
531                 fp->sofs = 20;
532                 fp->x_flag = random_num(fp->width - MAXW(fp));
533                 fp->y_flag = random_num(fp->height - MAXH(fp));
534         }
535
536         initSintab(mi);
537
538         XClearWindow(display, MI_WINDOW(mi));
539 }
540
541 void
542 draw_flag(ModeInfo * mi)
543 {
544         Display    *display = MI_DISPLAY(mi);
545         Window      window = MI_WINDOW(mi);
546         flagstruct *fp = &flags[MI_SCREEN(mi)];
547
548         if (fp->width <= MAXW(fp) || fp->height <= MAXH(fp)) {
549                 fp->size = MININITSIZE;
550                 /* fp->pointsize = MINPOINTSIZE; */
551                 XCopyArea(display, fp->cache, window, MI_GC(mi),
552                           0, 0, MINW(fp), MINH(fp), fp->x_flag, fp->y_flag);
553         } else {
554                 if ((fp->size + fp->inctaille) > MAXSCALE)
555                         fp->inctaille = -fp->inctaille;
556                 if ((fp->size + fp->inctaille) < MINSCALE)
557                         fp->inctaille = -fp->inctaille;
558                 fp->size += fp->inctaille;
559                 XCopyArea(display, fp->cache, window, MI_GC(mi),
560                           0, 0, MAXW(fp), MAXH(fp), fp->x_flag, fp->y_flag);
561         }
562         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
563         XFillRectangle(display, fp->cache, MI_GC(mi),
564                        0, 0, MAXW(fp), MAXH(fp));
565         XFlush(display);
566         affiche(mi);
567         fp->sidx += 2;
568         fp->sidx %= (ANGLES * MI_NPIXELS(mi));
569         XFlush(display);
570         fp->timer++;
571         if ((MI_CYCLES(mi) > 0) && (fp->timer >= MI_CYCLES(mi)))
572                 init_flag(mi);
573 }
574
575 void
576 release_flag(ModeInfo * mi)
577 {
578         if (flags != NULL) {
579                 int         screen;
580
581                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
582                   {
583                         if (flags[screen].cache)
584                                 XFreePixmap(MI_DISPLAY(mi), flags[screen].cache);
585                         if (flags[screen].image)
586                           XDestroyImage(flags[screen].image);
587                   }
588                 (void) free((void *) flags);
589                 flags = NULL;
590         }
591 }
592
593 void
594 refresh_flag(ModeInfo * mi)
595 {
596         /* Do nothing, it will refresh by itself */
597 }