http://www.jwz.org/xscreensaver/xscreensaver-5.07.tar.gz
[xscreensaver] / hacks / flag.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * flag --- a waving flag
3  */
4 #if 0
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 HAVE_COCOA
36 # define DEF_FONT "Monaco 15"
37 #else
38 # define DEF_FONT "fixed"
39 #endif
40
41 #ifdef STANDALONE
42 # define DEFAULTS       "*delay:                50000   \n"             \
43                                         "*cycles:               1000    \n"             \
44                                         "*size:                 -7      \n"             \
45                                         "*ncolors:              200     \n"             \
46                                         "*bitmap:                               \n"             \
47                                         "*font:         " DEF_FONT      "\n"    \
48                                         "*text:                                 \n" \
49                                         "*fpsSolid:             true    \n" \
50
51 # define BRIGHT_COLORS
52 # define UNIFORM_COLORS
53 # define reshape_flag 0
54 # define flag_handle_event 0
55 # include "xlockmore.h"                         /* from the xscreensaver distribution */
56
57 #include "xpm-pixmap.h"
58 #include "images/bob.xbm"
59
60 #else  /* !STANDALONE */
61 # include "xlock.h"                                     /* from the xlockmore distribution */
62 # include "flag.h"
63 #endif /* !STANDALONE */
64
65
66 #ifdef HAVE_UNAME
67 # include <sys/utsname.h>
68 #endif /* HAVE_UNAME */
69
70 #ifdef STANDALONE
71 static XrmOptionDescRec opts[] =
72 {
73   { "-bitmap", ".flag.bitmap", XrmoptionSepArg, 0 },
74   { "-text",   ".flag.text",   XrmoptionSepArg, 0 }
75 };
76
77 #endif /* STANDALONE */
78
79 ENTRYPOINT ModeSpecOpt flag_opts = {
80 #ifdef STANDALONE
81   2, opts, 0, NULL, NULL
82 #else  /* !STANDALONE */
83   0, NULL, 0, NULL, NULL
84 #endif /* STANDALONE */
85 };
86
87 #define MINSIZE 1
88 #define MAXSCALE 8
89 #define MINSCALE 2
90 #define MAXINITSIZE 6
91 #define MININITSIZE 2
92 #define MINAMP 5
93 #define MAXAMP 20
94 #define MAXW(fp) (MAXSCALE * (fp)->image->width + 2 * MAXAMP + (fp)->pointsize)
95 #define MAXH(fp) (MAXSCALE * (fp)->image->height+ 2 * MAXAMP + (fp)->pointsize)
96 #define MINW(fp) (MINSCALE * (fp)->image->width + 2 * MINAMP + (fp)->pointsize)
97 #define MINH(fp) (MINSCALE * (fp)->image->height+ 2 * MINAMP + (fp)->pointsize)
98 #define ANGLES          360
99
100 typedef struct {
101         int         samp;
102         int         sofs;
103         int         sidx;
104         int         x_flag, y_flag;
105         int         timer;
106         int         initialized;
107         int         stab[ANGLES];
108     Bool                dbufp;
109         Pixmap      cache;
110         int         width, height;
111         int         pointsize;
112         float      size;
113         float      inctaille;
114         int         startcolor;
115     XImage     *image;
116 } flagstruct;
117
118 static flagstruct *flags = NULL;
119
120 static int
121 random_num(int n)
122 {
123         return ((int) (((float) LRAND() / MAXRAND) * (n + 1.0)));
124 }
125
126 static void
127 initSintab(ModeInfo * mi)
128 {
129         flagstruct *fp = &flags[MI_SCREEN(mi)];
130         int         i;
131
132   /*-
133    * change the periodicity of the sin formula : the maximum of the
134    * periocity seem to be 16 ( 2^4 ), after the drawing isn't good looking
135    */
136         int         periodicity = random_num(4);
137         int         puissance = 1;
138
139         /* for (i=0;i<periodicity;i++) puissance*=2; */
140         puissance <<= periodicity;
141         for (i = 0; i < ANGLES; i++)
142                 fp->stab[i] = (int) (SINF(i * puissance * M_PI / ANGLES) * fp->samp) +
143                         fp->sofs;
144 }
145
146 static void
147 affiche(ModeInfo * mi)
148 {
149         Display    *display = MI_DISPLAY(mi);
150         int         x, y, xp, yp;
151         flagstruct *fp = &flags[MI_SCREEN(mi)];
152
153         for (x = 0; x < fp->image->width; x++)
154                 for (y = fp->image->height-1; y >= 0; y--) {
155                         xp = (int) (fp->size * (float) x) +
156                                 fp->stab[(fp->sidx + x + y) % ANGLES];
157                         yp = (int) (fp->size * (float) y) +
158                                 fp->stab[(fp->sidx + 4 * x + y + y) % ANGLES];
159
160                         if (fp->image->depth > 1)
161                           XSetForeground(display, MI_GC(mi),
162                                                          XGetPixel(fp->image, x, y));
163                         else if (XGetPixel(fp->image, x, y))
164                                 XSetForeground(display, MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
165                         else if (MI_NPIXELS(mi) <= 2)
166                                 XSetForeground(display, MI_GC(mi), MI_WIN_WHITE_PIXEL(mi));
167                         else
168                                 XSetForeground(display, MI_GC(mi),
169                                                MI_PIXEL(mi, (y + x + fp->sidx + fp->startcolor) % MI_NPIXELS(mi)));
170
171             if (fp->cache == MI_WINDOW(mi)) {  /* not double-buffering */
172               xp += fp->x_flag;
173               yp += fp->y_flag;
174             }
175
176                         if (fp->pointsize <= 1)
177                                 XDrawPoint(display, fp->cache, MI_GC(mi), xp, yp);
178                         else if (fp->pointsize < 6)
179                                 XFillRectangle(display, fp->cache, MI_GC(mi), xp, yp,
180                                                            fp->pointsize, fp->pointsize);
181                         else
182                                 XFillArc(display, fp->cache, MI_GC(mi), xp, yp,
183                                                  fp->pointsize, fp->pointsize, 0, 360*64);
184                 }
185 }
186
187 #ifdef STANDALONE
188
189 static void
190 make_flag_bits(ModeInfo *mi)
191 {
192   Display *dpy = MI_DISPLAY(mi);
193   flagstruct *fp = &flags[MI_SCREEN(mi)];
194   char *bitmap_name = get_string_resource (dpy, "bitmap", "Bitmap");
195   char *text = get_string_resource (dpy, "text", "Text");
196
197 #ifdef HAVE_COCOA
198   bitmap_name = 0;  /* #### always use default */
199 #endif
200
201   /* If neither a bitmap nor text are specified, randomly select either
202          the builtin bitmap or builtin text. */
203   if ((!bitmap_name || !*bitmap_name) && (!text || !*text))
204         {
205           if (random() & 1)
206                 {
207                   free(bitmap_name);
208                   bitmap_name = strdup("(default)");
209                 }
210           else
211                 {
212                   free(text);
213                   text = strdup("(default)");
214                 }
215         }
216
217   if (bitmap_name &&
218           *bitmap_name &&
219           !!strcmp(bitmap_name, "(default)"))
220         {
221           Pixmap bitmap = 0;
222       int width = 0;
223       int height = 0;
224
225       bitmap = xpm_file_to_pixmap (dpy, MI_WINDOW (mi), bitmap_name,
226                                    &width, &height, 0);
227           if (bitmap)
228                 {
229                   fp->image = XGetImage(dpy, bitmap, 0, 0, width, height, ~0L,
230                                                                 ZPixmap);
231                   XFreePixmap(dpy, bitmap);
232                 }
233         }
234   else if (text && *text)
235         {
236           char *text2;
237           char *fn = get_string_resource (dpy, "font", "Font");
238           char *def_fn = "fixed";
239           char *line, *token;
240           int width, height;
241           int lines;
242           int margin = 2;
243           int fg = 1;
244           int bg = 0;
245           Pixmap bitmap;
246           XFontStruct *font;
247           XCharStruct overall;
248       XGCValues gcv;
249           GC gc;
250
251           if (!strcmp(text, "(default)"))
252                 {
253 # ifdef HAVE_UNAME
254                   struct utsname uts;
255                   if (uname (&uts) < 0)
256                         {
257                           text = strdup("uname() failed");
258                         }
259                   else
260                         {
261                           char *s;
262                           if ((s = strchr(uts.nodename, '.')))
263                                 *s = 0;
264                           text = (char *) malloc(strlen(uts.nodename) +
265                                                                          strlen(uts.sysname) +
266                                                                          strlen(uts.version) +
267                                                                          strlen(uts.release) + 10);
268 # ifdef _AIX
269                           sprintf(text, "%s\n%s %s.%s",
270                                           uts.nodename, uts.sysname, uts.version, uts.release);
271 # else  /* !_AIX */
272                           sprintf(text, "%s\n%s %s",
273                                           uts.nodename, uts.sysname, uts.release);
274 # endif /* !_AIX */
275                         }
276 #else   /* !HAVE_UNAME */
277 # ifdef VMS
278                   text = strdup(getenv("SYS$NODE"));
279 # else
280                   text = strdup("X\nScreen\nSaver");
281 # endif
282 #endif  /* !HAVE_UNAME */
283                 }
284
285           while (*text &&
286                          (text[strlen(text)-1] == '\r' ||
287                           text[strlen(text)-1] == '\n'))
288                 text[strlen(text)-1] = 0;
289
290           text2 = strdup(text);
291
292           if (!fn) fn = def_fn;
293       font = XLoadQueryFont (dpy, fn);
294       if (! font)
295                 {
296                   fprintf(stderr, "%s: unable to load font %s; using %s\n",
297                                   progname, fn, def_fn);
298                   font = XLoadQueryFont (dpy, def_fn);
299                 }
300
301           memset(&overall, 0, sizeof(overall));
302           token = text;
303           lines = 0;
304           while ((line = strtok(token, "\r\n")))
305                 {
306                   XCharStruct o2;
307                   int ascent, descent, direction;
308                   token = 0;
309                   XTextExtents(font, line, strlen(line),
310                                            &direction, &ascent, &descent, &o2);
311                   overall.lbearing = MAX(overall.lbearing, o2.lbearing);
312                   overall.rbearing = MAX(overall.rbearing, o2.rbearing);
313                   lines++;
314                 }
315
316           width = overall.lbearing + overall.rbearing + margin + margin + 1;
317           height = ((font->ascent + font->descent) * lines) + margin + margin;
318
319           bitmap = XCreatePixmap(dpy, MI_WINDOW(mi), width, height, 1);
320
321       gcv.font = font->fid;
322       gcv.foreground = bg;
323       gc = XCreateGC (dpy, bitmap, (GCFont | GCForeground), &gcv);
324           XFillRectangle(dpy, bitmap, gc, 0, 0, width, height);
325           XSetForeground(dpy, gc, fg);
326
327           token = text2;
328           lines = 0;
329           while ((line = strtok(token, "\r\n")))
330                 {
331                   XCharStruct o2;
332                   int ascent, descent, direction, xoff;
333                   token = 0;
334
335                   XTextExtents(font, line, strlen(line),
336                                            &direction, &ascent, &descent, &o2);
337                   xoff = ((overall.lbearing + overall.rbearing) -
338                                   (o2.lbearing + o2.rbearing)) / 2;
339
340                   XDrawString(dpy, bitmap, gc,
341                                           overall.lbearing + margin + xoff,
342                                           ((font->ascent * (lines + 1)) +
343                                            (font->descent * lines) +
344                                            margin),
345                                           line, strlen(line));
346                   lines++;
347                 }
348           free(text2);
349           XUnloadFont(dpy, font->fid);
350           XFree((XPointer) font);
351           XFreeGC(dpy, gc);
352
353           fp->image = XGetImage(dpy, bitmap, 0, 0, width, height, 1L, XYPixmap);
354           XFreePixmap(dpy, bitmap);
355         }
356
357
358   if (! fp->image)
359         {
360       char *bits = (char *) malloc (sizeof(bob_bits));
361       memcpy (bits, bob_bits, sizeof(bob_bits));
362           fp->image = XCreateImage (dpy, MI_VISUAL(mi), 1, XYBitmap, 0,
363                                                                 bits, bob_width, bob_height,
364                                                                 8, 0);
365           fp->image->byte_order = LSBFirst;
366           fp->image->bitmap_bit_order = LSBFirst;
367         }
368
369   if (bitmap_name)
370         free (bitmap_name);
371   if (text)
372         free (text);
373 }
374
375 #else  /* !STANDALONE */
376
377 static void
378 make_flag_bits(ModeInfo *mi)
379 {
380   flagstruct *fp = &flags[MI_SCREEN(mi)];
381   int x, y;
382   int w = flag_width;
383   int h = flag_height;
384   int i = 0;
385   fp->image =
386         XCreateImage(MI_DISPLAY(mi), MI_VISUAL(mi),
387                                  1, XYBitmap, 0,                                        /* dpth, fmt, offset */
388                                  (char *) calloc ((w+8) / 8, h),        /* data */
389                                  w, h, 8, 0);                                           /* w, h, pad, bpl */
390   /* Geez, what kinda goofy bit order is this?? */
391   for (x = 0; x < w; x++)
392         for (y = h-1; y >= 0; y--)
393           XPutPixel (fp->image, x, y, flag_bits[i++]);
394 }
395
396 #endif /* !STANDALONE */
397
398
399 ENTRYPOINT void
400 init_flag(ModeInfo * mi)
401 {
402         Display    *display = MI_DISPLAY(mi);
403         int         size = MI_SIZE(mi);
404         flagstruct *fp;
405
406         if (flags == NULL) {
407                 if ((flags = (flagstruct *) calloc(MI_NUM_SCREENS(mi),
408                                                sizeof (flagstruct))) == NULL)
409                         return;
410         }
411         fp = &flags[MI_SCREEN(mi)];
412
413         make_flag_bits(mi);
414     if (!fp->image) abort();
415
416         fp->width = MI_WIN_WIDTH(mi);
417         fp->height = MI_WIN_HEIGHT(mi);
418
419         fp->samp = MAXAMP;      /* Amplitude */
420         fp->sofs = 20;          /* ???????? */
421         fp->pointsize = size;
422         if (size < -MINSIZE)
423                 fp->pointsize = NRAND(-size - MINSIZE + 1) + MINSIZE;
424         if (fp->pointsize < MINSIZE ||
425         fp->width <= MAXW(fp) || fp->height <= MAXH(fp))
426                 fp->pointsize = MINSIZE;
427         fp->size = MAXINITSIZE; /* Initial distance between pts */
428         fp->inctaille = 0.05;
429         fp->timer = 0;
430         fp->sidx = fp->x_flag = fp->y_flag = 0;
431
432         if (!fp->initialized) {
433       fp->dbufp = True;
434 # ifdef HAVE_COCOA              /* Don't second-guess Quartz's double-buffering */
435       fp->dbufp = False;
436 #endif
437                 fp->initialized = True;
438                 if (!fp->dbufp)
439           fp->cache = MI_WINDOW(mi);  /* not double-buffering */
440         else
441           if (!(fp->cache = XCreatePixmap(display, MI_WINDOW(mi),
442                                           MAXW(fp), MAXH(fp),
443                                           MI_WIN_DEPTH(mi))))
444 #ifdef STANDALONE
445                   exit(-1);
446 #else   /* !STANDALONE */
447                         error("%s: catastrophe memoire\n");
448 #endif /* !STANDALONE */
449         }
450         XSetForeground(display, MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
451         XFillRectangle(display, fp->cache, MI_GC(mi),
452                        0, 0, MAXW(fp), MAXH(fp));
453         /* don't want any exposure events from XCopyArea */
454         XSetGraphicsExposures(display, MI_GC(mi), False);
455         if (MI_NPIXELS(mi) > 2)
456                 fp->startcolor = NRAND(MI_NPIXELS(mi));
457         if (fp->width <= MAXW(fp) || fp->height <= MAXH(fp)) {
458                 fp->samp = MINAMP;
459                 fp->sofs = 0;
460                 fp->x_flag = random_num(fp->width - MINW(fp));
461                 fp->y_flag = random_num(fp->height - MINH(fp));
462         } else {
463                 fp->samp = MAXAMP;
464                 fp->sofs = 20;
465                 fp->x_flag = random_num(fp->width - MAXW(fp));
466                 fp->y_flag = random_num(fp->height - MAXH(fp));
467         }
468
469         initSintab(mi);
470
471         XClearWindow(display, MI_WINDOW(mi));
472 }
473
474 ENTRYPOINT void release_flag(ModeInfo * mi);
475
476
477 ENTRYPOINT void
478 draw_flag(ModeInfo * mi)
479 {
480         Display    *display = MI_DISPLAY(mi);
481         Window      window = MI_WINDOW(mi);
482         flagstruct *fp = &flags[MI_SCREEN(mi)];
483
484     if (!fp->image) abort();
485     if (fp->cache == window) {  /* not double-buffering */
486       XClearWindow (display, window);
487     } else if (fp->width <= MAXW(fp) || fp->height <= MAXH(fp)) {
488                 fp->size = MININITSIZE;
489                 /* fp->pointsize = MINPOINTSIZE; */
490         XCopyArea(display, fp->cache, window, MI_GC(mi),
491                   0, 0, MINW(fp), MINH(fp), fp->x_flag, fp->y_flag);
492         } else {
493                 if ((fp->size + fp->inctaille) > MAXSCALE)
494                         fp->inctaille = -fp->inctaille;
495                 if ((fp->size + fp->inctaille) < MINSCALE)
496                         fp->inctaille = -fp->inctaille;
497                 fp->size += fp->inctaille;
498         XCopyArea(display, fp->cache, window, MI_GC(mi),
499                   0, 0, MAXW(fp), MAXH(fp), fp->x_flag, fp->y_flag);
500         }
501         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
502         XFillRectangle(display, fp->cache, MI_GC(mi),
503                        0, 0, MAXW(fp), MAXH(fp));
504         affiche(mi);
505         fp->sidx += 2;
506         fp->sidx %= (ANGLES * MI_NPIXELS(mi));
507         fp->timer++;
508         if ((MI_CYCLES(mi) > 0) && (fp->timer >= MI_CYCLES(mi)))
509       {
510         release_flag(mi);
511                 init_flag(mi);
512       }
513 }
514
515 ENTRYPOINT void
516 release_flag(ModeInfo * mi)
517 {
518         if (flags != NULL) {
519                 int         screen;
520
521                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
522                   {
523                         if (flags[screen].cache && flags[screen].dbufp)
524                                 XFreePixmap(MI_DISPLAY(mi), flags[screen].cache);
525                         if (flags[screen].image)
526                           XDestroyImage(flags[screen].image);
527                   }
528                 (void) free((void *) flags);
529                 flags = NULL;
530         }
531 }
532
533 ENTRYPOINT void
534 refresh_flag(ModeInfo * mi)
535 {
536         /* Do nothing, it will refresh by itself */
537 }
538
539 XSCREENSAVER_MODULE ("Flag", flag)