1
/* twang, twist around screen bits, v1.3
2
* by Dan Bornstein, danfuzz@milk.com
3
* Copyright (c) 2003 Dan Bornstein. All rights reserved.
5
* Permission to use, copy, modify, distribute, and sell this software and its
6
* documentation for any purpose is hereby granted without fee, provided that
7
* the above copyright notice appear in all copies and that both that
8
* copyright notice and this permission notice appear in supporting
9
* documentation. No representations are made about the suitability of this
10
* software for any purpose. It is provided "as is" without express or
13
* See the included man page for more details.
17
#include "screenhack.h"
18
#include <X11/Xutil.h>
20
#ifdef HAVE_XSHM_EXTENSION
26
/* random float in the range (-1..1) */
27
#define RAND_FLOAT_PM1 \
28
(((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000) * 2 - 1)
30
/* random float in the range (0..1) */
31
#define RAND_FLOAT_01 \
32
(((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000))
36
/* parameters that are user configurable */
38
/* whether or not to use xshm */
39
#ifdef HAVE_XSHM_EXTENSION
43
/* delay (usec) between iterations */
46
/* the maximum number of columns of tiles */
47
static int maxColumns;
49
/* the maximum number of rows of tiles */
52
/* the size (width and height) of a tile */
55
/* the width of the border around each tile */
56
static int borderWidth;
58
/* the chance, per iteration, of an interesting event happening */
59
static FLOAT eventChance;
61
/* friction: the fraction (0..1) by which velocity decreased per iteration */
62
static FLOAT friction;
64
/* springiness: the fraction (0..1) of the orientation that turns into
65
* velocity towards the center */
66
static FLOAT springiness;
68
/* transference: the fraction (0..1) of the orientations of orthogonal
69
* neighbors that turns into velocity (in the same direction as the
71
static FLOAT transference;
75
/* non-user-modifiable immutable definitions */
77
/* width and height of the window */
78
static int windowWidth;
79
static int windowHeight;
81
static Display *display; /* the display to draw on */
82
static Window window; /* the window to draw on */
83
static Screen *screen; /* the screen to draw on */
85
static XImage *sourceImage; /* image source of stuff to draw */
86
static XImage *workImage; /* work area image, used when rendering */
87
static XImage *backgroundImage; /* image filled with background pixels */
89
static GC backgroundGC; /* GC for the background color */
90
static GC foregroundGC; /* GC for the foreground color */
91
static GC borderGC; /* GC for the border color */
92
unsigned long backgroundPixel; /* background color as a pixel value */
93
unsigned long borderPixel; /* border color as a pixel value */
95
#ifdef HAVE_XSHM_EXTENSION
96
XShmSegmentInfo shmInfo;
105
int x; /* x coordinate of the center of the tile */
106
int y; /* y coordinate of the center of the tile */
107
FLOAT angle; /* angle of the tile (-pi..pi) */
108
FLOAT zoom; /* log of the zoom of the tile (-1..1) */
109
FLOAT vAngle; /* angular velocity (-pi/4..pi/4) */
110
FLOAT vZoom; /* zoomular velocity (-0.25..0.25) */
114
static Tile *tiles; /* array of tiles (left->right, top->bottom, row major) */
115
static int rows; /* number of rows of tiles */
116
static int columns; /* number of columns of tiles */
118
static Tile **sortedTiles; /* array of tile pointers, sorted by zoom */
119
static int tileCount; /* total number of tiles */
121
#define TILE_AT(col,row) (&tiles[(row) * columns + (col)])
123
#define MAX_VANGLE (M_PI / 4.0)
124
#define MAX_VZOOM 0.25
126
#define RAND_ANGLE (RAND_FLOAT_PM1 * M_PI)
127
#define RAND_ZOOM (RAND_FLOAT_PM1)
128
#define RAND_VANGLE (RAND_FLOAT_PM1 * MAX_VANGLE)
129
#define RAND_VZOOM (RAND_FLOAT_PM1 * MAX_VZOOM)
134
* overall setup stuff
137
/* grab the source image */
138
static void grabImage (XWindowAttributes *xwa)
140
XFillRectangle (display, window, backgroundGC, 0, 0,
141
windowWidth, windowHeight);
143
XGetImage (display, window, 0, 0, windowWidth, windowHeight,
146
load_random_image (screen, window, window, NULL, NULL);
147
sourceImage = XGetImage (display, window, 0, 0, windowWidth, windowHeight,
150
#ifdef HAVE_XSHM_EXTENSION
154
workImage = create_xshm_image (display, xwa->visual, xwa->depth,
155
ZPixmap, 0, &shmInfo,
156
windowWidth, windowHeight);
160
fprintf (stderr, "create_xshm_image failed\n");
164
if (workImage == NULL)
165
#endif /* HAVE_XSHM_EXTENSION */
167
/* just use XSubImage to acquire the right visual, depth, etc;
168
* easier than the other alternatives */
169
workImage = XSubImage (sourceImage, 0, 0, windowWidth, windowHeight);
172
/* set up the system */
173
static void setup (void)
175
XWindowAttributes xgwa;
178
XGetWindowAttributes (display, window, &xgwa);
180
screen = xgwa.screen;
181
windowWidth = xgwa.width;
182
windowHeight = xgwa.height;
184
gcv.line_width = borderWidth;
185
gcv.foreground = get_pixel_resource ("borderColor", "BorderColor",
186
display, xgwa.colormap);
187
borderPixel = gcv.foreground;
188
borderGC = XCreateGC (display, window, GCForeground | GCLineWidth,
191
gcv.foreground = get_pixel_resource ("background", "Background",
192
display, xgwa.colormap);
193
backgroundPixel = gcv.foreground;
194
backgroundGC = XCreateGC (display, window, GCForeground, &gcv);
196
gcv.foreground = get_pixel_resource ("foreground", "Foreground",
197
display, xgwa.colormap);
198
foregroundGC = XCreateGC (display, window, GCForeground, &gcv);
209
/* event: randomize all the angular velocities */
210
static void randomizeAllAngularVelocities (void)
215
for (r = 0; r < rows; r++)
217
for (c = 0; c < columns; c++)
219
TILE_AT (c, r)->vAngle = RAND_VANGLE;
224
/* event: randomize all the zoomular velocities */
225
static void randomizeAllZoomularVelocities (void)
230
for (r = 0; r < rows; r++)
232
for (c = 0; c < columns; c++)
234
TILE_AT (c, r)->vZoom = RAND_VZOOM;
239
/* event: randomize all the velocities */
240
static void randomizeAllVelocities (void)
242
randomizeAllAngularVelocities ();
243
randomizeAllZoomularVelocities ();
246
/* event: randomize all the angular orientations */
247
static void randomizeAllAngularOrientations (void)
252
for (r = 0; r < rows; r++)
254
for (c = 0; c < columns; c++)
256
TILE_AT (c, r)->angle = RAND_ANGLE;
261
/* event: randomize all the zoomular orientations */
262
static void randomizeAllZoomularOrientations (void)
267
for (r = 0; r < rows; r++)
269
for (c = 0; c < columns; c++)
271
TILE_AT (c, r)->zoom = RAND_ZOOM;
276
/* event: randomize all the orientations */
277
static void randomizeAllOrientations (void)
279
randomizeAllAngularOrientations ();
280
randomizeAllZoomularOrientations ();
283
/* event: randomize everything */
284
static void randomizeEverything (void)
286
randomizeAllVelocities ();
287
randomizeAllOrientations ();
290
/* event: pick one tile and randomize all its stats */
291
static void randomizeOneTile (void)
293
int c = RAND_FLOAT_01 * columns;
294
int r = RAND_FLOAT_01 * rows;
296
Tile *t = TILE_AT (c, r);
297
t->angle = RAND_ANGLE;
299
t->vAngle = RAND_VANGLE;
300
t->vZoom = RAND_VZOOM;
303
/* event: pick one row and randomize everything about each of its tiles */
304
static void randomizeOneRow (void)
307
int r = RAND_FLOAT_01 * rows;
309
for (c = 0; c < columns; c++)
311
Tile *t = TILE_AT (c, r);
312
t->angle = RAND_ANGLE;
314
t->vAngle = RAND_VANGLE;
315
t->vZoom = RAND_VZOOM;
319
/* event: pick one column and randomize everything about each of its tiles */
320
static void randomizeOneColumn (void)
322
int c = RAND_FLOAT_01 * columns;
325
for (r = 0; r < rows; r++)
327
Tile *t = TILE_AT (c, r);
328
t->angle = RAND_ANGLE;
330
t->vAngle = RAND_VANGLE;
331
t->vZoom = RAND_VZOOM;
335
/* do model event processing */
336
static void modelEvents (void)
340
if (RAND_FLOAT_01 > eventChance)
345
which = RAND_FLOAT_01 * 10;
349
case 0: randomizeAllAngularVelocities (); break;
350
case 1: randomizeAllZoomularVelocities (); break;
351
case 2: randomizeAllVelocities (); break;
352
case 3: randomizeAllAngularOrientations (); break;
353
case 4: randomizeAllZoomularOrientations (); break;
354
case 5: randomizeAllOrientations (); break;
355
case 6: randomizeEverything (); break;
356
case 7: randomizeOneTile (); break;
357
case 8: randomizeOneColumn (); break;
358
case 9: randomizeOneRow (); break;
362
/* update the model for one iteration */
363
static void updateModel (void)
368
/* for each tile, decrease its velocities according to the friction,
369
* and increase them based on its current orientation and the orientations
370
* of its orthogonal neighbors */
371
for (r = 0; r < rows; r++)
373
for (c = 0; c < columns; c++)
375
Tile *t = TILE_AT (c, r);
378
FLOAT va = t->vAngle;
381
va -= t->angle * springiness;
382
vz -= t->zoom * springiness;
386
Tile *t2 = TILE_AT (c - 1, r);
387
va += (t2->angle - a) * transference;
388
vz += (t2->zoom - z) * transference;
391
if (c < (columns - 1))
393
Tile *t2 = TILE_AT (c + 1, r);
394
va += (t2->angle - a) * transference;
395
vz += (t2->zoom - z) * transference;
400
Tile *t2 = TILE_AT (c, r - 1);
401
va += (t2->angle - a) * transference;
402
vz += (t2->zoom - z) * transference;
407
Tile *t2 = TILE_AT (c, r + 1);
408
va += (t2->angle - a) * transference;
409
vz += (t2->zoom - z) * transference;
412
va *= (1.0 - friction);
413
vz *= (1.0 - friction);
415
if (va > MAX_VANGLE) va = MAX_VANGLE;
416
else if (va < -MAX_VANGLE) va = -MAX_VANGLE;
419
if (vz > MAX_VZOOM) vz = MAX_VZOOM;
420
else if (vz < -MAX_VZOOM) vz = -MAX_VZOOM;
425
/* for each tile, update its orientation based on its velocities */
426
for (r = 0; r < rows; r++)
428
for (c = 0; c < columns; c++)
430
Tile *t = TILE_AT (c, r);
431
FLOAT a = t->angle + t->vAngle;
432
FLOAT z = t->zoom + t->vZoom;
434
if (a > M_PI) a = M_PI;
435
else if (a < -M_PI) a = -M_PI;
438
if (z > 1.0) z = 1.0;
439
else if (z < -1.0) z = -1.0;
445
/* the comparator to us to sort the tiles (used immediately below); it'd
446
* sure be nice if C allowed inner functions (or jeebus-forbid *real
448
static int sortTilesComparator (const void *v1, const void *v2)
450
Tile *t1 = *(Tile **) v1;
451
Tile *t2 = *(Tile **) v2;
453
if (t1->zoom < t2->zoom)
458
if (t1->zoom > t2->zoom)
466
/* sort the tiles in sortedTiles by zoom */
467
static void sortTiles (void)
469
qsort (sortedTiles, tileCount, sizeof (Tile *), sortTilesComparator);
472
/* render the given tile */
473
static void renderTile (Tile *t)
475
/* note: the zoom as stored per tile is log-based (centered on 0, with
476
* 0 being no zoom, but the range for zoom-as-drawn is 0.4..2.5,
477
* hence the alteration of t->zoom, below */
484
FLOAT zoom = pow (2.5, t->zoom);
485
FLOAT ang = -t->angle;
486
FLOAT sinAng = sin (ang);
487
FLOAT cosAng = cos (ang);
489
FLOAT innerBorder = (tileSize - borderWidth) / 2.0;
490
FLOAT outerBorder = innerBorder + borderWidth;
492
int maxCoord = outerBorder * zoom * (fabs (sinAng) + fabs (cosAng));
493
int minX = tx - maxCoord;
494
int maxX = tx + maxCoord;
495
int minY = ty - maxCoord;
496
int maxY = ty + maxCoord;
500
if (minX < 0) minX = 0;
501
if (maxX > windowWidth) maxX = windowWidth;
502
if (minY < 0) minY = 0;
503
if (maxY > windowHeight) maxY = windowHeight;
508
for (y = minY, prey = y - ty; y < maxY; y++, prey++)
510
FLOAT prex = minX - tx;
511
FLOAT srcx = prex * cosAng - prey * sinAng;
512
FLOAT srcy = prex * sinAng + prey * cosAng;
516
x++, srcx += cosAng, srcy += sinAng)
518
if ((srcx < -innerBorder) || (srcx >= innerBorder) ||
519
(srcy < -innerBorder) || (srcy >= innerBorder))
521
if ((srcx < -outerBorder) || (srcx >= outerBorder) ||
522
(srcy < -outerBorder) || (srcy >= outerBorder))
526
XPutPixel (workImage, x, y, borderPixel);
531
XGetPixel (sourceImage, srcx + tx, srcy + ty);
532
XPutPixel (workImage, x, y, p);
538
/* render and display the current model */
539
static void renderFrame (void)
543
memcpy (workImage->data, backgroundImage->data,
544
workImage->bytes_per_line * workImage->height);
548
for (n = 0; n < tileCount; n++)
550
renderTile (sortedTiles[n]);
553
#ifdef HAVE_XSHM_EXTENSION
555
XShmPutImage (display, window, backgroundGC, workImage, 0, 0, 0, 0,
556
windowWidth, windowHeight, False);
558
#endif /* HAVE_XSHM_EXTENSION */
559
XPutImage (display, window, backgroundGC, workImage,
560
0, 0, 0, 0, windowWidth, windowHeight);
563
/* set up the model */
564
static void setupModel (void)
569
int leftX; /* x of the center of the top-left tile */
570
int topY; /* y of the center of the top-left tile */
572
if (tileSize > (windowWidth / 2))
574
tileSize = windowWidth / 2;
577
if (tileSize > (windowHeight / 2))
579
tileSize = windowHeight / 2;
582
columns = windowWidth / tileSize;
583
rows = windowHeight / tileSize;
585
if ((maxColumns != 0) && (columns > maxColumns))
587
columns = maxColumns;
590
if ((maxRows != 0) && (rows > maxRows))
595
tileCount = rows * columns;
597
leftX = (windowWidth - (columns * tileSize) + tileSize) / 2;
598
topY = (windowHeight - (rows * tileSize) + tileSize) / 2;
600
tiles = calloc (tileCount, sizeof (Tile));
601
sortedTiles = calloc (tileCount, sizeof (Tile *));
603
for (r = 0; r < rows; r++)
605
for (c = 0; c < columns; c++)
607
Tile *t = TILE_AT (c, r);
608
t->x = leftX + c * tileSize;
609
t->y = topY + r * tileSize;
610
sortedTiles[c + r * columns] = t;
614
randomizeEverything ();
617
/* do one iteration */
618
static void oneIteration (void)
627
/* main and options and stuff */
629
char *progclass = "Twang";
631
char *defaults [] = {
632
".background: black",
633
".foreground: white",
634
"*borderColor: blue",
637
"*eventChance: 0.01",
643
"*transference: 0.025",
644
#ifdef HAVE_XSHM_EXTENSION
650
XrmOptionDescRec options [] = {
651
{ "-border-color", ".borderColor", XrmoptionSepArg, 0 },
652
{ "-border-width", ".borderWidth", XrmoptionSepArg, 0 },
653
{ "-delay", ".delay", XrmoptionSepArg, 0 },
654
{ "-event-chance", ".eventChance", XrmoptionSepArg, 0 },
655
{ "-friction", ".friction", XrmoptionSepArg, 0 },
656
{ "-max-columns", ".maxColumns", XrmoptionSepArg, 0 },
657
{ "-max-rows", ".maxRows", XrmoptionSepArg, 0 },
658
{ "-springiness", ".springiness", XrmoptionSepArg, 0 },
659
{ "-tile-size", ".tileSize", XrmoptionSepArg, 0 },
660
{ "-transference", ".transference", XrmoptionSepArg, 0 },
661
#ifdef HAVE_XSHM_EXTENSION
662
{ "-shm", ".useSHM", XrmoptionNoArg, "True" },
663
{ "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
668
/* initialize the user-specifiable params */
669
static void initParams (void)
673
borderWidth = get_integer_resource ("borderWidth", "Integer");
676
fprintf (stderr, "error: border width must be at least 0\n");
680
delay = get_integer_resource ("delay", "Delay");
683
fprintf (stderr, "error: delay must be at least 0\n");
687
eventChance = get_float_resource ("eventChance", "Double");
688
if ((eventChance < 0.0) || (eventChance > 1.0))
690
fprintf (stderr, "error: eventChance must be in the range 0..1\n");
694
friction = get_float_resource ("friction", "Double");
695
if ((friction < 0.0) || (friction > 1.0))
697
fprintf (stderr, "error: friction must be in the range 0..1\n");
701
maxColumns = get_integer_resource ("maxColumns", "Integer");
704
fprintf (stderr, "error: max columns must be at least 0\n");
708
maxRows = get_integer_resource ("maxRows", "Integer");
711
fprintf (stderr, "error: max rows must be at least 0\n");
715
springiness = get_float_resource ("springiness", "Double");
716
if ((springiness < 0.0) || (springiness > 1.0))
718
fprintf (stderr, "error: springiness must be in the range 0..1\n");
722
tileSize = get_integer_resource ("tileSize", "Integer");
725
fprintf (stderr, "error: tile size must be at least 1\n");
729
transference = get_float_resource ("transference", "Double");
730
if ((transference < 0.0) || (transference > 1.0))
732
fprintf (stderr, "error: transference must be in the range 0..1\n");
736
#ifdef HAVE_XSHM_EXTENSION
737
useShm = get_boolean_resource ("useSHM", "Boolean");
747
void screenhack (Display *dpy, Window win)
760
screenhack_handle_events (dpy);