/* gmatrix.c */ #include #include #include #include #include #include #include #define FALSE 0 #define TRUE 1 #define ANSI_BLACK 0 #define ANSI_RED 1 #define ANSI_GREEN 2 #define ANSI_YELLOW 3 #define ANSI_BLUE 4 #define ANSI_PURPLE 5 #define ANSI_CYAN 6 #define ANSI_WHITE 7 /* EWWW!!! GLOBALS! */ /* General parameters */ int MIN_CHAR = '!'; int MAX_CHAR = '}'; /* Stream parameters */ int NUM_STREAMS = 16; /* Streams per column */ int MIN_STREAM_GAP = 16; /* Minimum vertical space between streams */ int MAX_STREAM_GAP = 48; /* Maximum vertical space between streams */ int MIN_STREAM_HEIGHT = 15; /* Minimum stream height in characters */ int MAX_STREAM_HEIGHT = 35; /* Maximum stream height in characters */ int MIN_COLOR = ANSI_GREEN; /* Minimum color */ int MAX_COLOR = ANSI_GREEN; /* Maximum color */ int USE_RANDOM_BOLD = TRUE; /* Have random bold characters? */ int USE_RANDOM_HEAD = TRUE; /* Have a random white head character? */ /* Speed */ int DELAY = 20000; /* Length of a tick, in microseconds */ int MIN_PERIOD = 2; /* Fastest sliding rate */ int MAX_PERIOD = 5; /* Slowest sliding rate */ /* Screen size */ int NUM_COLUMNS = 40; /* Onscreen column count */ int COLUMN_GAP = 2; /* Space in characters between columns + 1*/ int SCREEN_TOP = 1; /* Top row of screen */ int SCREEN_BOTTOM = 24; /* Bottom row of screen */ int SCREEN_LEFT = 1; /* Left column of the screen */ int SCREEN_RIGHT = 80; /* Right column of the screen */ /* Returns a random number between min and max */ #define rand_num(min, max) ((int)(min) + (int)((float)((max) - (min) + 1) * \ rand() / (float)(RAND_MAX))) /* Returns the lesser of the two parameters */ #define min(a, b) ((a) < (b) ? (a) : (b)) /* Returns the greater of the two parameters */ #define max(a, b) ((a) > (b) ? (a) : (b)) /* An individual cascade of gibberish */ typedef struct STREAM { int top; int bottom; int color; /* What color is it? */ int head; /* Does it have a white head character? */ int head_char; /* Head character's value */ } STREAM; /* A vertical column in which STREAMs live */ typedef struct COLUMN { int period; /* Ticks per slide */ int time; /* Elapsed ticks since last slide */ STREAM* streams; } COLUMN; COLUMN* columns; /* Returns a random character in range */ int rand_char() { return rand_num(MIN_CHAR, MAX_CHAR); } /* Erase screen */ void ansi_clear() { printf(""); } /* Plot character */ void ansi_plot(int x, int y, int ch, int color, int bold) { if (x < SCREEN_LEFT || x > SCREEN_RIGHT || y < SCREEN_TOP || y > SCREEN_BOTTOM) return; printf("[%i;%iH[%i;3%im%c", y, x, bold, color, ch); } /* Fill all the columns with random starting data */ void initialize_columns() { int x, y, bottom, height; STREAM* s; for (x = 0; x < NUM_COLUMNS; x++) { columns[x].period = rand_num(MIN_PERIOD, MAX_PERIOD); columns[x].time = rand_num(0, columns[x].period - 1); /* Initialize the streams bottom-to-top */ bottom = rand_num(SCREEN_BOTTOM - MAX_STREAM_GAP, SCREEN_BOTTOM + MIN_STREAM_HEIGHT - 1); for (y = 0; y < NUM_STREAMS; y++) { s = &columns[x].streams[y]; height = rand_num(MIN_STREAM_HEIGHT, MAX_STREAM_HEIGHT); s->bottom = bottom; s->top = bottom - height; s->color = rand_num(MIN_COLOR, MAX_COLOR); s->head = rand_num(FALSE, USE_RANDOM_HEAD); s->head_char = rand_char(); /* Go up above this stream for the next one */ bottom -= height + rand_num(MIN_STREAM_GAP, MAX_STREAM_GAP); } } } /* Display all the streams */ void initialize_display() { int x, y, z, bottom, top, color; STREAM* s; ansi_clear(); for (x = 0; x < NUM_COLUMNS; x++) for (y = 0; y < NUM_STREAMS; y++) { s = &columns[x].streams[y]; /* Draw each character individually */ for (z = s->top; z <= s->bottom; z++) { if (z == s->bottom && s->head) /* Draw white head character */ ansi_plot(x * COLUMN_GAP + SCREEN_LEFT, z, s->head_char, ANSI_WHITE, TRUE); else /* Draw ordinary character and maybe make it bold */ ansi_plot(x * COLUMN_GAP + SCREEN_LEFT, z, rand_char(), s->color, rand_num(FALSE, USE_RANDOM_BOLD)); } } /* Flush output */ fflush(stdout); } /* Move a stream to the top of its column */ void reset_stream(COLUMN* column, int stream_id) { int y, top, bottom; STREAM* s = &column->streams[stream_id]; /* Find top of all streams */ top = SCREEN_BOTTOM + 1; for (y = 0; y < NUM_STREAMS; y++) top = min(top, column->streams[y].top); /* Move stream up above previous top */ bottom = s->bottom = top - rand_num(MIN_STREAM_GAP, MAX_STREAM_GAP); s->top = bottom - rand_num(MIN_STREAM_HEIGHT, MAX_STREAM_HEIGHT); s->color = rand_num(MIN_COLOR, MAX_COLOR); s->head = rand_num(FALSE, USE_RANDOM_HEAD); s->head_char = rand_char(); } /* Ye olde main loop */ void main_loop() { static int count = 0; int x, y; STREAM* s; for (x = 0; x < NUM_COLUMNS; x++) /* Increment timer and move streams if necessary */ if (++columns[x].time >= columns[x].period) { columns[x].time = 0; for (y = 0; y < NUM_STREAMS; y++) { s = &columns[x].streams[y]; /* Erase top character */ ansi_plot(x * COLUMN_GAP + SCREEN_LEFT, s->top, ' ', ANSI_BLACK, FALSE); /* If there's a white head, dim it */ if (s->head) { /* Draw character above head */ ansi_plot(x * COLUMN_GAP + SCREEN_LEFT, s->bottom, s->head_char, s->color, rand_num(FALSE, USE_RANDOM_BOLD)); } /* Move down */ s->top++; s->bottom++; s->head_char = rand_char(); /* Move to very top if necessary */ if (s->top > SCREEN_BOTTOM) reset_stream(&columns[x], y); /* Draw bottom character */ if (s->head) /* Draw white head character */ ansi_plot(x * COLUMN_GAP + SCREEN_LEFT, s->bottom, s->head_char, ANSI_WHITE, TRUE); else /* Draw ordinary character and maybe make it bold */ ansi_plot(x * COLUMN_GAP + SCREEN_LEFT, s->bottom, s->head_char, s->color, rand_num(FALSE, USE_RANDOM_BOLD)); } } /* Flush output */ fflush(stdout); } /* Cleanup */ void quit() { int x; for (x = 0; x < NUM_COLUMNS; x++) free(columns[x].streams); free(columns); exit(EXIT_SUCCESS); } /* Ye olde main function */ int main(int argc, char* argv[]) { int x; struct itimerval timer; timer.it_interval.tv_usec = timer.it_value.tv_usec = DELAY; timer.it_interval.tv_sec = timer.it_value.tv_sec = 0; /* Allocate columns and streams */ columns = (COLUMN*)malloc(sizeof(COLUMN) * NUM_COLUMNS); for (x = 0; x < NUM_COLUMNS; x++) columns[x].streams = (STREAM*)malloc(sizeof(STREAM) * NUM_STREAMS); srand(time(NULL)); /* Initialize the randomizer */ initialize_columns(); /* Fill all the columns */ initialize_display(); /* And display them */ signal(SIGALRM, main_loop); /* Every tick, do a loop */ signal(SIGTERM, quit); /* On Ctrl+C, quit */ setitimer(ITIMER_REAL, &timer, NULL); /* Start the ball rolling */ while (TRUE); /* And wait forever */ /* This shouldn't happen, so... */ return EXIT_FAILURE; }