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