/* -*- Mode: C; tab-width: 4 -*- */ /* flag --- a waving flag image */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)flag.c 5.00 2000/11/01 xlockmore"; #endif /*- * Copyright (c) 1996 by Charles Vidal * http://www.chez.com/vidalc * * Thanks to Bas van Gaalen, Holland, PD, for his Pascal source * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * Revision History: * 01-Nov-2000: Allocation checks. * 22-Jan-1998: jwz: made the flag wigglier; added xpm support. * 24-Oct-1997: xpm and ras capability added. * 13-May-1997: jwz@jwz.org: turned into a standalone program. * Made it able to animate arbitrary (runtime) text or bitmaps * 15-May-1996: written. */ #ifdef STANDALONE #define MODE_flag #define PROGCLASS "Flag" #define HACK_INIT init_flag #define HACK_DRAW draw_flag #define flag_opts xlockmore_opts #define DEFAULTS "*delay: 50000 \n" \ "*cycles: 1000 \n" \ "*size: -7 \n" \ "*ncolors: 200 \n" \ "*bitmap: \n" \ "*font: \n" \ "*text: \n" \ "*fullrandom: True \n" #define BRIGHT_COLORS #define UNIFORM_COLORS #define DEF_FONT "-*-helvetica-bold-r-*-240-*" #define DEF_TEXT "" #include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* in xlockmore distribution */ #include "color.h" #endif /* STANDALONE */ #ifdef JWZ #ifdef HAVE_XPM #include #ifndef PIXEL_ALREADY_TYPEDEFED #define PIXEL_ALREADY_TYPEDEFED /* Sigh, Xmu/Drawing.h needs this... */ #endif #endif #ifdef HAVE_XMU #ifndef VMS #include #else /* VMS */ #include #endif /* VMS */ #endif /* HAVE_XMU */ #include "images/bob.xbm" #include #include #else #include "iostuff.h" #endif #ifdef MODE_flag #define DEF_INVERT "False" static Bool invert; static XrmOptionDescRec opts[] = { {(char *) "-invert", (char *) ".flag.invert", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+invert", (char *) ".flag.invert", XrmoptionNoArg, (caddr_t) "off"} }; static argtype vars[] = { {(void *) & invert, (char *) "invert", (char *) "Invert", (char *) DEF_INVERT, t_Bool} }; static OptionStruct desc[] = { {(char *) "-/+invert", (char *) "turn on/off inverting of flag"} }; ModeSpecOpt flag_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct flag_description = {"flag", "init_flag", "draw_flag", "release_flag", "refresh_flag", "init_flag", (char *) NULL, &flag_opts, 50000, 1, 1000, -7, 64, 1.0, "", "Shows a waving flag image", 0, NULL}; #endif /* aliases for vars defined in the bitmap file */ #define FLAG_WIDTH image_width #define FLAG_HEIGHT image_height #define FLAG_BITS image_bits #include "flag.xbm" #ifdef HAVE_XPM #define FLAG_NAME image_name #include "flag.xpm" #define DEFAULT_XPM 0 #endif #if !defined( VMS ) || ( __VMS_VER >= 70000000 ) #include #else #if USE_XVMSUTILS #if 0 #include "../xvmsutils/utsname.h" #else #include #endif #endif /* USE_XVMSUTILS */ #endif #define MINSIZE 1 #define MAXSCALE 8 #define MINSCALE 2 #define MAXINITSIZE 6 #define MININITSIZE 2 #define MINAMP 5 #define MAXAMP 20 #define MAXW(fp) (MAXSCALE * (fp)->image->width + 2 * MAXAMP + (fp)->pointsize + 2 * (fp)->sofs) #define MAXH(fp) (MAXSCALE * (fp)->image->height+ 2 * MAXAMP + (fp)->pointsize + 2 * (fp)->sofs) #define MINW(fp) (MINSCALE * (fp)->image->width + 2 * MINAMP + (fp)->pointsize) #define MINH(fp) (MINSCALE * (fp)->image->height+ 2 * MINAMP + (fp)->pointsize) #define ANGLES 360 #define IMAGE_FLAG 0 #define MESSAGE_FLAG 1 typedef struct { int samp; int sofs; int sidx; int x_flag, y_flag; int timer; int stab[ANGLES]; Pixmap cache; int width, height; int pointsize; float size; float inctaille; int startcolor; int choice; XImage *image; XImage *logo; Colormap cmap; unsigned long black; GC backGC; int graphics_format; } flagstruct; static XFontStruct *messagefont = None; static flagstruct *flags = (flagstruct *) NULL; extern XFontStruct *getFont(Display * display); static int random_num(int n) { return ((int) (((float) LRAND() / MAXRAND) * (n + 1.0))); } static void initSintab(ModeInfo * mi) { flagstruct *fp = &flags[MI_SCREEN(mi)]; int i; /*- * change the periodicity of the sin formula : the maximum of the * periocity seem to be 16 ( 2^4 ), after the drawing isn't good looking */ int periodicity = random_num(4); int puissance = 1; /* for (i=0;istab[i] = (int) (SINF(i * puissance * M_PI / ANGLES) * fp->samp) + fp->sofs; } static void affiche(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); int x, y, xp, yp, temp; flagstruct *fp = &flags[MI_SCREEN(mi)]; for (x = 0; x < fp->image->width; x++) for (y = fp->image->height - 1; y >= 0; y--) { temp = (fp->sidx - x + y) % ANGLES; if (temp < 0) temp = ANGLES - 1 + temp; xp = (int) (fp->size * (float) x) + fp->stab[temp]; temp = (fp->sidx - 4 * x + y + y) % ANGLES; if (temp < 0) temp = ANGLES - 1 + temp; yp = (int) (fp->size * (float) y) + fp->stab[temp]; if (MI_NPIXELS(mi) <= 2 || fp->graphics_format < IS_XPM || fp->choice == MESSAGE_FLAG) { if (((int) !invert) ^ XGetPixel(fp->image, x, y)) XSetForeground(display, fp->backGC, fp->black); else if (MI_NPIXELS(mi) <= 2) XSetForeground(display, fp->backGC, MI_WHITE_PIXEL(mi)); else { temp = (y - x + fp->sidx + fp->startcolor) % MI_NPIXELS(mi); if (temp < 0) temp = MI_NPIXELS(mi) - 1 + temp; XSetForeground(display, fp->backGC, MI_PIXEL(mi, temp)); } } else { /* * PURIFY sometimes reports thousands of Array Bounds Reads and Free Memory * Reads on the XGetPixel call on the next line. Appears to do this if image * loaded is small. */ XSetForeground(display, fp->backGC, XGetPixel(fp->image, x, y)); } if (fp->pointsize <= 1) XDrawPoint(display, fp->cache, fp->backGC, xp, yp); else #ifndef NOTHREED_EFFECT XFillRectangle(display, fp->cache, fp->backGC, xp, yp, fp->pointsize, fp->pointsize); #else if (fp->pointsize < 6) XFillRectangle(display, fp->cache, fp->backGC, xp, yp, fp->pointsize, fp->pointsize); else XFillArc(display, fp->cache, fp->backGC, xp, yp, fp->pointsize, fp->pointsize, 0, 360 * 64); #endif } } extern char *message; static Bool getText(ModeInfo * mi, XImage ** image) { Display *display = MI_DISPLAY(mi); char *text1, *text2; char *line, *token; int width, height; int lines; int margin = 2; XCharStruct overall; XGCValues gcv; GC gc; Pixmap text_pixmap; if (!message || !*message) { #if !defined( VMS ) || ( __VMS_VER >= 70000000 ) || defined( USE_XVMSUTILS ) struct utsname uts; if (uname(&uts) < 0) { if ((text1 = (char *) strdup("uname() failed")) == NULL) { return False; } } else { char *s; if ((s = strchr(uts.nodename, '.'))) *s = 0; if ((text1 = (char *) malloc(strlen(uts.nodename) + strlen(uts.sysname) + strlen(uts.release) + 10)) == NULL) return False; #ifdef AIXV3 (void) sprintf(text1, "%s\n%s %s", uts.nodename, uts.sysname, "3"); #else (void) sprintf(text1, "%s\n%s %s", uts.nodename, uts.sysname, uts.release); #endif } #else /* It says release 0 in my utsname.h */ if ((text1 = (char *) strdup("OpenVMS")) == NULL) return False; #endif } else { if ((text1 = (char *) strdup(message)) == NULL) return False; } while (*text1 && (text1[strlen(text1) - 1] == '\r' || text1[strlen(text1) - 1] == '\n')) text1[strlen(text1) - 1] = 0; if ((text2 = (char *) strdup(text1)) == NULL) { free(text1); return False; } (void) memset(&overall, 0, sizeof (overall)); token = text1; lines = 0; while ((line = strtok(token, "\r\n"))) { XCharStruct o2; int ascent, descent, direction; token = 0; (void) XTextExtents(messagefont, line, strlen(line), &direction, &ascent, &descent, &o2); overall.lbearing = MAX(overall.lbearing, o2.lbearing); overall.rbearing = MAX(overall.rbearing, o2.rbearing); lines++; } width = overall.lbearing + overall.rbearing + margin + margin + 1; height = ((messagefont->ascent + messagefont->descent) * lines) + margin + margin; if ((text_pixmap = XCreatePixmap(display, MI_WINDOW(mi), width, height, 1)) == None) { free(text1); free(text2); return False; } gcv.font = messagefont->fid; gcv.foreground = 0; gcv.background = 0; if ((gc = XCreateGC(display, text_pixmap, GCFont | GCForeground | GCBackground, &gcv)) == None) { XFreePixmap(display, text_pixmap); free(text1); free(text2); return False; } XFillRectangle(display, text_pixmap, gc, 0, 0, width, height); XSetForeground(display, gc, 1); token = text2; lines = 0; while ((line = strtok(token, "\r\n"))) { XCharStruct o2; int ascent, descent, direction, xoff; token = 0; (void) XTextExtents(messagefont, line, strlen(line), &direction, &ascent, &descent, &o2); xoff = ((overall.lbearing + overall.rbearing) - (o2.lbearing + o2.rbearing)) / 2; (void) XDrawString(display, text_pixmap, gc, overall.lbearing + margin + xoff, ((messagefont->ascent * (lines + 1)) + (messagefont->descent * lines) + margin), line, strlen(line)); lines++; } free(text1); free(text2); /*XUnloadFont(display, messagefont->fid); */ XFreeGC(display, gc); if ((*image = XGetImage(display, text_pixmap, 0, 0, width, height, 1L, XYPixmap)) == NULL) { XFreePixmap(display, text_pixmap); return False; } XFreePixmap(display, text_pixmap); return True; } static Bool init_stuff(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); flagstruct *fp = &flags[MI_SCREEN(mi)]; if (!fp->logo) { getImage(mi, &fp->logo, FLAG_WIDTH, FLAG_HEIGHT, FLAG_BITS, #ifdef HAVE_XPM DEFAULT_XPM, FLAG_NAME, #endif &fp->graphics_format, &fp->cmap, &fp->black); if (!fp->logo) return False; } #ifndef STANDALONE if (fp->cmap != None) { setColormap(display, window, fp->cmap, MI_IS_INWINDOW(mi)); if (fp->backGC == None) { XGCValues xgcv; xgcv.background = fp->black; if ((fp->backGC = XCreateGC(display, window, GCBackground, &xgcv)) == None) return False; } } else #endif /* STANDALONE */ { fp->black = MI_BLACK_PIXEL(mi); fp->backGC = MI_GC(mi); } return True; } static void free_stuff(Display * display, flagstruct * fp) { if (fp->cmap != None) { XFreeColormap(display, fp->cmap); if (fp->backGC != None) { XFreeGC(display, fp->backGC); fp->backGC = None; } fp->cmap = None; } else fp->backGC = None; } static void free_flag(Display * display, flagstruct * fp) { if (fp->cache != None) { XFreePixmap(display, fp->cache); fp->cache = None; } if (fp->image != None) { if (fp->choice == IMAGE_FLAG) XFree((caddr_t) fp->image); /* Do not destroy data */ else (void) XDestroyImage(fp->image); fp->image = None; } free_stuff(display, fp); if (fp->logo != None) { destroyImage(&fp->logo, &fp->graphics_format); fp->logo = None; } } void release_flag(ModeInfo * mi) { if (flags != NULL) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) free_flag(MI_DISPLAY(mi), &flags[screen]); free(flags); flags = (flagstruct *) NULL; } if (messagefont != None) { XFreeFont(MI_DISPLAY(mi), messagefont); messagefont = None; } } void init_flag(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); int size = MI_SIZE(mi); flagstruct *fp; if (flags == NULL) { if ((flags = (flagstruct *) calloc(MI_NUM_SCREENS(mi), sizeof (flagstruct))) == NULL) return; } fp = &flags[MI_SCREEN(mi)]; if (messagefont == None) { if ((messagefont = getFont(display)) == None) { release_flag(mi); return; } } if (!init_stuff(mi)) { free_flag(display, fp); return; } fp->width = MI_WIDTH(mi); fp->height = MI_HEIGHT(mi); if (fp->image != None) { if (fp->choice == IMAGE_FLAG) XFree((caddr_t) fp->image); /* Do not destroy data */ else (void) XDestroyImage(fp->image); fp->image = None; } if (MI_IS_FULLRANDOM(mi) || (MI_BITMAP(mi) && *(MI_BITMAP(mi)) && message && *message) || ((!(MI_BITMAP(mi)) || !*(MI_BITMAP(mi))) && (!message || !*message))) fp->choice = (int) (LRAND() & 1); else if (MI_BITMAP(mi) && *(MI_BITMAP(mi))) fp->choice = IMAGE_FLAG; else fp->choice = MESSAGE_FLAG; if (fp->choice == MESSAGE_FLAG) { if (!getText(mi, &fp->image)) { free_flag(display, fp); return; } } else { int depth = MI_DEPTH(mi); if (fp->graphics_format < IS_XPM) depth = 1; if (depth >= 24 && depth < 32) depth = 32; if ((fp->image = XCreateImage(display, MI_VISUAL(mi), depth, (fp->graphics_format < IS_XPM) ? XYBitmap : ZPixmap, 0, fp->logo->data, (fp->graphics_format == IS_RASTERFILE && (fp->logo->width & 1)) ? fp->logo->width + 1 : fp->logo->width, fp->logo->height, 8, 0)) == None) { free_flag(display, fp); return; } if (fp->graphics_format < IS_XPM) { fp->image->byte_order = LSBFirst; fp->image->bitmap_bit_order = LSBFirst; } } fp->samp = MAXAMP; /* Amplitude */ fp->sofs = 20; /* ???????? */ fp->pointsize = size; if (size < -MINSIZE) fp->pointsize = NRAND(-size - MINSIZE + 1) + MINSIZE; if (fp->pointsize < MINSIZE || fp->width <= MAXW(fp) || fp->height <= MAXH(fp)) fp->pointsize = MINSIZE; fp->size = MAXINITSIZE; /* Initial distance between pts */ fp->inctaille = 0.05; fp->timer = 0; fp->sidx = fp->x_flag = fp->y_flag = 0; if (fp->cache) { XFreePixmap(display, fp->cache); } if ((fp->cache = XCreatePixmap(display, MI_WINDOW(mi), MAXW(fp), MAXH(fp), MI_DEPTH(mi))) == None) { free_flag(display, fp); return; } XSetForeground(display, fp->backGC, fp->black); XSetBackground(display, fp->backGC, fp->black); XFillRectangle(display, fp->cache, fp->backGC, 0, 0, MAXW(fp), MAXH(fp)); /* don't want any exposure events from XCopyArea */ XSetGraphicsExposures(display, fp->backGC, False); if (MI_NPIXELS(mi) > 2) fp->startcolor = NRAND(MI_NPIXELS(mi)); if (fp->width <= MAXW(fp) || fp->height <= MAXH(fp)) { fp->samp = MINAMP; fp->sofs = 0; fp->x_flag = random_num(fp->width - MINW(fp)); fp->y_flag = random_num(fp->height - MINH(fp)); } else { fp->samp = MAXAMP; fp->sofs = 20; fp->x_flag = random_num(fp->width - MAXW(fp)); fp->y_flag = random_num(fp->height - MAXH(fp)); } initSintab(mi); MI_CLEARWINDOWCOLORMAP(mi, fp->backGC, fp->black); } void draw_flag(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); flagstruct *fp; if (flags == NULL) return; fp = &flags[MI_SCREEN(mi)]; if (fp->cache == None) return; MI_IS_DRAWN(mi) = True; if (fp->width <= MAXW(fp) || fp->height <= MAXH(fp)) { fp->size = MININITSIZE; /* fp->pointsize = MINPOINTSIZE; */ XCopyArea(display, fp->cache, window, fp->backGC, 0, 0, MINW(fp), MINH(fp), fp->x_flag, fp->y_flag); } else { if ((fp->size + fp->inctaille) > MAXSCALE) fp->inctaille = -fp->inctaille; if ((fp->size + fp->inctaille) < MINSCALE) fp->inctaille = -fp->inctaille; fp->size += fp->inctaille; XCopyArea(display, fp->cache, window, fp->backGC, 0, 0, MAXW(fp), MAXH(fp), fp->x_flag, fp->y_flag); } XSetForeground(MI_DISPLAY(mi), fp->backGC, fp->black); XFillRectangle(display, fp->cache, fp->backGC, 0, 0, MAXW(fp), MAXH(fp)); XFlush(display); affiche(mi); fp->sidx += 2; fp->sidx %= (ANGLES * MI_NPIXELS(mi)); XFlush(display); fp->timer++; if ((MI_CYCLES(mi) > 0) && (fp->timer >= MI_CYCLES(mi))) init_flag(mi); } void refresh_flag(ModeInfo * mi) { flagstruct *fp; if (flags == NULL) return; fp = &flags[MI_SCREEN(mi)]; #ifdef HAVE_XPM /* This is needed when another program changes the colormap. */ free_flag(MI_DISPLAY(mi), fp); init_flag(mi); #endif if (fp->backGC != None) MI_CLEARWINDOWCOLORMAP(mi, fp->backGC, fp->black); } #endif /* MODE_flag */