1 /* Whirlygig -- an experiment in X programming
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * When I was in trigonometry class as a kid, I remember being fascinated
12 * by the beauty of the shapes one receives when playing with sine waves
13 * Here is a little experiment to show that beauty is simple
17 #include "screenhack.h"
19 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
21 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
24 #define FULL_CYCLE 429496729
29 Bool writable; /* Is the screen writable */
30 double xspeed; /* A factor to modify the horizontal movement */
31 double yspeed; /* A factor to modify vertical movement */
34 int whirlies; /* How many whirlies per line do you want? */
35 int nlines; /* How many lines of whirlies do you want? */
36 int half_width; /* 1/2 the width of the screen */
48 spin_mode, funky_mode, circle_mode, linear_mode, test_mode, fun_mode, innie_mode, lissajous_mode
51 static void explain(int, int, struct info *, Display *, Window, GC);
52 static void draw_explain_string(int, int, Display *, Window, GC);
53 static void spin(unsigned long int, struct info *, int *, int);
54 static void funky(unsigned long int, struct info *, int *, int);
55 static void circle(unsigned long int, struct info *, int *, int);
56 static void fun(unsigned long int, struct info *, int *, int);
57 static void linear(unsigned long int, struct info *, int *, int);
58 static void lissajous(unsigned long int, struct info *, int *, int);
59 static void test(unsigned long int, struct info *, int *, int);
60 static void innie(unsigned long int, struct info *, int *, int, double);
62 XColor colors[NCOLORS];
63 int ncolors = NCOLORS;
64 char *progclass = "Whirlygig";
79 "*color_modifier: -1",
86 "*doubleBuffer: True",
87 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
90 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
94 XrmOptionDescRec options [] = {
95 { "-xspeed", ".xspeed", XrmoptionSepArg, 0 },
96 /* xspeed is a modifier of the argument to cos -- changing it thus
97 changes the frequency of cos */
98 { "-yspeed", ".yspeed", XrmoptionSepArg, 0 },
99 /* Similiarly, yspeed changes the frequency of sin */
100 { "-xamplitude", ".xamplitude", XrmoptionSepArg, 0 },
101 /* A factor by which to increase/decrease the amplitude of the sin */
102 { "-yamplitude", ".yamplitude", XrmoptionSepArg, 0 },
103 /* A factor by which to increase/decrease the amplitude of the cos */
104 { "-whirlies", ".whirlies",XrmoptionSepArg, 0 },
105 /* whirlies defines the number of circles to draw per line */
106 { "-nlines", ".nlines",XrmoptionSepArg, 0 },
107 /* nlines is the number of lines of whirlies to draw */
108 { "-xmode", ".xmode", XrmoptionSepArg, 0 },
109 /* There are a few different modes that I have written -- each mode
110 is in theory a different experiment with the possible modifiers to sin/cos */
111 { "-ymode", ".ymode", XrmoptionSepArg, 0 },
112 { "-speed", ".speed", XrmoptionSepArg, 0 },
113 /* This will modify how often it should draw, changing it will probably suck */
114 { "-trail", ".trail", XrmoptionSepArg, 0 },
115 /* Control whether or not you want the old circles to be erased */
116 { "-color_modifier", ".color_modifier", XrmoptionSepArg, 0 },
117 /* How many colors away from the current should the next whirly be? */
118 { "-start_time", ".start_time", XrmoptionSepArg, 0 },
119 /* Specify exactly at what time to start graphing... */
120 { "-xoffset", ".xoffset", XrmoptionSepArg, 0 },
121 /* Tell the whirlies to be offset by this factor of a sin */
122 { "-yoffset", ".yoffset", XrmoptionSepArg, 0 },
123 /* Tell the whirlies to be offset by this factor of a cos */
124 { "-offset_period", ".offset_period", XrmoptionSepArg, 0 },
125 /* Change the period of an offset cycle */
126 { "-explain", ".explain", XrmoptionNoArg, "True" },
127 /* Specify whether or not to print an explanation of the function used. */
128 { "-wrap", ".wrap", XrmoptionSepArg, 0 },
129 /* Specify if you want whirlies which are out of the boundary of the screen to be
130 wrapped around to the other side */
131 { "-db", ".doubleBuffer", XrmoptionNoArg, "True" },
132 { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" },
136 static const char spin_explanation[] =
137 "Spin mode is a simple sin/cos with every argument modified";
139 static const char funky_explanation[] =
140 "Funky mode is me goofing off.";
142 static const char circle_explanation[] =
143 "Circle mode graphs the x and y positions as you trace the edge of a circle over time.";
145 static const char linear_explanation[] =
146 "Linear mode draws a straight line";
148 static const char test_explanation[] =
149 "Test mode is a mode that I play around with ideas in.";
151 static const char fun_explanation[] =
152 "Fun mode is the coolest.";
154 static const char innie_explanation[] =
155 "Innie mode does something or other. Looks cool, though.";
157 static const char lissajous_explanation[] =
158 "Lissajous mode draws a slightly modified lissajous curve";
161 explain(int xmode, int ymode, struct info *info, Display *display, Window window, GC fgc)
163 XClearWindow(display, window);
164 draw_explain_string(xmode, info->half_height-100, display, window, fgc);
165 /* draw_explain_string(ymode, info->half_height-50, display, window, fgc);*/
166 XSync(display, False);
168 XClearWindow(display, window);
172 draw_explain_string(int mode, int offset, Display *display, Window window, GC fgc)
176 XDrawString(display, window, fgc, 50, offset, spin_explanation,
177 strlen(spin_explanation));
180 XDrawString(display, window, fgc, 50, offset, funky_explanation,
181 strlen(funky_explanation));
184 XDrawString(display, window, fgc, 50, offset, circle_explanation,
185 strlen(circle_explanation));
188 XDrawString(display, window, fgc, 50, offset, linear_explanation,
189 strlen(linear_explanation));
192 XDrawString(display, window, fgc, 50, offset, test_explanation,
193 strlen(test_explanation));
196 XDrawString(display, window, fgc, 50, offset, fun_explanation,
197 strlen(fun_explanation));
200 XDrawString(display, window, fgc, 50, offset, innie_explanation,
201 strlen(innie_explanation));
204 XDrawString(display, window, fgc, 50, offset, lissajous_explanation,
205 strlen(linear_explanation));
210 funky(unsigned long int the_time, struct info *info, int pos[], int index)
212 double new_time = ((the_time % 360 ) / 180.0) * M_PI;
214 double time_modifier = cos(new_time / 180.0);
215 double the_cos = cos((new_time * (double)info->xspeed) + (time_modifier * 80.0));
216 double dist_mod_x = cos(new_time) * (info->half_width - 50);
217 pos[index]= info->xamplitude * (the_cos * dist_mod_x) + info->half_width;
220 double new_time = ((the_time % 360 ) / 180.0) * M_PI;
221 double time_modifier = sin(new_time / 180.0);
222 double the_sin = sin((new_time * (double)info->yspeed) + (time_modifier * 80.0));
223 double dist_mod_y = sin(new_time) * (info->half_height - 50);
224 pos[index] = info->yamplitude * (the_sin * dist_mod_y) + info->half_height;
229 innie(unsigned long int the_time, struct info *info, int pos[], int index, double modifier)
231 double frequency = 2000000.0 + (modifier * cos(((double)the_time / 100.0)));
232 double arg = (double)the_time / frequency;
233 double amplitude = 200.0 * cos(arg);
234 double fun = 150.0 * cos((double)the_time / 2000.0);
235 int vert_mod, horiz_mod;
237 horiz_mod = (int)(fun * cos((double)the_time / 100.0)) + info->half_width;
238 pos[index] = (amplitude * cos((double)the_time / 100.0 * info->xspeed)) + horiz_mod;
241 vert_mod = (int)(fun * sin((double)the_time / 100.0)) + info->half_height;
242 pos[index] = (amplitude * sin((double)the_time / 100.0 * info->yspeed)) + vert_mod;
247 lissajous(unsigned long int the_time, struct info *info, int pos[], int index)
249 /* This is a pretty standard lissajous curve
252 The n and c modifiers are cyclic as well, however... */
254 double time = (double)the_time / 100.0;
255 double fun = 15.0 * cos((double)the_time / 800.0);
256 double weird = cos((time / 1100000.0) / 1000.0);
259 result = info->xamplitude * 200.0 * sin((weird * time) + fun) + info->half_width;
262 result = info->yamplitude * 200.0 * sin(time) + info->half_height;
268 circle(unsigned long int the_time, struct info *info, int pos[], int index)
272 result = info->xamplitude * (cos((double)the_time / 100.0 * info->xspeed) * (info->half_width / 2)) + info->half_width;
275 result = info->yamplitude * (sin((double)the_time / 100.0 * info->yspeed) * (info->half_height / 2)) + info->half_height;
282 mod(unsigned long int the_time, struct info *info, int pos[], int index)
285 int max = info->half_width;
286 if ((the_time % (max * 2)) < max)
287 amplitude = max - ((the_time % (max * 2)) - max);
289 amplitude = the_time % (max * 2);
290 amplitude = amplitude - max;
295 spin(unsigned long int the_time, struct info *info, int pos[], int index)
297 double funky = (((the_time % 360) * 1.0) / 180.0) * M_PI;
299 double the_cos = cos((double)the_time / (180.0 * info->xspeed));
300 double dist_mod_x = cos((double)funky) * (info->half_width - 50);
301 pos[index] = info->xamplitude * (the_cos * dist_mod_x) + info->half_width;
304 double the_sin = sin((double)the_time / (180.0 * info->yspeed));
305 double dist_mod_y = sin((double)funky) * (info->half_height - 50);
306 pos[index] = info->yamplitude * (the_sin * dist_mod_y) + info->half_height;
311 fun(unsigned long int the_time, struct info *info, int pos[], int index)
314 int max = info->half_width;
315 if ((the_time % (max * 2)) < max)
316 amplitude = max - ((the_time % (max * 2)) - max);
318 amplitude = the_time % (max * 2);
319 amplitude = amplitude - max;
321 pos[index] = (amplitude * cos((double)the_time / 100.0 * info->xspeed)) + info->half_width;
324 pos[index] = (amplitude * sin((double)the_time / 100.0 * info->yspeed)) + info->half_height;
329 linear(unsigned long int the_time, struct info *info, int pos[], int index)
331 if (index == 0) /* Calculate for the x axis */
332 pos[index] = ((the_time / 2) % (info->half_width * 2));
334 pos[index] = ((the_time / 2) % (info->half_height * 2));
338 test(unsigned long int the_time, struct info *info, int pos[], int index)
341 pos[index] = info->xamplitude * (cos((double)the_time / 100.0 * info->xspeed) * (info->half_width / 2)) + info->half_width;
344 pos[index] = info->yamplitude * (sin((double)the_time / 100.0 * info->yspeed) * (info->half_height / 2)) + info->half_height;
348 static int preen(int current, int max) {
357 smoothen(int x, int lastx, int y, int lasty, int size, int last_color, XColor *colors, Display *display, Window window, GC bgc, int screen, struct info *info)
359 double xdistance = abs((double)x-(double)lastx);
360 double ydistance = abs((double)y-(double)lasty);
361 double distance = sqrt(((xdistance * xdistance) + (ydistance * ydistance)) );
362 double slope = (((double)y-(double)lasty) / ((double)x-(double)lastx));
363 printf("Starting smoothen with values: %f, %f, %f, %f\n", xdistance, ydistance, distance, slope);
364 if (distance > 2.0) {
365 int newx = (int)((xdistance / distance) * slope);
366 int newy = (int)((ydistance / distance) * slope);
368 XSetForeground(display, bgc, BlackPixel(display, screen));
369 XFillArc(display, window, bgc, lastx, lasty, size, size, START_ARC, END_ARC);
371 XSetForeground(display, bgc, colors[last_color].pixel);
372 XFillArc(display, window, bgc, newx, newy, size, size, START_ARC, END_ARC);
373 XSync(display, False);
374 smoothen(newx, x, newy, y, size, last_color, colors, display, window, bgc, screen, info);
380 screenhack (Display *display, Window window)
382 /* The following are all X related toys */
383 XGCValues gcv; /* The structure to hold the GC data */
384 XWindowAttributes xgwa; /* A structure to hold window data */
385 Pixmap b=0, ba=0; /* double-buffer to reduce flicker */
386 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
387 XdbeBackBuffer backb = 0;
388 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
392 Bool dbuf = get_boolean_resource ("doubleBuffer", "Boolean");
393 Bool dbeclear_p = get_boolean_resource ("useDBEClear", "Boolean");
395 unsigned long int current_time = 0; /* The global int telling the current time */
396 unsigned long int start_time = current_time;
397 struct info *info = (struct info *)malloc(sizeof(struct info)); /* Dont forget to call free() later */
398 char *xmode_str, *ymode_str; /* holds the current mode for x and y computation */
399 /* pos is the current position x,y -- last_x contains one cell of every x coordinate
400 for every position of every whirly in every line up to 100 whirlies in 100 lines
401 -- lasy_y and last_size hold the same information for y and size respectively */
402 int pos[2], last_x[100][100], last_y[100][100], last_size[100][100];
406 double modifier; /* for innie */
407 /* Set up the X toys that I will be using later */
408 screen = DefaultScreen(display);
409 XGetWindowAttributes (display, window, &xgwa);
412 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
414 b = xdbe_get_backbuffer (display, window, XdbeBackground);
416 b = xdbe_get_backbuffer (display, window, XdbeUndefined);
418 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
422 ba = XCreatePixmap (display, window, xgwa.width, xgwa.height,xgwa.depth);
431 gcv.foreground = get_pixel_resource("foreground", "Foreground", display, xgwa.colormap);
432 fgc = XCreateGC (display, b, GCForeground, &gcv);
433 gcv.foreground = get_pixel_resource("background", "Background", display, xgwa.colormap);
434 bgc = XCreateGC (display, b, GCForeground, &gcv);
435 make_uniform_colormap (display, xgwa.visual, xgwa.colormap, colors, &ncolors, True, &info->writable, True);
437 if (ba) XFillRectangle (display, ba, bgc, 0, 0, xgwa.width, xgwa.height);
439 /* info is a structure holding all the random pieces of information I may want to
440 pass to my baby functions -- much of it I may never use, but it is nice to
441 have around just in case I want it to make a funky function funkier */
442 info->writable = get_boolean_resource ("cycle", "Boolean");
443 info->xspeed = get_float_resource("xspeed" , "Float");
444 info->yspeed = get_float_resource("yspeed" , "Float");
445 info->xamplitude = get_float_resource("xamplitude", "Float");
446 info->yamplitude = get_float_resource("yamplitude", "Float");
447 info->offset_period = get_float_resource("offset_period", "Float");
448 info->whirlies = get_integer_resource("whirlies", "Integer");
449 info->nlines = get_integer_resource("nlines", "Integer");
450 info->half_width = xgwa.width / 2;
451 info->half_height = xgwa.height / 2;
452 info->speed = get_integer_resource("speed" , "Integer");
453 info->trail = get_integer_resource("trail", "Integer");
454 info->color_modifier = get_integer_resource("color_modifier", "Integer");
455 info->xoffset = get_float_resource("xoffset", "Float");
456 info->yoffset = get_float_resource("yoffset", "Float");
457 xmode_str = get_string_resource("xmode", "Mode");
458 ymode_str = get_string_resource("ymode", "Mode");
459 wrap = get_integer_resource("wrap", "Integer");
460 modifier = 3000.0 + frand(1500.0);
461 if (! xmode_str) xmode = spin_mode;
462 else if (! strcmp (xmode_str, "spin")) xmode = spin_mode;
463 else if (! strcmp (xmode_str, "funky")) xmode = funky_mode;
464 else if (! strcmp (xmode_str, "circle")) xmode = circle_mode;
465 else if (! strcmp (xmode_str, "linear")) xmode = linear_mode;
466 else if (! strcmp (xmode_str, "test")) xmode = test_mode;
467 else if (! strcmp (xmode_str, "fun")) xmode = fun_mode;
468 else if (! strcmp (xmode_str, "innie")) xmode = innie_mode;
469 else if (! strcmp (xmode_str, "lissajous")) xmode = lissajous_mode;
473 if (! ymode_str) ymode = spin_mode;
474 else if (! strcmp (ymode_str, "spin")) ymode = spin_mode;
475 else if (! strcmp (ymode_str, "funky")) ymode = funky_mode;
476 else if (! strcmp (ymode_str, "circle")) ymode = circle_mode;
477 else if (! strcmp (ymode_str, "linear")) ymode = linear_mode;
478 else if (! strcmp (ymode_str, "test")) ymode = test_mode;
479 else if (! strcmp (ymode_str, "fun")) ymode = fun_mode;
480 else if (! strcmp (ymode_str, "innie")) ymode = innie_mode;
481 else if (! strcmp (ymode_str, "lissajous")) ymode = lissajous_mode;
486 if (get_integer_resource("start_time", "Integer") == -1)
487 current_time = (unsigned long int)(random());
489 current_time = get_integer_resource("start_time", "Integer");
490 if (info->whirlies == -1)
491 info->whirlies = 1 + (random() % 15);
492 if (info->nlines == -1)
493 info->nlines = 1 + (random() % 5);
494 if (info->color_modifier == -1)
495 info->color_modifier = 1 + (random() % 25);
496 if (get_boolean_resource("explain", "Integer"))
497 explain(xmode, ymode, info, display, window, fgc);
498 current_color = 1 + (random() % NCOLORS);
499 /* Now that info is full, lets play! */
502 int wcount; /* wcount is a counter incremented for every whirly take note of
503 internal_time before you mess with it */
504 int change_time = 4000;
505 if (! strcmp (xmode_str, "change") && ! strcmp (ymode_str, "change")) {
506 if ((current_time - start_time) > change_time) {
507 start_time = current_time;
508 xmode = 1 + (random() % 4);
509 ymode = 1 + (random() % 4);
512 else if (! strcmp (xmode_str, "change")) {
513 if ((current_time - start_time) > change_time) {
514 start_time = current_time;
515 xmode = 1 + (random() % 4);
518 else if (! strcmp (ymode_str, "change")) {
519 if ((current_time - start_time) > change_time) {
520 start_time = current_time;
521 ymode = 1 + (random() % 3);
522 printf("Changing ymode to %d\n", ymode);
525 if (++current_color >= NCOLORS)
527 for (wcount = 0; wcount < info->whirlies; wcount++) {
528 int lcount; /* lcount is a counter for every line -- take note of the offsets changing */
529 int internal_time = current_time;
530 int color_offset = (current_color + (info->color_modifier * wcount)) % NCOLORS;
531 if (current_time == 0)
534 /* I want the distance between whirlies to increase more each whirly */
535 internal_time = current_time + (10 * wcount) + (wcount * wcount);
537 /* All these functions expect an int time, the struct info,
538 a pointer to an array of positions, and the index that the
539 the function will fill of the array */
541 spin(internal_time, info, pos, 0);
544 funky(internal_time, info, pos, 0);
547 circle(internal_time, info, pos, 0);
550 linear(internal_time, info, pos, 0);
553 fun(internal_time, info, pos, 0);
556 test(internal_time, info, pos, 0);
559 innie(internal_time, info, pos, 0, modifier);
562 lissajous(internal_time, info, pos, 0);
565 spin(internal_time, info, pos, 0);
567 } /* End of the switch for the x position*/
570 spin(internal_time, info, pos, 1);
573 funky(internal_time, info, pos, 1);
576 circle(internal_time, info, pos, 1);
579 linear(internal_time, info, pos, 1);
582 fun(internal_time, info, pos, 1);
585 test(internal_time, info, pos, 1);
588 innie(internal_time, info, pos, 1, modifier);
591 lissajous(internal_time, info, pos, 1);
594 spin(internal_time, info, pos, 1);
596 } /* End of the switch for the y position*/
597 for (lcount = 0; lcount < info->nlines; lcount++) {
598 double arg = (double)((internal_time * info->offset_period) / 90.0);
599 double line_offset = 20.0 * (double)lcount * sin(arg);
601 size = (int)(15.0 + 5.0 * sin((double)internal_time / 180.0));
602 /* First delete the old circle... */
603 if (!info->trail && ( !dbeclear_p ||
604 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
606 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
608 XSetForeground(display, bgc, BlackPixel(display, screen));
609 XFillArc(display, b, bgc, last_x[wcount][lcount], last_y[wcount][lcount], last_size[wcount][lcount], last_size[wcount][lcount], START_ARC, END_ARC);
611 /* Now, lets draw in the new circle */
612 { /* Starting new scope for local x_pos and y_pos */
615 xpos = preen((int)(info->xoffset*line_offset)+pos[0], info->half_width * 2);
616 ypos = preen((int)(info->yoffset*line_offset)+pos[1], info->half_height * 2);
619 xpos = (int)(info->xoffset*line_offset)+pos[0];
620 ypos = (int)(info->yoffset*line_offset)+pos[1];
622 if (start_time == current_time) {
623 /* smoothen should move from one mode to another prettily... */
625 /* Note: smoothen has not been modified to take the double
626 buffering code into account, and needs to be hacked on
630 smoothen(xpos, last_x[wcount][lcount], ypos, last_y[wcount][lcount], size, color_offset, colors, display, window, bgc, screen, info);
633 last_x[wcount][lcount] = xpos;
634 last_y[wcount][lcount] = ypos;
635 last_size[wcount][lcount] = size;
636 XSetForeground(display, bgc, colors[color_offset].pixel);
637 XFillArc(display, b, bgc, xpos, ypos, size, size, START_ARC, END_ARC);
638 } /* End of my temporary scope for xpos and ypos */
639 } /* End of the for each line in nlines */
640 } /* End of the for each whirly in whirlies */
644 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
647 XdbeSwapInfo info[1];
648 info[0].swap_window = window;
649 info[0].swap_action = (dbeclear_p ? XdbeBackground : XdbeUndefined);
650 XdbeSwapBuffers (display, info, 1);
653 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
656 XCopyArea (display, b, window, bgc, 0, 0,
657 xgwa.width, xgwa.height, 0, 0);
660 XSync(display, False);
661 if (current_time == FULL_CYCLE)
664 current_time = current_time + info->speed;
665 screenhack_handle_events(display);
668 } /* End the while loop! */