1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* qix --- vector swirl */
3 
4 #if 0
5 static const char sccsid[] = "@(#)qix.c	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1991 by Patrick J. Naughton.
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 01-Nov-2000: Allocation checks
26  * 03-Mar-1998: Combined with Darrick Brown's "geometry".
27  * 10-May-1997: Compatible with xscreensaver
28  * 29-Jul-1990: support for multiple screens.
29  *	            made check_bounds_?() a macro.
30  *	            fixed initial parameter setup.
31  * 15-Dec-1989: Fix for proper skipping of {White,Black}Pixel() in colors.
32  * 08-Oct-1989: Fixed bug in memory allocation in init_qix().
33  *	            Moved seconds() to an extern.
34  * 23-Sep-1989: Switch to random() and fixed bug w/ less than 4 lines.
35  * 20-Sep-1989: Lint.
36  * 24-Mar-1989: Written.
37  */
38 
39 #ifdef STANDALONE
40 #define MODE_qix
41 #define DEFAULTS "*delay: 30000 \n" \
42 	"*count: -5 \n" \
43 	"*cycles: 32 \n" \
44 	"*ncolors: 200 \n" \
45 	"*fullrandom: True \n" \
46 
47 # define reshape_qix 0
48 # define qix_handle_event 0
49 #define SMOOTH_COLORS
50 #include "xlockmore.h"		/* in xscreensaver distribution */
51 #else /* STANDALONE */
52 #include "xlock.h"		/* in xlockmore distribution */
53 #endif /* STANDALONE */
54 
55 #ifdef MODE_qix
56 
57 #define DEF_COMPLETE  "False"
58 #define DEF_KALEID  "False"
59 #define DEF_SOLID  "False"
60 
61 static Bool complete;
62 static Bool kaleid;
63 static Bool solid;
64 
65 static XrmOptionDescRec opts[] =
66 {
67 	{(char *) "-complete", (char *) ".qix.complete", XrmoptionNoArg, (caddr_t) "on"},
68 	{(char *) "+complete", (char *) ".qix.complete", XrmoptionNoArg, (caddr_t) "off"},
69 	{(char *) "-kaleid", (char *) ".qix.kaleid", XrmoptionNoArg, (caddr_t) "on"},
70 	{(char *) "+kaleid", (char *) ".qix.kaleid", XrmoptionNoArg, (caddr_t) "off"},
71 	{(char *) "-solid", (char *) ".qix.solid", XrmoptionNoArg, (caddr_t) "on"},
72 	{(char *) "+solid", (char *) ".qix.solid", XrmoptionNoArg, (caddr_t) "off"}
73 };
74 static argtype vars[] =
75 {
76 	{(void *) & complete, (char *) "complete", (char *) "Complete", (char *) DEF_COMPLETE, t_Bool},
77 	{(void *) & kaleid, (char *) "kaleid", (char *) "Kaleid", (char *) DEF_KALEID, t_Bool},
78 	{(void *) & solid, (char *) "solid", (char *) "Solid", (char *) DEF_SOLID, t_Bool}
79 };
80 static OptionStruct desc[] =
81 {
82 	{(char *) "-/+complete", (char *) "turn on/off complete morphing graph"},
83 	{(char *) "-/+kaleid", (char *) "turn on/off complete kaleidoscope"},
84 	{(char *) "-/+solid", (char *) "turn on/off fills"}
85 };
86 
87 ENTRYPOINT ModeSpecOpt qix_opts =
88 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
89 
90 #ifdef USE_MODULES
91 ModStruct   qix_description =
92 {"qix", "init_qix", "draw_qix", "release_qix",
93  "refresh_qix", "init_qix", "free_qix", &qix_opts,
94  30000, -5, 32, 1, 64, 1.0, "",
95  "Shows spinning lines a la Qix(tm)", 0, NULL};
96 
97 #endif
98 
99 /* How many segments to draw per cycle when redrawing */
100 #define REDRAWSTEP 3
101 #define MINPOINTS 2
102 /*-
103  * remember when complete: the number of lines to be drawn is 1+2+3+...+N or
104  * N(N+1)/2
105  */
106 
107 /* I forget now, did Newton discover this as a boy? */
108 #define NEWT(n) (n*(n+1)/2)
109 
110 typedef struct {
111 	int         pix;
112 	int         first, last;
113 	XPoint     *delta, *position;
114 	int         npoints;
115 	int         offset;
116 	int         max_delta;
117 	int         width, height, mid, midx, midy;
118 	int         nlines, linecount;
119 	int         redrawing, redrawpos;
120 	XPoint     *lineq, *points;
121 	Bool        complete, kaleid, solid;
122 } qixstruct;
123 
124 static qixstruct *qixs = (qixstruct *) NULL;
125 
126 #define check_bounds(qp, val, del, min, max)				\
127 {								\
128     if ((val) < (min)) {						\
129 	*(del) = NRAND((qp)->max_delta) + (qp)->offset;	\
130     } else if ((val) >= (max)) {					\
131 	*(del) = -(NRAND((qp)->max_delta)) - (qp)->offset;	\
132     }								\
133 }
134 
135 static void
free_qix_screen(qixstruct * qp)136 free_qix_screen(qixstruct *qp)
137 {
138 	if (qp == NULL) {
139 		return;
140 	}
141 	if (qp->lineq != NULL) {
142 		free(qp->lineq);
143 		qp->lineq = (XPoint *) NULL;
144 	}
145 	if (qp->points != NULL) {
146 		free(qp->points);
147 		qp->points = (XPoint *) NULL;
148 	}
149 	if (qp->delta != NULL) {
150 		free(qp->delta);
151 		qp->delta = (XPoint *) NULL;
152 	}
153 	if (qp->position != NULL) {
154 		free(qp->position);
155 		qp->position = (XPoint *) NULL;
156 	}
157 	qp = NULL;
158 }
159 
160 ENTRYPOINT void
free_qix(ModeInfo * mi)161 free_qix(ModeInfo * mi)
162 {
163 	free_qix_screen(&qixs[MI_SCREEN(mi)]);
164 }
165 
166 ENTRYPOINT void
init_qix(ModeInfo * mi)167 init_qix(ModeInfo * mi)
168 {
169 	qixstruct  *qp;
170 	int         i;
171 
172 	MI_INIT(mi, qixs);
173 	qp = &qixs[MI_SCREEN(mi)];
174 
175 	qp->width = MI_WIDTH(mi);
176 	qp->height = MI_HEIGHT(mi);
177 	qp->mid = MAX(qp->width, qp->height) / 2;
178 	qp->midx = qp->width / 2;
179 	qp->midy = qp->height / 2;
180 	qp->max_delta = 16;
181 	qp->redrawing = 0;
182 	qp->linecount = 0;
183 
184 	if (qp->width < 100) {	/* icon window */
185 		qp->max_delta /= 4;
186 	}
187 	qp->offset = qp->max_delta / 3;
188 	qp->last = 0;
189 	if (MI_NPIXELS(mi) > 2)
190 		qp->pix = NRAND(MI_NPIXELS(mi));
191 
192 	qp->npoints = MI_COUNT(mi);
193 	if (qp->npoints < -MINPOINTS)
194 		qp->npoints = NRAND(-qp->npoints - MINPOINTS + 1) + MINPOINTS;
195 	/* Absolute minimum */
196 	if (qp->npoints < MINPOINTS)
197 		qp->npoints = MINPOINTS;
198 
199 	if (MI_IS_FULLRANDOM(mi))
200 		qp->complete = (Bool) (!NRAND(4));
201 	else
202 		qp->complete = complete;
203 
204 	if (qp->complete) {
205 		qp->kaleid = False;
206 	} else {
207 		if (MI_IS_FULLRANDOM(mi))
208 			qp->kaleid = (Bool) (!NRAND(3));
209 		else
210 			qp->kaleid = kaleid;
211 	}
212 	if (qp->complete || qp->kaleid) {
213 		qp->solid = False;
214 	} else {
215 		if (MI_IS_FULLRANDOM(mi))
216 			qp->solid = (Bool) (LRAND() & 1);
217 		else
218 			qp->solid = solid;
219 	}
220 	if (qp->delta)
221 		free(qp->delta);
222 	if (qp->position)
223 		free(qp->position);
224 
225 	if ((qp->delta = (XPoint *) malloc(qp->npoints *
226 			sizeof (XPoint))) == NULL) {
227 		free_qix_screen(qp);
228 		return;
229 	}
230 	if ((qp->position = (XPoint *) malloc(qp->npoints *
231 			sizeof (XPoint))) == NULL) {
232 		free_qix_screen(qp);
233 		return;
234 	}
235 	for (i = 0; i < qp->npoints; i++) {
236 		qp->delta[i].x = NRAND(qp->max_delta) + qp->offset;
237 		qp->delta[i].y = NRAND(qp->max_delta) + qp->offset;
238 		qp->position[i].x = NRAND(qp->width);
239 		qp->position[i].y = NRAND(qp->height);
240 	}
241 
242 	qp->nlines = (qp->npoints == 2) ? (MI_CYCLES(mi) + 1) * 2 :
243 		((MI_CYCLES(mi) + qp->npoints) / qp->npoints) * qp->npoints;
244 	if (qp->complete) {
245 		qp->max_delta /= 4;
246 		qp->nlines = qp->npoints;
247 	}
248 	if (qp->kaleid) {
249 		qp->nlines = (MI_CYCLES(mi) + 1) * 8;	/* Fudge it so its compatible */
250 	}
251 	if (qp->lineq) {
252 		free(qp->lineq);
253 		qp->lineq = (XPoint *) NULL;
254 	}
255 	if (qp->points) {
256 		free(qp->points);
257 		qp->points = (XPoint *) NULL;
258 	}
259 	if (!qp->kaleid) {
260 		if ((qp->lineq = (XPoint *) malloc(qp->nlines *
261 				sizeof (XPoint))) == NULL) {
262 			free_qix_screen(qp);
263 			return;
264 		}
265 		for (i = 0; i < qp->nlines; i++)
266 			qp->lineq[i].x = qp->lineq[i].y = -1;	/* move initial point off screen */
267 		if (qp->solid) {
268 			if ((qp->points = (XPoint *) malloc(2 * qp->nlines *
269 					sizeof (XPoint))) == NULL) {
270 				free_qix_screen(qp);
271 				return;
272 			}
273 		}
274 	}
275 	MI_CLEARWINDOW(mi);
276 }
277 
278 ENTRYPOINT void
draw_qix(ModeInfo * mi)279 draw_qix(ModeInfo * mi)
280 {
281 	Display    *display = MI_DISPLAY(mi);
282 	GC          gc = MI_GC(mi);
283 	qixstruct  *qp = &qixs[MI_SCREEN(mi)];
284 	int         i, j, k, l, prev;
285 
286 	qp->first = (qp->last + qp->npoints) % qp->nlines;
287 	prev = (qp->last - qp->npoints + qp->nlines) % qp->nlines;
288 
289 	MI_IS_DRAWN(mi) = True;
290 
291 
292 	for (i = 0; i < qp->npoints; i++) {
293 		qp->position[i].x += qp->delta[i].x;
294 		qp->position[i].y += qp->delta[i].y;
295 		if (NRAND(20) < 1) {
296 			check_bounds(qp, qp->position[i].x, &qp->delta[i].x,
297 				     qp->width / 3, 2 * qp->width / 3);
298 		} else {
299 			check_bounds(qp, qp->position[i].x, &qp->delta[i].x, 0, qp->width);
300 		}
301 		if (NRAND(20) < 1) {
302 			check_bounds(qp, qp->position[i].y, &qp->delta[i].y,
303 				     qp->height / 3, 2 * qp->height / 3);
304 		} else {
305 			check_bounds(qp, qp->position[i].y, &qp->delta[i].y, 0, qp->height);
306 		}
307 	}
308 	XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
309 	if (qp->complete && qp->npoints > 3) {
310 		for (i = 0; i < qp->npoints - 1; i++)
311 			for (j = i + 1; j < qp->npoints; j++) {
312 				XDrawLine(display, MI_WINDOW(mi), gc,
313 					qp->lineq[qp->first + i].x,
314 					qp->lineq[qp->first + i].y,
315 					qp->lineq[qp->first + j].x,
316 					qp->lineq[qp->first + j].y);
317 			}
318 	} else if (!qp->kaleid) {
319 		if (qp->solid) {
320 			if (qp->lineq[qp->last].x != -1 ||
321 			    qp->lineq[qp->last].y != -1) {
322 			  for (i = 0; i < qp->npoints; i++) {
323 			    qp->points[i] = qp->lineq[qp->last + i];
324 			    qp->points[i + qp->npoints] =
325 			      qp->lineq[qp->first + qp->npoints - 1 - i];
326 			  }
327 			  XFillPolygon(display, MI_WINDOW(mi), gc,
328 			    qp->points, 2 * qp->npoints,
329 			    Complex, CoordModeOrigin);
330 			}
331 		} else {
332 			for (i = 1; i < qp->npoints +
333 			    ((qp->npoints == 2) ? -1 : 0); i++)
334 			  XDrawLine(display, MI_WINDOW(mi), gc,
335 			    qp->lineq[qp->first + i - 1].x,
336 			    qp->lineq[qp->first + i - 1].y,
337 			    qp->lineq[qp->first + i].x,
338 			    qp->lineq[qp->first + i].y);
339 			XDrawLine(display, MI_WINDOW(mi), gc,
340 			  qp->lineq[qp->first].x, qp->lineq[qp->first].y,
341 			  qp->lineq[qp->first + qp->npoints - 1].x,
342 			  qp->lineq[qp->first + qp->npoints - 1].y);
343 		}
344 	}
345 	if (MI_NPIXELS(mi) > 2) {
346 		XSetForeground(display, gc, MI_PIXEL(mi, qp->pix));
347 		if (++qp->pix >= MI_NPIXELS(mi))
348 			qp->pix = 0;
349 	} else
350 		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
351 
352 	if (qp->complete && qp->npoints > 3) {
353 		for (i = 0; i < qp->npoints - 1; i++)
354 			for (j = i + 1; j < qp->npoints; j++)
355 				XDrawLine(display, MI_WINDOW(mi), gc,
356 					qp->position[i].x, qp->position[i].y,
357 				       qp->position[j].x, qp->position[j].y);
358 	} else if (qp->kaleid) {
359 		for (i = 0; i < qp->npoints; i++) {
360 			XSegment    segs[8];
361 			XPoint      small[2];
362 
363 			j = (i + 1) % qp->npoints;
364 			small[0].x = ABS(qp->position[i].x - qp->mid);
365 			small[0].y = ABS(qp->position[i].y - qp->mid);
366 			small[1].x = ABS(qp->position[j].x - qp->mid);
367 			small[1].y = ABS(qp->position[j].y - qp->mid);
368 			segs[0].x1 = qp->midx + small[0].x;
369 			segs[0].y1 = qp->midy + small[0].y;
370 			segs[0].x2 = qp->midx + small[1].x;
371 			segs[0].y2 = qp->midy + small[1].y;
372 			segs[1].x1 = qp->midx - small[0].x;
373 			segs[1].y1 = qp->midy + small[0].y;
374 			segs[1].x2 = qp->midx - small[1].x;
375 			segs[1].y2 = qp->midy + small[1].y;
376 			segs[2].x1 = qp->midx - small[0].x;
377 			segs[2].y1 = qp->midy - small[0].y;
378 			segs[2].x2 = qp->midx - small[1].x;
379 			segs[2].y2 = qp->midy - small[1].y;
380 			segs[3].x1 = qp->midx + small[0].x;
381 			segs[3].y1 = qp->midy - small[0].y;
382 			segs[3].x2 = qp->midx + small[1].x;
383 			segs[3].y2 = qp->midy - small[1].y;
384 			segs[4].x1 = qp->midx + small[0].y;
385 			segs[4].y1 = qp->midy + small[0].x;
386 			segs[4].x2 = qp->midx + small[1].y;
387 			segs[4].y2 = qp->midy + small[1].x;
388 			segs[5].x1 = qp->midx + small[0].y;
389 			segs[5].y1 = qp->midy - small[0].x;
390 			segs[5].x2 = qp->midx + small[1].y;
391 			segs[5].y2 = qp->midy - small[1].x;
392 			segs[6].x1 = qp->midx - small[0].y;
393 			segs[6].y1 = qp->midy - small[0].x;
394 			segs[6].x2 = qp->midx - small[1].y;
395 			segs[6].y2 = qp->midy - small[1].x;
396 			segs[7].x1 = qp->midx - small[0].y;
397 			segs[7].y1 = qp->midy + small[0].x;
398 			segs[7].x2 = qp->midx - small[1].y;
399 			segs[7].y2 = qp->midy + small[1].x;
400 			XDrawSegments(display, MI_WINDOW(mi), gc, segs, 8);
401 			if (qp->npoints == 2)	/* do not repeat drawing the same line */
402 				break;
403 		}
404 		if (++qp->linecount >= qp->nlines) {
405 			qp->linecount = 0;
406 			MI_CLEARWINDOW(mi);
407 		}
408 	} else {
409 		if (qp->solid) {
410 			if (qp->lineq[prev].x != -1 ||
411 			    qp->lineq[prev].y != -1) {
412 			  for (i = 0; i < qp->npoints; i++) {
413 			    qp->points[i] = qp->lineq[prev + i];
414 			    qp->points[i + qp->npoints] =
415 			      qp->position[qp->npoints - 1 - i];
416 			  }
417 			  XFillPolygon(display, MI_WINDOW(mi), gc,
418 			    qp->points, 2 * qp->npoints,
419 			    Complex, CoordModeOrigin);
420 			}
421 		} else {
422 			for (i = 1; i < qp->npoints + ((qp->npoints == 2) ? -1 : 0); i++)
423 				XDrawLine(display, MI_WINDOW(mi), gc,
424 					qp->position[i - 1].x,
425 					qp->position[i - 1].y,
426 					qp->position[i].x,
427 					qp->position[i].y);
428 			XDrawLine(display, MI_WINDOW(mi), gc,
429 				qp->position[0].x, qp->position[0].y,
430 				qp->position[qp->npoints - 1].x,
431 				qp->position[qp->npoints - 1].y);
432 		}
433 	}
434 
435 	if (!qp->kaleid)
436 		for (i = 0; i < qp->npoints; i++) {
437 			qp->lineq[qp->last].x = qp->position[i].x;
438 			qp->lineq[qp->last].y = qp->position[i].y;
439 			qp->last++;
440 			if (qp->last >= qp->nlines)
441 				qp->last = 0;
442 		}
443 	if (qp->redrawing) {
444 		for (i = 0; i < REDRAWSTEP; i++) {
445 			int jold = (qp->last - qp->redrawpos + qp->nlines - qp->npoints) % qp->nlines;
446 
447 			j = (qp->first - qp->redrawpos + qp->nlines - qp->npoints) % qp->nlines;
448 			if (qp->complete && qp->npoints > 3) {
449 				for (k = 0; k < qp->npoints - 1; k++)
450 					for (l = k + 1; l < qp->npoints; l++)
451 					  XDrawLine(display, MI_WINDOW(mi), gc,
452 					    qp->lineq[j + k].x,
453 					    qp->lineq[j + k].y,
454 					    qp->lineq[j + l].x,
455 					    qp->lineq[j + l].y);
456 			} else if (!qp->kaleid) {
457 				if (qp->solid) {
458 				  if (qp->lineq[jold].x != -1 ||
459 				      qp->lineq[jold].y != -1) {
460 				    for (i = 0; i < qp->npoints; i++) {
461 				      qp->points[i] = qp->lineq[jold + i];
462 				      qp->points[i + qp->npoints] =
463 				        qp->lineq[j + qp->npoints - 1 - i];
464 				    }
465 				    XFillPolygon(display, MI_WINDOW(mi), gc,
466 				      qp->points, 2 * qp->npoints,
467 				      Complex, CoordModeOrigin);
468 				  }
469 				} else {
470 				  for (k = 1; k < qp->npoints +
471 				      ((qp->npoints == 2) ? -1 : 0); k++)
472 				    XDrawLine(display, MI_WINDOW(mi), gc,
473 				      qp->lineq[j + k - 1].x,
474 				      qp->lineq[j + k - 1].y,
475 				      qp->lineq[j + k].x,
476 				      qp->lineq[j + k].y);
477 				  XDrawLine(display, MI_WINDOW(mi), gc,
478 				    qp->lineq[j].x, qp->lineq[j].y,
479 				    qp->lineq[j + qp->npoints - 1].x,
480 				    qp->lineq[j + qp->npoints - 1].y);
481 				}
482 			}
483 			qp->redrawpos += qp->npoints;
484 			if (qp->redrawpos >= qp->nlines - qp->npoints) {
485 				qp->redrawing = 0;
486 				break;
487 			}
488 		}
489 	}
490 }
491 
492 ENTRYPOINT void
release_qix(ModeInfo * mi)493 release_qix(ModeInfo * mi)
494 {
495 	if (qixs != NULL) {
496 		int         screen;
497 
498 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
499 			free_qix_screen(&qixs[screen]);
500 		free(qixs);
501 		qixs = (qixstruct *) NULL;
502 	}
503 }
504 
505 #ifndef STANDALONE
506 ENTRYPOINT void
refresh_qix(ModeInfo * mi)507 refresh_qix(ModeInfo * mi)
508 {
509 	qixstruct  *qp = &qixs[MI_SCREEN(mi)];
510 
511 	MI_CLEARWINDOW(mi);
512 	qp->redrawing = 1;
513 	qp->redrawpos = 0;
514 }
515 #endif
516 
517 XSCREENSAVER_MODULE ("Qix", qix)
518 
519 #endif /* MODE_qix */
520