1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* munch --- munching squares */
3 
4 #if 0
5 static const char sccsid[] = "@(#)munch.c	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * Tim Showalter <tjs@andrew.cmu.edu>
23  *
24  * Copyright 1997, Tim Showalter
25  * Permission is granted to copy, modify, and use this as long
26  * as this notice remains intact.  No warranties are expressed or implied.
27  * CMU Sucks.
28  *
29  * Some code stolen from / This is meant to work with
30  * xscreensaver, Copyright (c) 1992, 1995, 1996
31  * Jamie Zawinski <jwz AT jwz.org>
32  *
33  * Permission to use, copy, modify, distribute, and sell this software and its
34  * documentation for any purpose is hereby granted without fee, provided that
35  * the above copyright notice appear in all copies and that both that
36  * copyright notice and this permission notice appear in supporting
37  * documentation.  No representations are made about the suitability of this
38  * software for any purpose.  It is provided "as is" without express or
39  * implied warranty.
40  *
41  * Revision History:
42  * 01-Nov-2000: Allocation checks
43  */
44 
45 /*-
46  * Munching Squares is this simplistic, silly screen hack (according to
47  * HAKMEM, discovered by Jackson Wright in 1962) where you take Y = X XOR T
48  * and graph it over and over.  According to HAKMEM, it  takes 5 instructions
49  * of PDP-1 assembly.  This is a little more complicated than that, mostly X's
50  *  fault, but it does some other random things.
51  * http://www.inwap.com/pdp10/hbaker/hakmem/hacks.html#item146
52  */
53 
54 #ifdef STANDALONE
55 #define MODE_munch
56 #define DEFAULTS "*delay: 5000 \n" \
57 	"*cycles: 7 \n" \
58 
59 # define free_munch 0
60 # define reshape_munch 0
61 # define munch_handle_event 0
62 #include "xlockmore.h"		/* from the xscreensaver distribution */
63 #else /* !STANDALONE */
64 #include "xlock.h"		/* from the xlockmore distribution */
65 #endif /* !STANDALONE */
66 
67 #ifdef MODE_munch
68 
69 ENTRYPOINT ModeSpecOpt munch_opts =
70 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
71 
72 #ifdef USE_MODULES
73 ModStruct   munch_description =
74 {"munch", "init_munch", "draw_munch", "release_munch",
75  "init_munch", "init_munch", (char *) NULL, &munch_opts,
76  5000, 1, 7, 1, 64, 1.0, "",
77  "Shows munching squares", 0, NULL};
78 
79 #endif
80 
81 #ifdef DEBUG
82 #include <assert.h>
83 #endif
84 
85 /* flags for random things.  Must be < log2(random's maximum), incidentally. */
86 #define SHIFT_KX (0x01)
87 #define SHIFT_KT (0x02)
88 #define SHIFT_KY (0x04)
89 #define GRAV     (0x08)
90 
91 typedef struct {
92 	int         width, height;
93 	int         logminwidth;
94 	int         logmaxwidth;
95 	GC          gc;
96 	int         thiswidth;
97 	int         t;
98 	int         atX, atY;
99 	int         kX, kT, kY;
100 	int         grav;
101 } munchstruct;
102 
103 static munchstruct *munches = (munchstruct *) NULL;
104 
105 static void
munchBit(ModeInfo * mi,int width,int atX,int atY,int kX,int kT,int kY,int grav)106 munchBit(ModeInfo * mi, int width,	/* pixels */
107 	 int atX, int atY,	/* pixels */
108 	 int kX, int kT, int kY,	/* pixels */
109 	 int grav /* 0 or not */ )
110 {
111 	munchstruct *mp = &munches[MI_SCREEN(mi)];
112 
113 	int         x, y;
114 	int         drawX, drawY;
115 
116 #if 0
117 	(void) fprintf(stderr, "Doing width %d at %d %d shift %d %d %d grav %d\n",
118 		       width, atX, atY, kX, kT, kY, grav);
119 #endif
120 
121 	for (x = 0; x < width; x++) {
122 		/* figure out the next point */
123 		y = ((x ^ ((mp->t + kT) % width)) + kY) % width;
124 
125 		drawX = ((x + kX) % width) + atX;
126 		drawY = (grav ? y + atY : atY + width - 1 - y);
127 
128 		/* used to be bugs where it would draw partially offscreen.
129 		   while that might be a pretty feature, I didn't want it to do
130 		   that yet.  if these trigger, please let me know.
131 		 */
132 #ifdef DEBUG
133 		assert(drawX >= 0 && drawX < MI_WIDTH(mi));
134 		assert(drawY >= 0 && drawY < MI_HEIGHT(mi));
135 #endif
136 
137 		XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), mp->gc, drawX, drawY);
138 		/* XXX may want to change this to XDrawPoints,
139 		   but it's fast enough without it for the moment. */
140 
141 	}
142 }
143 
144 /*
145  * dumb way to get # of digits in number.  Probably faster than actually
146  * doing a log and a division, maybe.
147  */
148 static int
dumb_log_2(int k)149 dumb_log_2(int k)
150 {
151 	int         r = -1;
152 
153 	while (k > 0) {
154 		k >>= 1;
155 		r++;
156 	}
157 	return r;
158 }
159 
160 static void
free_munch_screen(Display * display,munchstruct * mp)161 free_munch_screen(Display * display, munchstruct * mp)
162 {
163 	if (mp == NULL) {
164 		return;
165 	}
166 	if (mp->gc)
167 		XFreeGC(display, mp->gc);
168 	mp = NULL;
169 }
170 
171 ENTRYPOINT void
init_munch(ModeInfo * mi)172 init_munch(ModeInfo * mi)
173 {
174 	Display    *display = MI_DISPLAY(mi);
175 	munchstruct *mp;
176 
177 	MI_INIT(mi, munches);
178 	mp = &munches[MI_SCREEN(mi)];
179 
180 	if (mp->gc == None) {
181 		if ((mp->gc = XCreateGC(display, MI_WINDOW(mi),
182 			     (unsigned long) 0, (XGCValues *) NULL)) == None)
183 			return;
184 	}
185 	mp->width = MI_WIDTH(mi);
186 	mp->height = MI_HEIGHT(mi);
187 
188 	/* We need a square; limit on screen size? */
189 	/* we want a power of 2 for the width or the munch doesn't fill up. */
190 	mp->logmaxwidth = (int)
191 		dumb_log_2((mp->height < mp->width) ? mp->height : mp->width);
192 
193 	XSetFunction(display, mp->gc, GXxor);
194 
195 	mp->logminwidth = MI_CYCLES(mi);
196 	if (mp->logminwidth < 2 || MI_IS_ICONIC(mi))
197 		mp->logminwidth = 2;
198 
199 	if (mp->logmaxwidth < mp->logminwidth)
200 		mp->logmaxwidth = mp->logminwidth;
201 
202 	mp->t = 0;
203 
204 	MI_CLEARWINDOW(mi);
205 }
206 
207 ENTRYPOINT void
draw_munch(ModeInfo * mi)208 draw_munch(ModeInfo * mi)
209 {
210 	munchstruct *mp;
211 
212 	if (munches == NULL)
213 		return;
214 	mp = &munches[MI_SCREEN(mi)];
215 	if (mp->gc == None)
216 		return;
217 
218 	MI_IS_DRAWN(mi) = True;
219 	if (!mp->t) {		/* New one */
220 		int         randflags = (int) LRAND();
221 
222 		/* choose size -- power of two */
223 		mp->thiswidth = (int) (1 << (mp->logminwidth +
224 		       (LRAND() % (1 + mp->logmaxwidth - mp->logminwidth))));
225 
226 		if (MI_NPIXELS(mi) > 2)
227 			XSetForeground(MI_DISPLAY(mi), mp->gc,
228 				       MI_PIXEL(mi, NRAND(MI_NPIXELS(mi))));
229 		else		/* Xor'red so WHITE may not be appropriate */
230 			XSetForeground(MI_DISPLAY(mi), mp->gc, 1);
231 
232 		mp->atX = (int) (LRAND() %
233 				 ((mp->width <= mp->thiswidth) ? 1 : mp->width - mp->thiswidth));
234 		mp->atY = (int) (LRAND() %
235 				 ((mp->height <= mp->thiswidth) ? 1 : mp->height - mp->thiswidth));
236 
237 		/* wrap-around by these values; no need to %
238 		   as we end up doing that later anyway */
239 		mp->kX = (int) ((randflags & SHIFT_KX) ? LRAND() % mp->thiswidth : 0);
240 		mp->kT = (int) ((randflags & SHIFT_KT) ? LRAND() % mp->thiswidth : 0);
241 		mp->kY = (int) ((randflags & SHIFT_KY) ? LRAND() % mp->thiswidth : 0);
242 
243 		/* set the gravity of the munch, or rather,
244 		   which direction we draw stuff in. */
245 		mp->grav = randflags & GRAV;
246 	}
247 	/* Finally draw this munching square. */
248 	munchBit(mi,
249 		 mp->thiswidth,	/* Width, in pixels */
250 	/* draw at this location */
251 		 mp->atX, mp->atY, mp->kX, mp->kT, mp->kY, mp->grav);
252 
253 	mp->t++;
254 	if (mp->t == mp->thiswidth)
255 		mp->t = 0;
256 }
257 
258 ENTRYPOINT void
release_munch(ModeInfo * mi)259 release_munch(ModeInfo * mi)
260 {
261 	if (munches != NULL) {
262 		int         screen;
263 
264 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
265 			munchstruct *mp = &munches[screen];
266 			free_munch_screen(MI_DISPLAY(mi), mp);
267 		}
268 		free(munches);
269 		munches = (munchstruct *) NULL;
270 	}
271 }
272 
273 XSCREENSAVER_MODULE ("Munch", munch)
274 
275 #endif /* MODE_munch */
276