2ed9296e5aa50fed2872c54a5dd9b6b965b8e2c4
[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@jwz.org: 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 #ifdef HAVE_UNAME
75 # include <sys/utsname.h>
76 #endif /* HAVE_UNAME */
77
78 #ifdef STANDALONE
79 static XrmOptionDescRec opts[] =
80 {
81   { "-bitmap", ".flag.bitmap", XrmoptionSepArg, 0 },
82   { "-text",   ".flag.text",   XrmoptionSepArg, 0 }
83 };
84
85 #endif /* STANDALONE */
86
87 ModeSpecOpt flag_opts = {
88 #ifdef STANDALONE
89   2, opts, 0, NULL, NULL
90 #else  /* !STANDALONE */
91   0, NULL, 0, NULL, NULL
92 #endif /* STANDALONE */
93 };
94
95 #include <string.h>
96 #include <X11/Xutil.h>
97
98 #define MINSIZE 1
99 #define MAXSCALE 8
100 #define MINSCALE 2
101 #define MAXINITSIZE 6
102 #define MININITSIZE 2
103 #define MINAMP 5
104 #define MAXAMP 20
105 #define MAXW(fp) (MAXSCALE * (fp)->image->width + 2 * MAXAMP + (fp)->pointsize)
106 #define MAXH(fp) (MAXSCALE * (fp)->image->height+ 2 * MAXAMP + (fp)->pointsize)
107 #define MINW(fp) (MINSCALE * (fp)->image->width + 2 * MINAMP + (fp)->pointsize)
108 #define MINH(fp) (MINSCALE * (fp)->image->height+ 2 * MINAMP + (fp)->pointsize)
109 #define ANGLES          360
110
111 typedef struct {
112         int         samp;
113         int         sofs;
114         int         sidx;
115         int         x_flag, y_flag;
116         int         timer;
117         int         initialized;
118         int         stab[ANGLES];
119         Pixmap      cache;
120         int         width, height;
121         int         pointsize;
122         float      size;
123         float      inctaille;
124         int         startcolor;
125     XImage     *image;
126 } flagstruct;
127
128 static flagstruct *flags = NULL;
129
130 static int
131 random_num(int n)
132 {
133         return ((int) (((float) LRAND() / MAXRAND) * (n + 1.0)));
134 }
135
136 static void
137 initSintab(ModeInfo * mi)
138 {
139         flagstruct *fp = &flags[MI_SCREEN(mi)];
140         int         i;
141
142   /*-
143    * change the periodicity of the sin formula : the maximum of the
144    * periocity seem to be 16 ( 2^4 ), after the drawing isn't good looking
145    */
146         int         periodicity = random_num(4);
147         int         puissance = 1;
148
149         /* for (i=0;i<periodicity;i++) puissance*=2; */
150         puissance <<= periodicity;
151         for (i = 0; i < ANGLES; i++)
152                 fp->stab[i] = (int) (SINF(i * puissance * M_PI / ANGLES) * fp->samp) +
153                         fp->sofs;
154 }
155
156 static void
157 affiche(ModeInfo * mi)
158 {
159         Display    *display = MI_DISPLAY(mi);
160         int         x, y, xp, yp;
161         flagstruct *fp = &flags[MI_SCREEN(mi)];
162
163         for (x = 0; x < fp->image->width; x++)
164                 for (y = fp->image->height-1; y >= 0; y--) {
165                         xp = (int) (fp->size * (float) x) +
166                                 fp->stab[(fp->sidx + x + y) % ANGLES];
167                         yp = (int) (fp->size * (float) y) +
168                                 fp->stab[(fp->sidx + 4 * x + y + y) % ANGLES];
169
170                         if (fp->image->depth > 1)
171                           XSetForeground(display, MI_GC(mi),
172                                                          XGetPixel(fp->image, x, y));
173                         else if (XGetPixel(fp->image, x, y))
174                                 XSetForeground(display, MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
175                         else if (MI_NPIXELS(mi) <= 2)
176                                 XSetForeground(display, MI_GC(mi), MI_WIN_WHITE_PIXEL(mi));
177                         else
178                                 XSetForeground(display, MI_GC(mi),
179                                                MI_PIXEL(mi, (y + x + fp->sidx + fp->startcolor) % MI_NPIXELS(mi)));
180
181                         if (fp->pointsize <= 1)
182                                 XDrawPoint(display, fp->cache, MI_GC(mi), xp, yp);
183                         else if (fp->pointsize < 6)
184                                 XFillRectangle(display, fp->cache, MI_GC(mi), xp, yp,
185                                                            fp->pointsize, fp->pointsize);
186                         else
187                                 XFillArc(display, fp->cache, MI_GC(mi), xp, yp,
188                                                  fp->pointsize, fp->pointsize, 0, 360*64);
189                 }
190 }
191
192 #ifdef STANDALONE
193
194 static void
195 make_flag_bits(ModeInfo *mi)
196 {
197   Display *dpy = MI_DISPLAY(mi);
198   flagstruct *fp = &flags[MI_SCREEN(mi)];
199   char *bitmap_name = get_string_resource ("bitmap", "Bitmap");
200   char *text = get_string_resource ("text", "Text");
201
202   /* If neither a bitmap nor text are specified, randomly select either
203          the builtin bitmap or builtin text. */
204   if ((!bitmap_name || !*bitmap_name) && (!text || !*text))
205         {
206           if (random() & 1)
207                 {
208                   free(bitmap_name);
209                   bitmap_name = strdup("(default)");
210                 }
211           else
212                 {
213                   free(text);
214                   text = strdup("(default)");
215                 }
216         }
217
218   if (bitmap_name &&
219           *bitmap_name &&
220           !!strcmp(bitmap_name, "(default)"))
221         {
222 #ifdef HAVE_XPM
223           Window window = MI_WINDOW(mi);
224           XWindowAttributes xgwa;
225           XpmAttributes xpmattrs;
226           int result;
227           Pixmap bitmap = 0;
228           int width = 0, height = 0;
229           xpmattrs.valuemask = 0;
230
231           XGetWindowAttributes (dpy, window, &xgwa);
232
233 # ifdef XpmCloseness
234           xpmattrs.valuemask |= XpmCloseness;
235           xpmattrs.closeness = 40000;
236 # endif
237 # ifdef XpmVisual
238           xpmattrs.valuemask |= XpmVisual;
239           xpmattrs.visual = xgwa.visual;
240 # endif
241 # ifdef XpmDepth
242           xpmattrs.valuemask |= XpmDepth;
243           xpmattrs.depth = xgwa.depth;
244 # endif
245 # ifdef XpmColormap
246           xpmattrs.valuemask |= XpmColormap;
247           xpmattrs.colormap = xgwa.colormap;
248 # endif
249
250           /* Uh, we don't need these now.  We use the colors from the xpm.
251                  It kinda sucks that we already allocated them. */
252           XFreeColors(dpy, xgwa.colormap, mi->pixels, mi->npixels, 0L);
253
254           result = XpmReadFileToPixmap (dpy, window, bitmap_name, &bitmap, 0,
255                                                                         &xpmattrs);
256           switch (result)
257                 {
258                 case XpmColorError:
259                   fprintf (stderr, "%s: warning: xpm color substitution performed\n",
260                                    progname);
261                   /* fall through */
262                 case XpmSuccess:
263                   width = xpmattrs.width;
264                   height = xpmattrs.height;
265                   break;
266                 case XpmFileInvalid:
267                 case XpmOpenFailed:
268                   bitmap = 0;
269                   break;
270                 case XpmColorFailed:
271                   fprintf (stderr, "%s: xpm: color allocation failed\n", progname);
272                   exit (-1);
273                 case XpmNoMemory:
274                   fprintf (stderr, "%s: xpm: out of memory\n", progname);
275                   exit (-1);
276                 default:
277                   fprintf (stderr, "%s: xpm: unknown error code %d\n", progname,
278                                    result);
279                   exit (-1);
280                 }
281
282           if (bitmap)
283                 {
284                   fp->image = XGetImage(dpy, bitmap, 0, 0, width, height, ~0L,
285                                                                 ZPixmap);
286                   XFreePixmap(dpy, bitmap);
287                 }
288           else
289 #endif /* HAVE_XPM */
290
291 #ifdef HAVE_XMU
292                 {
293                   int width, height, xh, yh;
294                   Pixmap bitmap =
295                         XmuLocateBitmapFile (DefaultScreenOfDisplay (dpy),
296                                                                  bitmap_name, 0, 0, &width, &height, &xh, &yh);
297                   if (!bitmap)
298                         {
299                           fprintf(stderr, "%s: unable to load bitmap file %s\n",
300                                           progname, bitmap_name);
301                           exit (1);
302                         }
303                   fp->image = XGetImage(dpy, bitmap, 0, 0, width, height,
304                                                                 1L, XYPixmap);
305                   XFreePixmap(dpy, bitmap);
306                 }
307
308 #else  /* !XMU */
309       fprintf (stderr,
310                            "%s: your vendor doesn't ship the standard Xmu library.\n",
311                            progname);
312       fprintf (stderr, "\tWe can't load XBM files without it.\n");
313       exit (1);
314 #endif /* !XMU */
315
316         }
317   else if (text && *text)
318         {
319           char *text2;
320           char *fn = get_string_resource ("font", "Font");
321           char *def_fn = "fixed";
322           char *line, *token;
323           int width, height;
324           int lines;
325           int margin = 2;
326           int fg = 1;
327           int bg = 0;
328           Pixmap bitmap;
329           XFontStruct *font;
330           XCharStruct overall;
331       XGCValues gcv;
332           GC gc;
333
334           if (!strcmp(text, "(default)"))
335                 {
336 # ifdef HAVE_UNAME
337                   struct utsname uts;
338                   if (uname (&uts) < 0)
339                         {
340                           text = strdup("uname() failed");
341                         }
342                   else
343                         {
344                           char *s;
345                           if ((s = strchr(uts.nodename, '.')))
346                                 *s = 0;
347                           text = (char *) malloc(strlen(uts.nodename) +
348                                                                          strlen(uts.sysname) +
349                                                                          strlen(uts.version) +
350                                                                          strlen(uts.release) + 10);
351 # ifdef _AIX
352                           sprintf(text, "%s\n%s %s.%s",
353                                           uts.nodename, uts.sysname, uts.version, uts.release);
354 # else  /* !_AIX */
355                           sprintf(text, "%s\n%s %s",
356                                           uts.nodename, uts.sysname, uts.release);
357 # endif /* !_AIX */
358                         }
359 #else   /* !HAVE_UNAME */
360 # ifdef VMS
361                   text = strdup(getenv("SYS$NODE"));
362 # else
363                   text = strdup("X\nScreen\nSaver");
364 # endif
365 #endif  /* !HAVE_UNAME */
366                 }
367
368           while (*text &&
369                          (text[strlen(text)-1] == '\r' ||
370                           text[strlen(text)-1] == '\n'))
371                 text[strlen(text)-1] = 0;
372
373           text2 = strdup(text);
374
375           if (!fn) fn = def_fn;
376       font = XLoadQueryFont (dpy, fn);
377       if (! font)
378                 {
379                   fprintf(stderr, "%s: unable to load font %s; using %s\n",
380                                   progname, fn, def_fn);
381                   font = XLoadQueryFont (dpy, def_fn);
382                 }
383
384           memset(&overall, 0, sizeof(overall));
385           token = text;
386           lines = 0;
387           while ((line = strtok(token, "\r\n")))
388                 {
389                   XCharStruct o2;
390                   int ascent, descent, direction;
391                   token = 0;
392                   XTextExtents(font, line, strlen(line),
393                                            &direction, &ascent, &descent, &o2);
394                   overall.lbearing = MAX(overall.lbearing, o2.lbearing);
395                   overall.rbearing = MAX(overall.rbearing, o2.rbearing);
396                   lines++;
397                 }
398
399           width = overall.lbearing + overall.rbearing + margin + margin + 1;
400           height = ((font->ascent + font->descent) * lines) + margin + margin;
401
402           bitmap = XCreatePixmap(dpy, MI_WINDOW(mi), width, height, 1);
403
404       gcv.font = font->fid;
405       gcv.foreground = bg;
406       gc = XCreateGC (dpy, bitmap, (GCFont | GCForeground), &gcv);
407           XFillRectangle(dpy, bitmap, gc, 0, 0, width, height);
408           XSetForeground(dpy, gc, fg);
409
410           token = text2;
411           lines = 0;
412           while ((line = strtok(token, "\r\n")))
413                 {
414                   XCharStruct o2;
415                   int ascent, descent, direction, xoff;
416                   token = 0;
417
418                   XTextExtents(font, line, strlen(line),
419                                            &direction, &ascent, &descent, &o2);
420                   xoff = ((overall.lbearing + overall.rbearing) -
421                                   (o2.lbearing + o2.rbearing)) / 2;
422
423                   XDrawString(dpy, bitmap, gc,
424                                           overall.lbearing + margin + xoff,
425                                           ((font->ascent * (lines + 1)) +
426                                            (font->descent * lines) +
427                                            margin),
428                                           line, strlen(line));
429                   lines++;
430                 }
431           free(text2);
432           XUnloadFont(dpy, font->fid);
433           XFree((XPointer) font);
434           XFreeGC(dpy, gc);
435
436           fp->image = XGetImage(dpy, bitmap, 0, 0, width, height, 1L, XYPixmap);
437           XFreePixmap(dpy, bitmap);
438         }
439   else
440         {
441       char *bits = (char *) malloc (sizeof(bob_bits));
442       memcpy (bits, bob_bits, sizeof(bob_bits));
443           fp->image = XCreateImage (dpy, MI_VISUAL(mi), 1, XYBitmap, 0,
444                                                                 bits, bob_width, bob_height,
445                                                                 8, 0);
446           fp->image->byte_order = LSBFirst;
447           fp->image->bitmap_bit_order = LSBFirst;
448         }
449
450   if (bitmap_name)
451         free (bitmap_name);
452   if (text)
453         free (text);
454 }
455
456 #else  /* !STANDALONE */
457
458 static void
459 make_flag_bits(ModeInfo *mi)
460 {
461   flagstruct *fp = &flags[MI_SCREEN(mi)];
462   int x, y;
463   int w = flag_width;
464   int h = flag_height;
465   int i = 0;
466   fp->image =
467         XCreateImage(MI_DISPLAY(mi), MI_VISUAL(mi),
468                                  1, XYBitmap, 0,                                        /* dpth, fmt, offset */
469                                  (char *) calloc ((w+8) / 8, h),        /* data */
470                                  w, h, 8, 0);                                           /* w, h, pad, bpl */
471   /* Geez, what kinda goofy bit order is this?? */
472   for (x = 0; x < w; x++)
473         for (y = h-1; y >= 0; y--)
474           XPutPixel (fp->image, x, y, flag_bits[i++]);
475 }
476
477 #endif /* !STANDALONE */
478
479
480 void
481 init_flag(ModeInfo * mi)
482 {
483         Display    *display = MI_DISPLAY(mi);
484         int         size = MI_SIZE(mi);
485         flagstruct *fp;
486
487         if (flags == NULL) {
488                 if ((flags = (flagstruct *) calloc(MI_NUM_SCREENS(mi),
489                                                sizeof (flagstruct))) == NULL)
490                         return;
491         }
492         fp = &flags[MI_SCREEN(mi)];
493
494         make_flag_bits(mi);
495
496         fp->width = MI_WIN_WIDTH(mi);
497         fp->height = MI_WIN_HEIGHT(mi);
498
499         fp->samp = MAXAMP;      /* Amplitude */
500         fp->sofs = 20;          /* ???????? */
501         fp->pointsize = size;
502         if (size < -MINSIZE)
503                 fp->pointsize = NRAND(-size - MINSIZE + 1) + MINSIZE;
504         if (fp->pointsize < MINSIZE ||
505         fp->width <= MAXW(fp) || fp->height <= MAXH(fp))
506                 fp->pointsize = MINSIZE;
507         fp->size = MAXINITSIZE; /* Initial distance between pts */
508         fp->inctaille = 0.05;
509         fp->timer = 0;
510         fp->sidx = fp->x_flag = fp->y_flag = 0;
511
512         if (!fp->initialized) {
513                 fp->initialized = True;
514                 if (!(fp->cache = XCreatePixmap(display, MI_WINDOW(mi),
515                 MAXW(fp), MAXH(fp), MI_WIN_DEPTH(mi))))
516 #ifdef STANDALONE
517                   exit(-1);
518 #else   /* !STANDALONE */
519                         error("%s: catastrophe memoire\n");
520 #endif /* !STANDALONE */
521         }
522         XSetForeground(display, MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
523         XFillRectangle(display, fp->cache, MI_GC(mi),
524                        0, 0, MAXW(fp), MAXH(fp));
525         /* don't want any exposure events from XCopyArea */
526         XSetGraphicsExposures(display, MI_GC(mi), False);
527         if (MI_NPIXELS(mi) > 2)
528                 fp->startcolor = NRAND(MI_NPIXELS(mi));
529         if (fp->width <= MAXW(fp) || fp->height <= MAXH(fp)) {
530                 fp->samp = MINAMP;
531                 fp->sofs = 0;
532                 fp->x_flag = random_num(fp->width - MINW(fp));
533                 fp->y_flag = random_num(fp->height - MINH(fp));
534         } else {
535                 fp->samp = MAXAMP;
536                 fp->sofs = 20;
537                 fp->x_flag = random_num(fp->width - MAXW(fp));
538                 fp->y_flag = random_num(fp->height - MAXH(fp));
539         }
540
541         initSintab(mi);
542
543         XClearWindow(display, MI_WINDOW(mi));
544 }
545
546 void release_flag(ModeInfo * mi);
547
548
549 void
550 draw_flag(ModeInfo * mi)
551 {
552         Display    *display = MI_DISPLAY(mi);
553         Window      window = MI_WINDOW(mi);
554         flagstruct *fp = &flags[MI_SCREEN(mi)];
555
556         if (fp->width <= MAXW(fp) || fp->height <= MAXH(fp)) {
557                 fp->size = MININITSIZE;
558                 /* fp->pointsize = MINPOINTSIZE; */
559                 XCopyArea(display, fp->cache, window, MI_GC(mi),
560                           0, 0, MINW(fp), MINH(fp), fp->x_flag, fp->y_flag);
561         } else {
562                 if ((fp->size + fp->inctaille) > MAXSCALE)
563                         fp->inctaille = -fp->inctaille;
564                 if ((fp->size + fp->inctaille) < MINSCALE)
565                         fp->inctaille = -fp->inctaille;
566                 fp->size += fp->inctaille;
567                 XCopyArea(display, fp->cache, window, MI_GC(mi),
568                           0, 0, MAXW(fp), MAXH(fp), fp->x_flag, fp->y_flag);
569         }
570         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
571         XFillRectangle(display, fp->cache, MI_GC(mi),
572                        0, 0, MAXW(fp), MAXH(fp));
573         XFlush(display);
574         affiche(mi);
575         fp->sidx += 2;
576         fp->sidx %= (ANGLES * MI_NPIXELS(mi));
577         XFlush(display);
578         fp->timer++;
579         if ((MI_CYCLES(mi) > 0) && (fp->timer >= MI_CYCLES(mi)))
580       {
581         release_flag(mi);
582                 init_flag(mi);
583       }
584 }
585
586 void
587 release_flag(ModeInfo * mi)
588 {
589         if (flags != NULL) {
590                 int         screen;
591
592                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
593                   {
594                         if (flags[screen].cache)
595                                 XFreePixmap(MI_DISPLAY(mi), flags[screen].cache);
596                         if (flags[screen].image)
597                           XDestroyImage(flags[screen].image);
598                   }
599                 (void) free((void *) flags);
600                 flags = NULL;
601         }
602 }
603
604 void
605 refresh_flag(ModeInfo * mi)
606 {
607         /* Do nothing, it will refresh by itself */
608 }