/* * Copyright (c) 1988-91 by Patrick J. Naughton. * * 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. */ /* * Copyright (c) 1991, 2015, Oracle and/or its affiliates. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /*- * pyro.c - Fireworks for xlock, the X Window System lockscreen. * * Copyright (c) 1991 by Patrick J. Naughton. * * See xlock.c for copying information. * * Revision History: * 16-Mar-91: Written. (received from David Brooks, brooks@osf.org). */ /* The physics of the rockets is a little bogus, but it looks OK. Each is * given an initial velocity impetus. They decelerate slightly (gravity * overcomes the rocket's impulse) and explode as the rocket's main fuse * gives out (we could add a ballistic stage, maybe). The individual * stars fan out from the rocket, and they decelerate less quickly. * That's called bouyancy, but really it's again a visual preference. */ #include "xlock.h" #include #define TWOPI 6.2831853 /* Define this >1 to get small rectangles instead of points */ #ifndef STARSIZE #define STARSIZE 1 #endif #define SILENT 0 #define REDGLARE 1 #define BURSTINGINAIR 2 #define CLOUD 0 #define DOUBLECLOUD 1 /* Clearly other types and other fascinating visual effects could be added...*/ /* P_xxx parameters represent the reciprocal of the probability... */ #define P_IGNITE 5000 /* ...of ignition per cycle */ #define P_DOUBLECLOUD 10 /* ...of an ignition being double */ #define P_MULTI 75 /* ...of an ignition being several @ once */ #define P_FUSILLADE 250 /* ...of an ignition starting a fusillade */ #define ROCKETW 2 /* Dimensions of rocket */ #define ROCKETH 4 #define XVELFACTOR 0.0025 /* Max horizontal velocity / screen width */ #define MINYVELFACTOR 0.016 /* Min vertical velocity / screen height */ #define MAXYVELFACTOR 0.018 #define GRAVFACTOR 0.0002 /* delta v / screen height */ #define MINFUSE 50 /* range of fuse lengths for rocket */ #define MAXFUSE 100 #define FUSILFACTOR 10 /* Generate fusillade by reducing P_IGNITE */ #define FUSILLEN 100 /* Length of fusillade, in ignitions */ #define SVELFACTOR 0.1 /* Max star velocity / yvel */ #define BOUYANCY 0.2 /* Reduction in grav deceleration for stars */ #define MAXSTARS 75 /* Number of stars issued from a shell */ #define MINSTARS 50 #define MINSFUSE 50 /* Range of fuse lengths for stars */ #define MAXSFUSE 100 #define INTRAND(min,max) ((int) (random()%((max+1)-(min))+(min))) #define FLOATRAND(min,max) ((min)+(random()/MAXRAND)*((max)-(min))) typedef struct { int state; int shelltype; unsigned long color1, color2; int fuse; float xvel, yvel; float x, y; int nstars; #if STARSIZE > 1 XRectangle Xpoints[MAXSTARS]; XRectangle Xpoints2[MAXSTARS]; #else XPoint Xpoints[MAXSTARS]; XPoint Xpoints2[MAXSTARS]; #endif float sx[MAXSTARS], sy[MAXSTARS]; /* Distance from notional * center */ float sxvel[MAXSTARS], syvel[MAXSTARS]; /* Relative to notional * center */ } rocket; typedef struct { Screen *scr; Colormap cmap; int p_ignite; unsigned long bgpixel; unsigned long fgpixel; unsigned long rockpixel; GC bgGC; int nflying; int fusilcount; int width, lmargin, rmargin, height; float minvelx, maxvelx; float minvely, maxvely; float maxsvel; float rockdecel, stardecel; rocket *rockq; } pyrostruct; static pyrostruct pyros[MAXSCREENS]; static int orig_p_ignite; static int just_started = True;/* Greet the user right away */ static void ignite(pyrostruct *pp); static void animate(Window win, pyrostruct *pp, rocket *rp); static void shootup(Window win, pyrostruct *pp, rocket *rp); static void burst(Window win, pyrostruct *pp, rocket *rp); void initpyro(Window win) { pyrostruct *pp = &pyros[screen]; rocket *rp; XWindowAttributes xwa; XGCValues xgcv; unsigned int rockn; #if STARSIZE > 1 unsigned int starn; short bsize; #endif XGetWindowAttributes(dsp, win, &xwa); if ((batchcount < 1) || (batchcount > 1024)) batchcount = 40; orig_p_ignite = P_IGNITE / batchcount; if (orig_p_ignite <= 0) orig_p_ignite = 1; pp->p_ignite = orig_p_ignite; if (pp->rockq == NULL) { pp->rockq = calloc(batchcount, sizeof(rocket)); if (pp->rockq == NULL) error("allocation failed, unable to launch rockets\n"); } pp->nflying = pp->fusilcount = 0; #if STARSIZE > 1 bsize = (short) ((xwa.height <= 64) ? 1 : STARSIZE); #endif for (rockn = 0, rp = pp->rockq; rockn < batchcount; rockn++, rp++) { rp->state = SILENT; #if STARSIZE > 1 for (starn = 0; starn < MAXSTARS; starn++) { rp->Xpoints[starn].width = rp->Xpoints[starn].height = rp->Xpoints2[starn].width = rp->Xpoints2[starn].height = bsize; } #endif } pp->width = xwa.width; pp->lmargin = xwa.width / 16; pp->rmargin = xwa.width - pp->lmargin; pp->height = xwa.height; pp->scr = ScreenOfDisplay(dsp, screen); pp->cmap = xwa.colormap; pp->fgpixel = sswhite[screen].pixel; pp->bgpixel = ssblack[screen].pixel; if (!mono && Scr[screen].npixels > 3) pp->rockpixel = Scr[screen].pixels[3]; /* Just the right shade of * orange */ else pp->rockpixel = pp->fgpixel; if (!pp->bgGC) { xgcv.foreground = pp->bgpixel; pp->bgGC = XCreateGC(dsp, win, GCForeground, &xgcv); } /* Geometry-dependent physical data: */ pp->maxvelx = (float) (xwa.width) * XVELFACTOR; pp->minvelx = -pp->maxvelx; pp->minvely = -(float) (xwa.height) * MINYVELFACTOR; pp->maxvely = -(float) (xwa.height) * MAXYVELFACTOR; pp->maxsvel = pp->minvely * SVELFACTOR; pp->rockdecel = (float) (pp->height) * GRAVFACTOR; pp->stardecel = pp->rockdecel * BOUYANCY; XFillRectangle(dsp, win, pp->bgGC, 0, 0, xwa.width, xwa.height); } void drawpyro(Window win) { pyrostruct *pp = &pyros[screen]; rocket *rp; int rockn; if (pp->p_ignite == 0) pp->p_ignite = 1; if (just_started || (random() % pp->p_ignite == 0)) { just_started = False; if (random() % P_FUSILLADE == 0) { pp->p_ignite = orig_p_ignite / FUSILFACTOR; pp->fusilcount = INTRAND(FUSILLEN * 9 / 10, FUSILLEN * 11 / 10); } ignite(pp); if (pp->fusilcount > 0) { if (--pp->fusilcount == 0) pp->p_ignite = orig_p_ignite; } } for (rockn = pp->nflying, rp = pp->rockq; rockn > 0; rp++) { if (rp->state != SILENT) { animate(win, pp, rp); rockn--; } } } static void ignite(pyrostruct *pp) { rocket *rp; int multi, shelltype, nstars, fuse, npix, pix; unsigned long color1, color2; float xvel, yvel, x; x = random() % pp->width; xvel = FLOATRAND(-pp->maxvelx, pp->maxvelx); /* All this to stop too many rockets going offscreen: */ if (x < pp->lmargin && xvel < 0.0 || x > pp->rmargin && xvel > 0.0) xvel = -xvel; yvel = FLOATRAND(pp->minvely, pp->maxvely); fuse = INTRAND(MINFUSE, MAXFUSE); nstars = INTRAND(MINSTARS, MAXSTARS); if (!mono && (npix = Scr[screen].npixels) > 2) { color1 = Scr[screen].pixels[pix = (int) random() % npix]; color2 = Scr[screen].pixels[(pix + (npix / 2)) % npix]; } else { color1 = color2 = sswhite[screen].pixel; } multi = 1; if (random() % P_DOUBLECLOUD == 0) shelltype = DOUBLECLOUD; else { shelltype = CLOUD; if (random() % P_MULTI == 0) multi = INTRAND(5, 15); } rp = pp->rockq; while (multi--) { if (pp->nflying >= batchcount) return; while (rp->state != SILENT) rp++; pp->nflying++; rp->shelltype = shelltype; rp->state = REDGLARE; rp->color1 = color1; rp->color2 = color2; rp->xvel = xvel; rp->yvel = FLOATRAND(yvel * 0.97, yvel * 1.03); rp->fuse = INTRAND((fuse * 90) / 100, (fuse * 110) / 100); rp->x = x + FLOATRAND(multi * 7.6, multi * 8.4); rp->y = pp->height - 1; rp->nstars = nstars; } } static void animate( Window win, pyrostruct *pp, rocket *rp ) { int starn; float r, theta; if (rp->state == REDGLARE) { shootup(win, pp, rp); /* Handle setup for explosion */ if (rp->state == BURSTINGINAIR) { for (starn = 0; starn < rp->nstars; starn++) { rp->sx[starn] = rp->sy[starn] = 0.0; rp->Xpoints[starn].x = (short) rp->x; rp->Xpoints[starn].y = (short) rp->y; if (rp->shelltype == DOUBLECLOUD) { rp->Xpoints2[starn].x = (short) rp->x; rp->Xpoints2[starn].y = (short) rp->y; } /* This isn't accurate solid geometry, but it looks OK. */ r = FLOATRAND(0.0, pp->maxsvel); theta = FLOATRAND(0.0, TWOPI); rp->sxvel[starn] = r * cos(theta); rp->syvel[starn] = r * sin(theta); } rp->fuse = INTRAND(MINSFUSE, MAXSFUSE); } } if (rp->state == BURSTINGINAIR) { burst(win, pp, rp); } } static void shootup( Window win, pyrostruct *pp, rocket *rp ) { XFillRectangle(dsp, win, pp->bgGC, (int) (rp->x), (int) (rp->y), ROCKETW, ROCKETH + 3); if (rp->fuse-- <= 0) { rp->state = BURSTINGINAIR; return; } rp->x += rp->xvel; rp->y += rp->yvel; rp->yvel += pp->rockdecel; XSetForeground(dsp, Scr[screen].gc, pp->rockpixel); XFillRectangle(dsp, win, Scr[screen].gc, (int) (rp->x), (int) (rp->y), ROCKETW, (int) (ROCKETH + random() % 4)); } static void burst( Window win, pyrostruct *pp, rocket *rp ) { register int starn; register int nstars, stype; register float rx, ry, sd; /* Help compiler optimize :-) */ register float sx, sy; nstars = rp->nstars; stype = rp->shelltype; #if STARSIZE > 1 XFillRectangles(dsp, win, pp->bgGC, rp->Xpoints, nstars); if (stype == DOUBLECLOUD) XFillRectangles(dsp, win, pp->bgGC, rp->Xpoints2, nstars); #else XDrawPoints(dsp, win, pp->bgGC, rp->Xpoints, nstars, CoordModeOrigin); if (stype == DOUBLECLOUD) XDrawPoints(dsp, win, pp->bgGC, rp->Xpoints2, nstars, CoordModeOrigin); #endif if (rp->fuse-- <= 0) { rp->state = SILENT; pp->nflying--; return; } /* Stagger the stars' decay */ if (rp->fuse <= 7) { if ((rp->nstars = nstars = nstars * 90 / 100) == 0) return; } rx = rp->x; ry = rp->y; sd = pp->stardecel; for (starn = 0; starn < nstars; starn++) { sx = rp->sx[starn] += rp->sxvel[starn]; sy = rp->sy[starn] += rp->syvel[starn]; rp->syvel[starn] += sd; rp->Xpoints[starn].x = (short) (rx + sx); rp->Xpoints[starn].y = (short) (ry + sy); if (stype == DOUBLECLOUD) { rp->Xpoints2[starn].x = (short) (rx + 1.7 * sx); rp->Xpoints2[starn].y = (short) (ry + 1.7 * sy); } } rp->x = rx + rp->xvel; rp->y = ry + rp->yvel; rp->yvel += sd; XSetForeground(dsp, Scr[screen].gc, rp->color1); #if STARSIZE > 1 XFillRectangles(dsp, win, Scr[screen].gc, rp->Xpoints, nstars); if (stype == DOUBLECLOUD) { XSetForeground(dsp, Scr[screen].gc, rp->color2); XFillRectangles(dsp, win, Scr[screen].gc, rp->Xpoints2, nstars); } #else XDrawPoints(dsp, win, Scr[screen].gc, rp->Xpoints, nstars, CoordModeOrigin); if (stype == DOUBLECLOUD) { XSetForeground(dsp, Scr[screen].gc, rp->color2); XDrawPoints(dsp, win, Scr[screen].gc, rp->Xpoints2, nstars, CoordModeOrigin); } #endif }