From http://www.jwz.org/xscreensaver/xscreensaver-5.37.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_JWXYZ
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 # if defined(_AIX)
269                           sprintf(text, "%s\n%s %s.%s",
270                                           uts.nodename, uts.sysname, uts.version, uts.release);
271 #  elif defined(__APPLE__) && !defined(USE_IPHONE)  /* MacOS X + XDarwin */
272               {
273                 const char *file = 
274                   "/System/Library/CoreServices/SystemVersion.plist";
275                 FILE *f = fopen (file, "r");
276                 char *pbv = 0, *pn = 0, *puvv = 0;
277                 if (f) {
278                   char *s, buf[255];
279
280                   while (fgets (buf, sizeof(buf)-1, f)) {
281 #                   define GRAB(S,V)                                    \
282                     if (strstr(buf, S)) {                                       \
283                       fgets (buf, sizeof(buf)-1, f);                    \
284                       if ((s = strchr (buf, '>'))) V = strdup(s+1);     \
285                       if ((s = strchr (V, '<'))) *s = 0;                        \
286                     }
287                     GRAB ("ProductName", pn)
288                     GRAB ("ProductBuildVersion", pbv)
289                     GRAB ("ProductUserVisibleVersion", puvv)
290 #                   undef GRAB
291                   }
292                 }
293                 if (pbv)
294                   sprintf (text, "%s\n%s\n%s", 
295                            uts.nodename, pn, puvv /*, uts.machine*/);
296                 else
297                   sprintf(text, "%s\n%s %s",
298                           uts.nodename, uts.sysname, uts.release);
299               }
300 # else
301                           sprintf(text, "%s\n%s %s",
302                                           uts.nodename, uts.sysname, uts.release);
303 # endif /* special system types */
304                         }
305 #else   /* !HAVE_UNAME */
306 # ifdef VMS
307                   text = strdup(getenv("SYS$NODE"));
308 # else
309                   text = strdup("X\nScreen\nSaver");
310 # endif
311 #endif  /* !HAVE_UNAME */
312                 }
313
314           while (*text &&
315                          (text[strlen(text)-1] == '\r' ||
316                           text[strlen(text)-1] == '\n'))
317                 text[strlen(text)-1] = 0;
318
319           text2 = strdup(text);
320
321           if (!fn) fn = def_fn;
322       font = XLoadQueryFont (dpy, fn);
323       if (! font)
324                 {
325                   fprintf(stderr, "%s: unable to load font %s; using %s\n",
326                                   progname, fn, def_fn);
327                   font = XLoadQueryFont (dpy, def_fn);
328                 }
329
330           memset(&overall, 0, sizeof(overall));
331           token = text;
332           lines = 0;
333           while ((line = strtok(token, "\r\n")))
334                 {
335                   XCharStruct o2;
336                   int ascent, descent, direction;
337                   token = 0;
338                   XTextExtents(font, line, strlen(line),
339                                            &direction, &ascent, &descent, &o2);
340                   overall.lbearing = MAX(overall.lbearing, o2.lbearing);
341                   overall.rbearing = MAX(overall.rbearing, o2.rbearing);
342                   lines++;
343                 }
344
345           width = overall.lbearing + overall.rbearing + margin + margin + 1;
346           height = ((font->ascent + font->descent) * lines) + margin + margin;
347
348           bitmap = XCreatePixmap(dpy, MI_WINDOW(mi), width, height, 1);
349
350       gcv.font = font->fid;
351       gcv.foreground = bg;
352       gc = XCreateGC (dpy, bitmap, (GCFont | GCForeground), &gcv);
353           XFillRectangle(dpy, bitmap, gc, 0, 0, width, height);
354           XSetForeground(dpy, gc, fg);
355
356           token = text2;
357           lines = 0;
358           while ((line = strtok(token, "\r\n")))
359                 {
360                   XCharStruct o2;
361                   int ascent, descent, direction, xoff;
362                   token = 0;
363
364                   XTextExtents(font, line, strlen(line),
365                                            &direction, &ascent, &descent, &o2);
366                   xoff = ((overall.lbearing + overall.rbearing) -
367                                   (o2.lbearing + o2.rbearing)) / 2;
368
369                   XDrawString(dpy, bitmap, gc,
370                                           overall.lbearing + margin + xoff,
371                                           ((font->ascent * (lines + 1)) +
372                                            (font->descent * lines) +
373                                            margin),
374                                           line, strlen(line));
375                   lines++;
376                 }
377           free(text2);
378           XUnloadFont(dpy, font->fid);
379           XFree((XPointer) font);
380           XFreeGC(dpy, gc);
381
382           fp->image = XGetImage(dpy, bitmap, 0, 0, width, height, 1L, XYPixmap);
383           XFreePixmap(dpy, bitmap);
384         }
385
386
387   if (! fp->image)
388         {
389       char *bits = (char *) malloc (sizeof(bob_bits));
390       memcpy (bits, bob_bits, sizeof(bob_bits));
391           fp->image = XCreateImage (dpy, MI_VISUAL(mi), 1, XYBitmap, 0,
392                                                                 bits, bob_width, bob_height,
393                                                                 8, 0);
394           fp->image->byte_order = LSBFirst;
395           fp->image->bitmap_bit_order = LSBFirst;
396         }
397
398   if (bitmap_name)
399         free (bitmap_name);
400   if (text)
401         free (text);
402 }
403
404 #else  /* !STANDALONE */
405
406 static void
407 make_flag_bits(ModeInfo *mi)
408 {
409   flagstruct *fp = &flags[MI_SCREEN(mi)];
410   int x, y;
411   int w = flag_width;
412   int h = flag_height;
413   int i = 0;
414   fp->image =
415         XCreateImage(MI_DISPLAY(mi), MI_VISUAL(mi),
416                                  1, XYBitmap, 0,                                        /* dpth, fmt, offset */
417                                  (char *) calloc ((w+8) / 8, h),        /* data */
418                                  w, h, 8, 0);                                           /* w, h, pad, bpl */
419   /* Geez, what kinda goofy bit order is this?? */
420   for (x = 0; x < w; x++)
421         for (y = h-1; y >= 0; y--)
422           XPutPixel (fp->image, x, y, flag_bits[i++]);
423 }
424
425 #endif /* !STANDALONE */
426
427
428 ENTRYPOINT void
429 init_flag(ModeInfo * mi)
430 {
431         Display    *display = MI_DISPLAY(mi);
432         int         size = MI_SIZE(mi);
433         flagstruct *fp;
434
435         MI_INIT (mi, flags, 0);
436         fp = &flags[MI_SCREEN(mi)];
437
438         make_flag_bits(mi);
439     if (!fp->image) abort();
440
441         fp->width = MI_WIN_WIDTH(mi);
442         fp->height = MI_WIN_HEIGHT(mi);
443
444         fp->samp = MAXAMP;      /* Amplitude */
445         fp->sofs = 20;          /* ???????? */
446         fp->pointsize = size;
447         if (size < -MINSIZE)
448                 fp->pointsize = NRAND(-size - MINSIZE + 1) + MINSIZE;
449         if (fp->pointsize < MINSIZE ||
450         fp->width <= MAXW(fp) || fp->height <= MAXH(fp))
451                 fp->pointsize = MINSIZE;
452         fp->size = MAXINITSIZE; /* Initial distance between pts */
453         fp->inctaille = 0.05;
454         fp->timer = 0;
455         fp->sidx = fp->x_flag = fp->y_flag = 0;
456
457         if (!fp->initialized) {
458       fp->dbufp = True;
459 # ifdef HAVE_JWXYZ              /* Don't second-guess Quartz's double-buffering */
460       fp->dbufp = False;
461 #endif
462                 fp->initialized = True;
463                 if (!fp->dbufp)
464           fp->cache = MI_WINDOW(mi);  /* not double-buffering */
465         else
466           if (!(fp->cache = XCreatePixmap(display, MI_WINDOW(mi),
467                                           MAXW(fp), MAXH(fp),
468                                           MI_WIN_DEPTH(mi))))
469 #ifdef STANDALONE
470                   exit(-1);
471 #else   /* !STANDALONE */
472                         error("%s: catastrophe memoire\n");
473 #endif /* !STANDALONE */
474         }
475         XSetForeground(display, MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
476         XFillRectangle(display, fp->cache, MI_GC(mi),
477                        0, 0, MAXW(fp), MAXH(fp));
478         /* don't want any exposure events from XCopyArea */
479         XSetGraphicsExposures(display, MI_GC(mi), False);
480         if (MI_NPIXELS(mi) > 2)
481                 fp->startcolor = NRAND(MI_NPIXELS(mi));
482         if (fp->width <= MAXW(fp) || fp->height <= MAXH(fp)) {
483                 fp->samp = MINAMP;
484                 fp->sofs = 0;
485                 fp->x_flag = random_num(fp->width - MINW(fp));
486                 fp->y_flag = random_num(fp->height - MINH(fp));
487         } else {
488                 fp->samp = MAXAMP;
489                 fp->sofs = 20;
490                 fp->x_flag = random_num(fp->width - MAXW(fp));
491                 fp->y_flag = random_num(fp->height - MAXH(fp));
492         }
493
494         initSintab(mi);
495
496         XClearWindow(display, MI_WINDOW(mi));
497 }
498
499 ENTRYPOINT void release_flag(ModeInfo * mi);
500
501
502 ENTRYPOINT void
503 draw_flag(ModeInfo * mi)
504 {
505         Display    *display = MI_DISPLAY(mi);
506         Window      window = MI_WINDOW(mi);
507         flagstruct *fp = &flags[MI_SCREEN(mi)];
508
509     if (!fp->image) abort();
510     if (fp->cache == window) {  /* not double-buffering */
511       XClearWindow (display, window);
512     } else if (fp->width <= MAXW(fp) || fp->height <= MAXH(fp)) {
513                 fp->size = MININITSIZE;
514                 /* fp->pointsize = MINPOINTSIZE; */
515         XCopyArea(display, fp->cache, window, MI_GC(mi),
516                   0, 0, MINW(fp), MINH(fp), fp->x_flag, fp->y_flag);
517         } else {
518                 if ((fp->size + fp->inctaille) > MAXSCALE)
519                         fp->inctaille = -fp->inctaille;
520                 if ((fp->size + fp->inctaille) < MINSCALE)
521                         fp->inctaille = -fp->inctaille;
522                 fp->size += fp->inctaille;
523         XCopyArea(display, fp->cache, window, MI_GC(mi),
524                   0, 0, MAXW(fp), MAXH(fp), fp->x_flag, fp->y_flag);
525         }
526         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
527         XFillRectangle(display, fp->cache, MI_GC(mi),
528                        0, 0, MAXW(fp), MAXH(fp));
529         affiche(mi);
530         fp->sidx += 2;
531         fp->sidx %= (ANGLES * MI_NPIXELS(mi));
532         fp->timer++;
533         if ((MI_CYCLES(mi) > 0) && (fp->timer >= MI_CYCLES(mi)))
534       {
535         release_flag(mi);
536                 init_flag(mi);
537       }
538 }
539
540 ENTRYPOINT void
541 release_flag(ModeInfo * mi)
542 {
543         if (flags != NULL) {
544                 int         screen;
545
546                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
547                   {
548                         if (flags[screen].cache && flags[screen].dbufp)
549                                 XFreePixmap(MI_DISPLAY(mi), flags[screen].cache);
550                         if (flags[screen].image)
551                           XDestroyImage(flags[screen].image);
552                   }
553                 (void) free((void *) flags);
554                 flags = NULL;
555         }
556 }
557
558 ENTRYPOINT void
559 refresh_flag(ModeInfo * mi)
560 {
561         /* Do nothing, it will refresh by itself */
562 }
563
564 XSCREENSAVER_MODULE ("Flag", flag)