X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fcritical.c;h=f151925cc19fca86327ad141cb7695243ace400a;hp=17abf8f4c398b9b6e95e7b3003bec4a0a77ed120;hb=07faf451b99879183ed7e909e43a0e065be1ee7f;hpb=551b3de3f619c04c2dd1971ee9b3f02e270c28c9 diff --git a/hacks/critical.c b/hacks/critical.c index 17abf8f4..f151925c 100644 --- a/hacks/critical.c +++ b/hacks/critical.c @@ -1,5 +1,5 @@ /* critical -- Self-organizing-criticality display hack for XScreenSaver - * Copyright (C) 1998, 1999 Martin Pool + * Copyright (C) 1998, 1999, 2000 Martin Pool * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without @@ -13,15 +13,18 @@ * * Revision history: * 13 Nov 1998: Initial version, Martin Pool - */ + * 08 Feb 2000: Change to keeping and erasing a trail, + * + * It would be nice to draw curvy shapes rather than just straight + * lines, but X11 doesn't have spline primitives (?) so we'd have to + * do all the work ourselves */ #include "screenhack.h" #include "erase.h" #include #include - -char *progclass = "Critical"; +#include typedef struct { @@ -29,57 +32,78 @@ typedef struct { unsigned short *cells; } CriticalModel; +typedef struct { + int trail; /* length of trail */ + int cell_size; +} CriticalSettings; -CriticalModel * model_allocate (int w, int h); -void model_initialize (CriticalModel *model); -static void model_step (CriticalModel *model, int *top_x, int *top_y); +/* Number of screens that should be drawn before reinitializing the + model, and count of the number of screens done so far. */ -/* Options this module understands. */ -XrmOptionDescRec options[] = { - { "-ncolors", ".ncolors", XrmoptionSepArg, 0 }, - { "-delay", ".delay", XrmoptionSepArg, 0 }, - { "-colorscheme", ".colorscheme", XrmoptionSepArg, 0 }, - { "-restart", ".restart", XrmoptionSepArg, 0 }, - { "-cellsize", ".cellsize", XrmoptionSepArg, 0 }, - { "-batchcount", ".batchcount", XrmoptionSepArg, 0 }, - { 0, 0, 0, 0 } /* end */ -}; +struct state { + Display *dpy; + Window window; + + int n_restart, i_restart; + XWindowAttributes wattr; + CriticalModel *model; + int batchcount; + XPoint *history; /* in cell coords */ + long delay_usecs; + GC fgc, bgc; + XGCValues gcv; + CriticalSettings settings; + int d_n_colors; + XColor *d_colors; + int lines_per_color; + int d_i_color; + int d_pos; + int d_wrapped; + + int d_i_batch; + eraser_state *eraser; -/* Default xrm resources. */ -char *defaults[] = { - ".background: black", - "*colorscheme: smooth", - "*delay: 10000", - "*ncolors: 64", - "*restart: 8", - "*cellsize: 9", - "*batchcount: 1500", - 0 /* end */ }; +static CriticalModel * model_allocate (int w, int h); +static void model_initialize (CriticalModel *); + + +static int +clip (int low, int val, int high) +{ + if (val < low) + return low; + else if (val > high) + return high; + else + return val; +} + + /* Allocate an return a new simulation model datastructure. */ -CriticalModel * +static CriticalModel * model_allocate (int model_w, int model_h) { - CriticalModel *model; + CriticalModel *mm; - model = malloc (sizeof (CriticalModel)); - if (!model) + mm = malloc (sizeof (CriticalModel)); + if (!mm) return 0; - model->width = model_w; - model->height = model_h; + mm->width = model_w; + mm->height = model_h; - model->cells = malloc (sizeof (int) * model_w * model_h); - if (!model->cells) + mm->cells = malloc (sizeof (unsigned short) * model_w * model_h); + if (!mm->cells) return 0; - return model; + return mm; } @@ -102,14 +126,14 @@ model_allocate (int model_w, int model_h) */ -void -model_initialize (CriticalModel *model) +static void +model_initialize (CriticalModel *mm) { int i; - for (i = model->width * model->height; i >= 0; i--) + for (i = mm->width * mm->height - 1; i >= 0; i--) { - model->cells[i] = (unsigned short) random (); + mm->cells[i] = (unsigned short) random (); } } @@ -122,23 +146,24 @@ model_initialize (CriticalModel *model) Neighbours that fall off the edge of the model are simply ignored. */ static void -model_step (CriticalModel *model, int *top_x, int *top_y) +model_step (CriticalModel *mm, XPoint *ptop) { int x, y, i; int dx, dy; - unsigned short top_value; + unsigned short top_value = 0; + int top_x = 0, top_y = 0; /* Find the top cell */ top_value = 0; i = 0; - for (y = 0; y < model->height; y++) - for (x = 0; x < model->width; x++) + for (y = 0; y < mm->height; y++) + for (x = 0; x < mm->width; x++) { - if (model->cells[i] >= top_value) + if (mm->cells[i] >= top_value) { - top_value = model->cells[i]; - *top_x = x; - *top_y = y; + top_value = mm->cells[i]; + top_x = x; + top_y = y; } i++; } @@ -146,36 +171,37 @@ model_step (CriticalModel *model, int *top_x, int *top_y) /* Replace it and its neighbours with new random values */ for (dy = -1; dy <= 1; dy++) { - int y = *top_y + dy; - if (y < 0 || y >= model->height) + int yy = top_y + dy; + if (yy < 0 || yy >= mm->height) continue; for (dx = -1; dx <= 1; dx++) { - int x = *top_x + dx; - if (x < 0 || x >= model->width) + int xx = top_x + dx; + if (xx < 0 || xx >= mm->width) continue; - model->cells[y * model->width + x] = (unsigned short) random(); + mm->cells[yy * mm->width + xx] = (unsigned short) random(); } } + + ptop->x = top_x; + ptop->y = top_y; } /* Construct and return in COLORS and N_COLORS a new set of colors, depending on the resource settings. */ -void -setup_colormap (Display *dpy, XWindowAttributes *wattr, - XColor **colors, - int *n_colors) +static void +setup_colormap (struct state *st, XColor **colors, int *n_colors) { Bool writable; char const * color_scheme; /* Make a colormap */ - *n_colors = get_integer_resource ("ncolors", "Integer"); - if (*n_colors < 2) - *n_colors = 2; + *n_colors = get_integer_resource (st->dpy, "ncolors", "Integer"); + if (*n_colors < 3) + *n_colors = 3; *colors = (XColor *) calloc (sizeof(XColor), *n_colors); if (!*colors) @@ -186,144 +212,240 @@ setup_colormap (Display *dpy, XWindowAttributes *wattr, } writable = False; - color_scheme = get_string_resource ("colorscheme", "ColorScheme"); + color_scheme = get_string_resource (st->dpy, "colorscheme", "ColorScheme"); if (!strcmp (color_scheme, "random")) { - make_random_colormap (dpy, wattr->visual, - wattr->colormap, + make_random_colormap (st->dpy, st->wattr.visual, + st->wattr.colormap, *colors, n_colors, True, True, &writable, True); } else if (!strcmp (color_scheme, "smooth")) { - make_smooth_colormap (dpy, wattr->visual, - wattr->colormap, + make_smooth_colormap (st->dpy, st->wattr.visual, + st->wattr.colormap, *colors, n_colors, True, &writable, True); } else { - make_uniform_colormap (dpy, wattr->visual, - wattr->colormap, + make_uniform_colormap (st->dpy, st->wattr.visual, + st->wattr.colormap, *colors, n_colors, True, &writable, True); } } +/* Free allocated colormap created by setup_colormap. */ +static void +free_colormap (struct state *st, XColor **colors, int n_colors) +{ + free_colors (st->dpy, st->wattr.colormap, *colors, n_colors); + free (*colors); +} + -/* Display a self-organizing criticality screen hack. The program - runs indefinately on the root window. */ -void -screenhack (Display *dpy, Window window) + +/* Draw one step of the hack. Positions are cell coordinates. */ +static void +draw_step (struct state *st, GC gc, int pos) { - GC fgc, bgc; - XGCValues gcv; - XWindowAttributes wattr; - int n_colors; - XColor *colors; - int model_w, model_h; - CriticalModel *model; - int lines_per_color = 10; - int i_color = 0; - int x1, y1, x2, y2; - long delay_usecs; - int cell_size; - int batchcount; + int cell_size = st->settings.cell_size; + int half = cell_size/2; + int old_pos = (pos + st->settings.trail - 1) % st->settings.trail; + + pos = pos % st->settings.trail; - /* Number of screens that should be drawn before reinitializing the - model, and count of the number of screens done so far. */ - int n_restart, i_restart; + XDrawLine (st->dpy, st->window, gc, + st->history[pos].x * cell_size + half, + st->history[pos].y * cell_size + half, + st->history[old_pos].x * cell_size + half, + st->history[old_pos].y * cell_size + half); +} + + + +static void * +critical_init (Display *dpy, Window window) +{ + struct state *st = (struct state *) calloc (1, sizeof(*st)); + int model_w, model_h; + st->dpy = dpy; + st->window = window; /* Find window attributes */ - XGetWindowAttributes (dpy, window, &wattr); + XGetWindowAttributes (st->dpy, st->window, &st->wattr); + + st->batchcount = get_integer_resource (st->dpy, "batchcount", "Integer"); + if (st->batchcount < 5) + st->batchcount = 5; + + st->lines_per_color = 10; + + /* For the moment the model size is just fixed -- making it vary + with the screen size just makes the hack boring on large + screens. */ + model_w = 80; + st->settings.cell_size = st->wattr.width / model_w; + model_h = st->settings.cell_size ? + st->wattr.height / st->settings.cell_size : 0; /* Construct the initial model state. */ - cell_size = get_integer_resource ("cellsize", "Integer"); - if (cell_size < 1) - cell_size = 1; - if (cell_size >= 100) - cell_size = 99; - - batchcount = get_integer_resource ("batchcount", "Integer"); - if (batchcount < 5) - batchcount = 5; - - model_w = wattr.width / cell_size; - model_h = wattr.height / cell_size; + + st->settings.trail = clip(2, get_integer_resource (st->dpy, "trail", "Integer"), 1000); - model = model_allocate (model_w, model_h); - if (!model) + st->history = calloc (st->settings.trail, sizeof (st->history[0])); + if (!st->history) + { + fprintf (stderr, "critical: " + "couldn't allocate trail history of %d cells\n", + st->settings.trail); + abort(); + } + + st->model = model_allocate (model_w, model_h); + if (!st->model) { fprintf (stderr, "critical: error preparing the model\n"); - return; + abort(); } /* make a black gc for the background */ - gcv.foreground = get_pixel_resource ("background", "Background", - dpy, wattr.colormap); - bgc = XCreateGC (dpy, window, GCForeground, &gcv); + st->gcv.foreground = get_pixel_resource (st->dpy, st->wattr.colormap, + "background", "Background"); + st->bgc = XCreateGC (st->dpy, st->window, GCForeground, &st->gcv); - fgc = XCreateGC (dpy, window, 0, &gcv); + st->fgc = XCreateGC (st->dpy, st->window, 0, &st->gcv); - x2 = rand() % model_w; - y2 = rand() % model_h; +#ifdef HAVE_COCOA + jwxyz_XSetAntiAliasing (dpy, st->fgc, False); + jwxyz_XSetAntiAliasing (dpy, st->bgc, False); +#endif - delay_usecs = get_integer_resource ("delay", "Integer"); - n_restart = get_integer_resource ("restart", "Integer"); + st->delay_usecs = get_integer_resource (st->dpy, "delay", "Integer"); + st->n_restart = get_integer_resource (st->dpy, "restart", "Integer"); - /* xscreensaver will kill or stop us when the user does something - * that deserves attention. */ - i_restart = 0; - - while (1) { - int i_batch; + setup_colormap (st, &st->d_colors, &st->d_n_colors); + model_initialize (st->model); + model_step (st->model, &st->history[0]); + st->d_pos = 1; + st->d_wrapped = 0; + st->i_restart = 0; + st->d_i_batch = st->batchcount; + + return st; +} - if (!i_restart) - { - setup_colormap (dpy, &wattr, &colors, &n_colors); - model_initialize (model); - } - - for (i_batch = batchcount; i_batch; i_batch--) - { - /* Set color */ - if ((i_batch % lines_per_color) == 0) - { - i_color = (i_color + 1) % n_colors; - gcv.foreground = colors[i_color].pixel; - XChangeGC (dpy, fgc, GCForeground, &gcv); - } +static unsigned long +critical_draw (Display *dpy, Window window, void *closure) +{ + struct state *st = (struct state *) closure; + + if (st->eraser) { + st->eraser = erase_window (st->dpy, st->window, st->eraser); + return st->delay_usecs; + } + + /* for (d_i_batch = batchcount; d_i_batch; d_i_batch--) */ + { + /* Set color */ + if ((st->d_i_batch % st->lines_per_color) == 0) + { + st->d_i_color = (st->d_i_color + 1) % st->d_n_colors; + st->gcv.foreground = st->d_colors[st->d_i_color].pixel; + XChangeGC (st->dpy, st->fgc, GCForeground, &st->gcv); + } - /* draw a line */ - x1 = x2; - y1 = y2; + assert(st->d_pos >= 0 && st->d_pos < st->settings.trail); + model_step (st->model, &st->history[st->d_pos]); - model_step (model, &x2, &y2); + draw_step (st, st->fgc, st->d_pos); - XDrawLine (dpy, window, fgc, - x1 * cell_size + cell_size/2, - y1 * cell_size + cell_size/2, - x2 * cell_size + cell_size/2, - y2 * cell_size + cell_size/2); + /* we use the history as a ring buffer, but don't start erasing until + we've d_wrapped around once. */ + if (++st->d_pos >= st->settings.trail) + { + st->d_pos -= st->settings.trail; + st->d_wrapped = 1; + } - XSync (dpy, False); - screenhack_handle_events (dpy); + if (st->d_wrapped) + { + draw_step (st, st->bgc, st->d_pos+1); + } - if (delay_usecs) - usleep (delay_usecs); } + + st->d_i_batch--; + if (st->d_i_batch < 0) + st->d_i_batch = st->batchcount; + else + return st->delay_usecs; - i_restart = (i_restart + 1) % n_restart; - erase_full_window (dpy, window); - } + st->i_restart = (st->i_restart + 1) % st->n_restart; + + if (st->i_restart == 0) + { + /* Time to start a new simulation, this one has probably got + to be a bit boring. */ + free_colormap (st, &st->d_colors, st->d_n_colors); + setup_colormap (st, &st->d_colors, &st->d_n_colors); + st->eraser = erase_window (st->dpy, st->window, st->eraser); + model_initialize (st->model); + model_step (st->model, &st->history[0]); + st->d_pos = 1; + st->d_wrapped = 0; + st->d_i_batch = st->batchcount; + } + + return st->delay_usecs; } +static void +critical_reshape (Display *dpy, Window window, void *closure, + unsigned int w, unsigned int h) +{ +} -/* - * Local variables: - * c-indent-mode: gnu - * compile-command "make critical && ./critical" - * End: - */ +static Bool +critical_event (Display *dpy, Window window, void *closure, XEvent *event) +{ + return False; +} + +static void +critical_free (Display *dpy, Window window, void *closure) +{ + struct state *st = (struct state *) closure; + free (st); +} + + +/* Options this module understands. */ +static XrmOptionDescRec critical_options[] = { + { "-ncolors", ".ncolors", XrmoptionSepArg, 0 }, + { "-delay", ".delay", XrmoptionSepArg, 0 }, + { "-colorscheme", ".colorscheme", XrmoptionSepArg, 0 }, + { "-restart", ".restart", XrmoptionSepArg, 0 }, + { "-batchcount", ".batchcount", XrmoptionSepArg, 0 }, + { "-trail", ".trail", XrmoptionSepArg, 0 }, + { 0, 0, 0, 0 } /* end */ +}; + + +/* Default xrm resources. */ +static const char *critical_defaults[] = { + ".background: black", + "*colorscheme: smooth", + "*delay: 10000", + "*ncolors: 64", + "*restart: 8", + "*batchcount: 1500", + "*trail: 50", + 0 /* end */ +}; + + +XSCREENSAVER_MODULE ("Critical", critical)