
#ifndef lint
static char sccsid[] = "@(#)cartoon.c  3.11h 96/09/30 xlockmore";

#endif

/*-
 * cartoon.c - A bouncing cartoon for xlock, the X Window System lockscreen.
 *
 * Copyright (c) 1988 by Sun Microsystems
 *
 * See xlock.c for copying information.
 *
 * Revision History:
 *
 * 14-Oct-96:  (dhansen@metapath.com)
 *             Updated the equations for mapping the charater
 *             to follow a parabolic time curve.  Also adjust
 *             the rate based on the size (area) of the cartoon.
 *             Eliminated unused variables carried over from bounce.c
 *             Still needs a user attribute to control the rate
 *             depending on their computer and preference.
 *
 * 20-Sep-94: I done this file starting from the previous bounce.c
 *       I draw the 8 cartoons like they are.
 *       Lorenzo Patocchi (patol@info.isbiel.ch)
 *       David monkeyed around with the cmap stuff to get it to work.
 *
 * 2-Sep-93: xlock version (David Bagley bagleyd@source.asset.com)
 * 1986: Sun Microsystems
 */


/*-
 * original copyright
 * **************************************************************************
 * Copyright 1988 by Sun Microsystems, Inc. Mountain View, CA.
 *
 * All Rights Reserved
 *
 * 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, and that the names of Sun or MIT not be used in advertising
 * or publicity pertaining to distribution of the software without specific
 * prior written permission. Sun and M.I.T. make no representations about the
 * suitability of this software for any purpose. It is provided "as is"
 * without any express or implied warranty.
 *
 * SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 * IN NO EVENT SHALL SUN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 * ***************************************************************************
 */

#if defined( HAS_XPM ) || defined( HAS_XPMINC )

#include "xlock.h"
#include <math.h>
#if defined( HAS_XPM ) || defined( HAS_XPMINC )
#if HAS_XPMINC
#include <xpm.h>
#else
#include <X11/xpm.h>		/* Normal spot */
#endif

#include "pixmaps/calvin.xpm"
#include "pixmaps/calvin2.xpm"
#include "pixmaps/calvin3.xpm"
#include "pixmaps/gravity.xpm"
#include "pixmaps/marino2.xpm"
#include "pixmaps/calvin4.xpm"
#include "pixmaps/calvinf.xpm"
#include "pixmaps/hobbes.xpm"
#ifdef ALL_CARTOONS		/* Craps out for me on Linux 1.3.20 8mRam 486 */
#include "pixmaps/dragon.xpm"
#include "pixmaps/cal_hob.xpm"
#define NUM_CARTOONS    10
#else
#define NUM_CARTOONS    8
#endif
#else
#define NUM_CARTOONS    0	/* Not much point... */
#endif

#define MAX_X_STRENGTH  8
#define MAX_Y_STRENGTH  3

ModeSpecOpt cartoon_opts =
{0, NULL, 0, NULL, NULL};

static char **cartoonlist[NUM_CARTOONS] =
{
	calvin, calvin2, calvin3, gravity, marino2, calvin4, calvinf, hobbes
#if ALL_CARTOONS
	,dragon, cal_hob
#endif
};

typedef struct {
	int         x, y, xlast, ylast;
	int         vx, vy;
	int         ncartoons;
	int         ytime, yrate, ybase;
	int         width, height;
	int         cwidth, cheight;
	GC          backGC;
	XImage     *images[NUM_CARTOONS];
	int         choice;
	Colormap    cm;
	unsigned long black, white;
} cartoonstruct;

static cartoonstruct *cartoons = NULL;

static void erase_image(Display * display, Window win, GC gc,
			int x, int y, int xl, int yl, int xsize, int ysize);

static int  tbx = 0;
static int  tby = 0;

static void
init_images(ModeInfo * mi)
{
#if defined( HAS_XPM ) || defined( HAS_XPMINC )
	cartoonstruct *cp = &cartoons[MI_SCREEN(mi)];
	int         i = 0;
	XpmAttributes attrib;
	Display    *display = MI_DISPLAY(mi);
	Window      window = MI_WINDOW(mi);
	int         screen = MI_SCREEN(mi);

	if (MI_WIN_IS_INSTALL(mi) && !MI_WIN_IS_INROOT(mi)) {
		cp->cm = XCreateColormap(display, window, MI_VISUAL(mi), AllocNone);
		attrib.colormap = cp->cm;
		reserveColors(mi, cp->cm, &cp->black, &cp->white);
	} else
		attrib.colormap = DefaultColormap(display, screen);

	attrib.visual = DefaultVisual(display, screen);
	attrib.depth = DefaultDepth(display, screen);
	attrib.valuemask = XpmVisual | XpmColormap | XpmDepth;

	if (MI_NPIXELS(mi) > 2) {
		for (i = 0; i < NUM_CARTOONS; i++)
			if (XpmSuccess != XpmCreateImageFromData(display, cartoonlist[i],
				&(cp->images[i]), (XImage **) NULL, &attrib))
				break;
		cp->ncartoons = i;
	}
#endif
	if (MI_WIN_IS_VERBOSE(mi))
		(void) printf("%d full color images loaded.\n", cp->ncartoons);
	if (cp->ncartoons == 0)
		cp->ncartoons = -1;
	if (cp->cm != None) {
		if (cp->backGC == None) {
			XGCValues   xgcv;

			setColormap(display, window, cp->cm, MI_WIN_IS_INWINDOW(mi));
			xgcv.background = cp->black;
			cp->backGC = XCreateGC(display, window, GCBackground, &xgcv);
		}
	} else {
		cp->black = MI_WIN_BLACK_PIXEL(mi);
		cp->backGC = MI_GC(mi);
	}
}

static void
put_cartoon(ModeInfo * mi)
{
	cartoonstruct *cp = &cartoons[MI_SCREEN(mi)];

	if (cp->ncartoons > 0 && cp->choice >= 0) {
/* PURIFY 4.0 on SunOS4 reports a memory leak here */
		XPutImage(MI_DISPLAY(mi), MI_WINDOW(mi), cp->backGC,
			  cp->images[cp->choice], 0, 0,
			  cp->x - tbx, cp->y - tby,
			  cp->cwidth, cp->cheight);
		if (cp->xlast != -1) {
			erase_image(MI_DISPLAY(mi), MI_WINDOW(mi), cp->backGC,
				    cp->x - tbx, cp->y - tby,
				    cp->xlast, cp->ylast,
				    cp->cwidth, cp->cheight);
		}
	}
}				/* put_cartoon */

static void
select_cartoon(ModeInfo * mi)
{
	cartoonstruct *cp = &cartoons[MI_SCREEN(mi)];
	int         old;

	old = cp->choice;
	if (cp->ncartoons > 1)
		while (old == cp->choice)
			cp->choice = NRAND(cp->ncartoons);
	if (MI_WIN_IS_VERBOSE(mi))
		(void) printf("cartoon %d.\n", cp->choice);

	if (cp->ncartoons > 0) {
		cp->cwidth = cp->images[cp->choice]->width;
		cp->cheight = cp->images[cp->choice]->height;
	}
}				/* select_cartoon */

static void
move_cartoon(ModeInfo * mi)
{
	cartoonstruct *cp = &cartoons[MI_SCREEN(mi)];
	int         j;

	cp->xlast = cp->x - tbx;
	cp->ylast = cp->y - tby;

	cp->x += cp->vx;
	if ((cp->width > cp->cwidth && cp->x > cp->width - cp->cwidth) ||
	    (cp->width < cp->cwidth && cp->x < 0)) {
		/* Bounce off the right edge */
		cp->x = 2 * (cp->width - cp->cwidth) - cp->x;
		cp->vx = -cp->vx;
	} else if ((cp->width < cp->cwidth && cp->x > cp->width - cp->cwidth) ||
		   (cp->width > cp->cwidth && cp->x < 0)) {
		/* Bounce off the left edge */
		cp->x = -cp->x;
		cp->vx = -cp->vx;
	}
	cp->ytime++;

	cp->y += ((cp->ytime / cp->yrate) - cp->ybase);


	if (cp->y >= (cp->height + cp->cheight)) {
		/* Back at the bottom edge, time for a new cartoon */
		select_cartoon(mi);

		/* reset the direction */
		cp->vx = ((LRAND() & 1) ? -1 : 1) * NRAND(MAX_X_STRENGTH);

		cp->y = (cp->height + cp->cheight);
		cp->ytime = 0;
		j = 100000 / (cp->cwidth * cp->cheight);
		cp->yrate = (1 + NRAND(MAX_Y_STRENGTH)) * (j * j) / 4;
		cp->ybase = sqrt(((3 * cp->y / 4) + (NRAND(cp->y / 4))) *
				 2 / cp->yrate);
	}
}				/* move_cartoon */

/* This stops some flashing, could be more efficient */
static void
erase_image(Display * display, Window win, GC gc,
	    int x, int y, int xl, int yl, int xsize, int ysize)
{
	if (y < 0)
		y = 0;
	if (x < 0)
		x = 0;
	if (xl < 0)
		xl = 0;
	if (yl < 0)
		yl = 0;

	if (xl - x > 0)
		XFillRectangle(display, win, gc, x + xsize, yl, xl - x, ysize);
	if (yl - y > 0)
		XFillRectangle(display, win, gc, xl, y + ysize, xsize, yl - y);
	if (y - yl > 0)
		XFillRectangle(display, win, gc, xl, yl, xsize, y - yl);
	if (x - xl > 0)
		XFillRectangle(display, win, gc, xl, yl, x - xl, ysize);
}				/* erase_image */

void
init_cartoon(ModeInfo * mi)
{
	cartoonstruct *cp;
	int         i;

	if (cartoons == NULL) {
		if ((cartoons = (cartoonstruct *) calloc(MI_NUM_SCREENS(mi),
					    sizeof (cartoonstruct))) == NULL)
			return;
	}
	cp = &cartoons[MI_SCREEN(mi)];

	if (cp->ncartoons == 0) {
		cp->cm = None;
		cp->choice = -1;
		init_images(mi);
	}
	cp->width = MI_WIN_WIDTH(mi);
	cp->height = MI_WIN_HEIGHT(mi);

	/* set up intial movement */
	if (cp->cwidth < 1)
		cp->cwidth = 1;
	if (cp->cheight < 1)
		cp->cheight = 1;

	cp->vx = ((LRAND() & 1) ? -1 : 1) * NRAND(MAX_X_STRENGTH);

	cp->x = (cp->vx >= 0) ? 0 : cp->width - cp->cwidth;

	cp->y = cp->height + cp->cheight;
	cp->ytime = 0;
	i = 100000 / (cp->cwidth * cp->cheight);
	cp->yrate = (1 + NRAND(MAX_Y_STRENGTH)) * (i * i) / 4;
	cp->ybase = sqrt(((3 * cp->y / 4) +
			  (NRAND(cp->y / 4))) * 2 / cp->yrate);

	cp->xlast = -1;
	cp->ylast = 0;

	XSetForeground(MI_DISPLAY(mi), cp->backGC, cp->black);
	XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), cp->backGC,
		       0, 0, cp->width, cp->height);

}				/* init_cartoon */

void
draw_cartoon(ModeInfo * mi)
{
	put_cartoon(mi);
	move_cartoon(mi);
}				/* draw_cartoon */

void
release_cartoon(ModeInfo * mi)
{
	if (cartoons != NULL) {
#if defined( HAS_XPM ) || defined( HAS_XPMINC )
		int         screen;

		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
			cartoonstruct *cp = &cartoons[screen];

			int         i;

			for (i = 0; i < cp->ncartoons; i++)
				XDestroyImage(cp->images[i]);
			if (cp->cm != None) {
				if (cp->backGC != None)
					XFreeGC(MI_DISPLAY(mi), cp->backGC);
				XFreeColormap(MI_DISPLAY(mi), cp->cm);
			}
		}
#endif
		(void) free((void *) cartoons);
		cartoons = NULL;
	}
}				/* release_cartoon */

#endif
