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