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