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