summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--README.md31
-rw-r--r--example.pngbin167019 -> 522305 bytes
-rw-r--r--src/main.c313
4 files changed, 209 insertions, 139 deletions
diff --git a/Makefile.am b/Makefile.am
index 6e4c431..102e0fc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I m4
-bin_PROGRAMS = matrix
+bin_PROGRAMS = amx
-matrix_SOURCES = src/main.c
+amx_SOURCES = src/main.c
diff --git a/README.md b/README.md
index 994cc08..0976343 100644
--- a/README.md
+++ b/README.md
@@ -2,25 +2,40 @@
![example](example.png)
-> All I see now is blonde, brunette, redhead.
+> Do not try and bend the spoon, that's impossble. Instead, only try to realize the truth. There is no spoon.
- -- _Cypher_
+ -- _Spoon Boy_
-# Overview
+## Overview
-Pure C implementation of the famous [digital rain](https://en.wikipedia.org/wiki/Matrix_digital_rain) effect from _The Matrix_ series. While the implementation was inspired by [fakesteak](https://github.com/domsson/fakesteak), this implementation uses the default colours of the terminal to match the overall theme of the setup. Colour of the first drop can be changed by changing `ANSI_COL_DROP` which is set to white by default.
+Pure C implementation of the famous [digital rain](https://en.wikipedia.org/wiki/Matrix_digital_rain) effect from _The Matrix_ series for Linux inspired by [fakesteak](https://github.com/domsson/fakesteak). While trying to keep the simplicity and lightweightness of fakesteak as much as possible, I have added the following characteristics.
-Glitching effect is implemented using unicode characters, hence the terminal needs to support them or else change the glitch code characters to ascii before compiling.
+ - Simulation pattern is closer to the one seen in the background while Neo and Cypher were talking in the first Matrix movie.
+ - Ghosting effect of monochrome displays.
+ - Truecolor support and simple colour customisation options to match the theme of the setup for ricing.
+ - Unicode support.
-# Building
+## Requirements and Dependencies
-All the code is in a single file (src/main.c) and has no dependencies. So, you can compile it any way you like. If you have autotools installed you can build the project with:
+ - Terminal emulator with support for 24-bit RGB colours and unicode characters.
+
+## Customisation
+
+ - Character set: set `UNICODE_MIN` and `UNICODE_MAX` for the [unicode block](https://en.wikipedia.org/wiki/List_of_Unicode_characters) you like to use (e.g. 0x30A1 and 0x30F6 for Katakana).
+ - Colours: set the RGB values of `COLOR_BG_*`, `COLOR_HD_*` and `COLOR_TL_*` for background, head and the tail characters respectively.
+ - Rain attributes: set `RAIN_RATE` and `RAIN_DENSITY` to change the speed and the density of the rain.
+
+## Building and Running
+
+All the code is in a single file (src/main.c) and has no external dependencies. You can compile it any way you like. If you have autotools installed you can build and run using the following commands.
```
autoreconf -i
cd build/
../configure
make
+./amx
```
-# Contributing
+
+## Contributing
Code is hosted on [sourcehut](https://git.sr.ht/~sadeep/matrix-digital-rain). Please submit your patches there or via email to sadeep@asciimx.com ([PGP key](http://www.asciimx.com/sadeep.asc)).
diff --git a/example.png b/example.png
index fbe8b0c..1dedacf 100644
--- a/example.png
+++ b/example.png
Binary files differ
diff --git a/src/main.c b/src/main.c
index 338cc3a..a7ecb51 100644
--- a/src/main.c
+++ b/src/main.c
@@ -11,89 +11,159 @@
#include <unistd.h>
#include <wchar.h>
-#define ANSI_COL_DROP "\x1b[97m"
+#define UNICODE_MIN 0x0021
+#define UNICODE_MAX 0x007E
+
+#define RAIN_RATE 60
+#define RAIN_DENSITY 0.6
+
+#define COLOR_BG_RED 0
+#define COLOR_BG_GRN 0
+#define COLOR_BG_BLU 0
+#define COLOR_HD_RED 255
+#define COLOR_HD_GRN 255
+#define COLOR_HD_BLU 255
+#define COLOR_TL_RED 40
+#define COLOR_TL_GRN 254
+#define COLOR_TL_BLU 20
+
#define ANSI_CUR_HIDE "\e[?25l"
#define ANSI_CUR_SHOW "\e[?25h"
#define ANSI_CUR_RESET "\x1b[H"
-#define ANSI_SCRN_CLEAR "\x1b[2J"
+#define ANSI_FONT_BOLD "\x1b[1m"
#define ANSI_FONT_RESET "\x1b[0m"
+#define ANSI_SCRN_CLEAR "\x1b[2J"
+
+enum { red, green, blue };
+
+typedef union color_tag {
+ uint32_t rgb;
+ unsigned char color[4];
+} color;
typedef struct matrix_tag {
- int rows;
- int cols;
+ size_t rows;
+ size_t cols;
+ size_t *col;
+ size_t *row;
+ char *shade;
wchar_t *code;
+ color *rgb;
} matrix;
-static int mat_index(const matrix *mat, int row, int col) {
+static size_t mat_idx(const matrix *mat, size_t row, size_t col) {
return mat->cols * row + col;
}
-static int mat_init(matrix *mat, struct winsize *ws) {
- int i, j;
+static void mat_put_code(matrix *mat, size_t row, size_t col) {
+ mat->code[mat_idx(mat, row, col)] =
+ rand() % (UNICODE_MAX - UNICODE_MIN) + UNICODE_MIN;
+}
- static wchar_t code[] = {
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
- 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
- 'a', 'b', 'c', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
- 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
+static void shuffle(size_t *a, size_t n) {
+ size_t i, j;
- static int codelen = sizeof(code) / sizeof(code[0]);
+ for (i = n - 1; i > 0; i--) {
+ j = rand() % (i + 1);
+ a[j] = a[i] ^ a[j];
+ a[i] = a[i] ^ a[j];
+ a[j] = a[i] ^ a[j];
+ }
+}
+
+static int mat_init(matrix *mat, const struct winsize *ws) {
+ size_t i;
- mat->rows = ws->ws_row + 1;
mat->cols = ws->ws_col;
+ mat->rows = ws->ws_row + 1;
+
mat->code = realloc(mat->code, sizeof mat->code[0] * mat->rows * mat->cols);
- if (mat->code) {
- for (i = 0; i < mat->rows; i++) {
- for (j = 0; j < mat->cols; j++)
- mat->code[mat_index(mat, i, j)] = code[rand() % codelen];
- }
- return 1;
+ if (!mat->code)
+ return 0;
+
+ mat->rgb = realloc(mat->rgb, sizeof mat->rgb[0] * mat->rows * mat->cols);
+ if (!mat->rgb) {
+ free(mat->code);
+ return 0;
}
- return 0;
-}
-typedef struct table_tag {
- int len; // number of columns
- int *cols; // column indices
- int *rows; // row indices ((rows[i], cols[i]) points to a cell in the matrix)
- int *attr; // cell attributes
-} table;
+ mat->shade = realloc(mat->shade, sizeof mat->shade[0] * mat->cols);
+ if (!mat->shade) {
+ free(mat->code);
+ free(mat->rgb);
+ return 0;
+ }
-static void shuffle(int *a, int n) {
- int i, j, tmp;
+ mat->col = realloc(mat->col, sizeof mat->col[0] * mat->cols);
+ if (!mat->col) {
+ free(mat->code);
+ free(mat->rgb);
+ free(mat->shade);
+ return 0;
+ }
- for (i = n - 1; i > 0; i--) {
- j = rand() % (i + 1);
- tmp = a[j];
- a[j] = a[i];
- a[i] = tmp;
+ mat->row = realloc(mat->row, sizeof mat->row[0] * mat->cols);
+ if (!mat->row) {
+ free(mat->code);
+ free(mat->rgb);
+ free(mat->col);
+ free(mat->shade);
+ return 0;
+ }
+
+ for (i = 0; i < mat->cols; i++) {
+ mat->row[i] = 0;
+ mat->col[i] = i;
+ mat->shade[i] = 0;
}
+
+ shuffle(mat->col, mat->cols);
+ return 1;
}
-static int tab_init(table *tab, const matrix *mat) {
- size_t i;
+static void mat_reset_head(matrix *mat, size_t row, size_t col) {
+ unsigned char *sc, *tc;
- tab->len = mat->cols;
- tab->cols = realloc(tab->cols, sizeof tab->cols[0] * tab->len);
- tab->rows = realloc(tab->rows, sizeof tab->rows[0] * tab->len);
- tab->attr = realloc(tab->attr, sizeof tab->attr[0] * tab->len);
+ sc = mat->rgb[mat_idx(mat, 0, col)].color;
+ tc = mat->rgb[mat_idx(mat, row, col)].color;
+ tc[red] = sc[red];
+ tc[green] = sc[green];
+ tc[blue] = sc[blue];
+}
- if (tab->cols && tab->rows && tab->attr) {
- for (i = 0; i < mat->cols; i++) {
- tab->rows[i] = 0;
- tab->cols[i] = i;
- tab->attr[i] = 0;
- }
- shuffle(tab->cols, tab->len);
- return 1;
- }
- return 0;
+static void mat_set_tail(matrix *mat, size_t row, size_t col) {
+ unsigned char *color;
+
+ color = mat->rgb[mat_idx(mat, row, col)].color;
+ color[red] = COLOR_TL_RED;
+ color[green] = COLOR_TL_GRN;
+ color[blue] = COLOR_TL_BLU;
+}
+
+static void mat_set_head(matrix *mat, size_t row, size_t col) {
+ unsigned char *color;
+
+ color = mat->rgb[mat_idx(mat, row, col)].color;
+ color[red] = COLOR_HD_RED;
+ color[green] = COLOR_HD_GRN;
+ color[blue] = COLOR_HD_BLU;
}
-static void tab_free(table *tab) {
- free(tab->cols);
- free(tab->rows);
- free(tab->attr);
+static void mat_shade(matrix *mat, size_t row, size_t col) {
+ unsigned char *color;
+
+ color = mat->rgb[mat_idx(mat, row, col)].color;
+ color[red] = color[red] - (color[red] - COLOR_BG_RED) / 2;
+ color[green] = color[green] - (color[green] - COLOR_BG_GRN) / 2;
+ color[blue] = color[blue] - (color[blue] - COLOR_BG_BLU) / 2;
+}
+
+static void mat_free(matrix *mat) {
+ free(mat->code);
+ free(mat->col);
+ free(mat->row);
+ free(mat->rgb);
+ free(mat->shade);
}
static int term_init() {
@@ -102,6 +172,8 @@ static int term_init() {
if (tcgetattr(STDIN_FILENO, &ta) == 0) {
ta.c_lflag &= ~ECHO;
if (tcsetattr(STDIN_FILENO, TCSANOW, &ta) == 0) {
+ wprintf(L"\x1b[48;2;%d;%d;%dm", COLOR_BG_RED, COLOR_BG_GRN, COLOR_BG_BLU);
+ wprintf(L"%s", ANSI_FONT_BOLD);
wprintf(L"%s", ANSI_CUR_HIDE);
wprintf(L"%s", ANSI_CUR_RESET);
wprintf(L"%s", ANSI_SCRN_CLEAR);
@@ -128,45 +200,17 @@ static void term_reset() {
setvbuf(stdout, 0, _IOLBF, 0);
}
-static void term_size(struct winsize *ws) {
+static void term_size(const struct winsize *ws) {
ioctl(STDOUT_FILENO, TIOCGWINSZ, ws);
}
-static void term_clear_cell(int row, int col) {
- wprintf(L"\x1b[%d;%dH ", row, col);
-}
-
-static void term_reset_cell(const matrix *mat, int row, int col) {
- wprintf(L"\x1b[%d;%dH\x1b[39m%lc", row, col,
- mat->code[mat_index(mat, row, col)]);
-}
-
-static void term_print(const matrix *mat, int row, int col) {
- wprintf(L"\x1b[%d;%dH%s%lc", row, col, ANSI_COL_DROP,
- mat->code[mat_index(mat, row, col)]);
-}
+static void term_print(const matrix *mat, size_t row, size_t col) {
+ size_t idx;
-static void term_glitch(int row, int col) {
- static wchar_t code[] = {
- 0x0413, // Г
- 0x0418, // И
- 0x042F, // Я
- 0x2200, // ∀
- 0x04D9, // ә
- 0x0500, // Ԁ
- 0x2203, // ∃
- 0x2132, // Ⅎ
- 0x0245, // Ʌ
- 0x037B, // ͻ
- 0x03FD, // Ͻ
- 0x041F, // П
- 0x04D4, // Ӕ
- 0x0424, // Ф
- };
-
- static int codelen = sizeof(code) / sizeof(code[0]);
-
- wprintf(L"\x1b[%d;%dH\x1b[39m%lc", row, col, code[rand() % codelen]);
+ idx = mat_idx(mat, row, col);
+ wprintf(L"\x1b[%d;%dH\x1b[38;2;%d;%d;%dm%lc", row, col,
+ mat->rgb[idx].color[red], mat->rgb[idx].color[green],
+ mat->rgb[idx].color[blue], mat->code[idx]);
}
static volatile int run;
@@ -183,11 +227,10 @@ static void handle_signal(int sa) {
int main(int argc, char *argv[]) {
matrix mat;
- table tab;
struct winsize ws;
struct timespec ts;
struct sigaction sa;
- int i, cols, blank, delay, tmp;
+ size_t i, j, len, maxlen;
setlocale(LC_CTYPE, "");
@@ -208,54 +251,66 @@ int main(int argc, char *argv[]) {
return 1;
}
- tab = (table){0};
- if (!tab_init(&tab, &mat)) {
- term_reset();
- return 1;
- }
-
- delay = 75;
- ts.tv_sec = delay / 1000;
- ts.tv_nsec = (delay % 1000) * 1000000;
+ ts.tv_sec = RAIN_RATE / 1000;
+ ts.tv_nsec = (RAIN_RATE % 1000) * 1000000;
- run = cols = 1;
- blank = tab.cols[tab.len - 1];
+ run = 1, len = 1;
+ maxlen = mat.cols * RAIN_DENSITY;
while (run) {
- for (i = 0; run && i < cols; i++) {
- if (tab.rows[i] == mat.rows) {
- tab.rows[i] = 0;
- term_reset_cell(&mat, mat.rows - 1, tab.cols[i]);
+ for (i = 0; run && i < len; i++) {
+ if (mat.row[i] == mat.rows) {
+ mat_reset_head(&mat, mat.row[i] - 1, mat.col[i]);
+ term_print(&mat, mat.rows - 1, mat.col[i]);
+ mat.row[i] = 0;
}
- if (tab.attr[i] == 0) {
- // hard-coding to 5 (row[i] - rand() % rows[i] glitches on empty rows)
- if (tab.rows[i] > 5 && rand() % 6 == 0)
- term_glitch(tab.rows[i] - 5, tab.cols[i]);
- if (tab.rows[i] > 0)
- term_reset_cell(&mat, tab.rows[i] - 1, tab.cols[i]);
- term_print(&mat, tab.rows[i], tab.cols[i]);
- if (tab.rows[i] == mat.rows - 1)
- tab.attr[i] = 1;
- tab.rows[i]++;
+ if (mat.shade[i] == 0) {
+ if (mat.row[i] > 0) {
+ mat_set_tail(&mat, mat.row[i] - 1, mat.col[i]);
+ term_print(&mat, mat.row[i] - 1, mat.col[i]);
+ }
+ mat_set_head(&mat, mat.row[i], mat.col[i]);
+ mat_put_code(&mat, mat.row[i], mat.col[i]);
+ term_print(&mat, mat.row[i], mat.col[i]);
+ if (mat.row[i] > 0 && rand() % 6 == 0) {
+ j = rand() % mat.row[i];
+ if (mat.code[mat_idx(&mat, j, mat.col[i])] != ' ') {
+ mat_put_code(&mat, j, mat.col[i]);
+ term_print(&mat, j, mat.col[i]);
+ }
+ }
+ if (mat.row[i] == mat.rows - 1)
+ mat.shade[i] = 1;
+ mat.row[i]++;
+ } else if (mat.shade[i] == 1 || mat.shade[i] == 2) {
+ mat_shade(&mat, mat.row[i], mat.col[i]);
+ term_print(&mat, mat.row[i], mat.col[i]);
+ if (mat.row[i] == mat.rows - 1)
+ mat.shade[i]++;
+ mat.row[i]++;
} else {
- term_clear_cell(tab.rows[i], tab.cols[i]);
- if (tab.rows[i] == mat.rows - 1) {
- tab.rows[i] = 0;
- tab.attr[i] = 0;
- tmp = blank;
- blank = tab.cols[i];
- tab.cols[i] = tmp;
+ mat.code[mat_idx(&mat, mat.row[i], mat.col[i])] = ' ';
+ term_print(&mat, mat.row[i], mat.col[i]);
+ if (mat.row[i] == mat.rows - 1) {
+ mat.row[i] = 0;
+ mat.shade[i] = 0;
+ j = rand() % (mat.cols - maxlen) + maxlen;
+ mat.col[i] = mat.col[i] ^ mat.col[j];
+ mat.col[j] = mat.col[i] ^ mat.col[j];
+ mat.col[i] = mat.col[i] ^ mat.col[j];
} else
- tab.rows[i]++;
+ mat.row[i]++;
}
}
- if (cols < tab.len * 0.6)
- tab.rows[cols++] = 0;
+ if (len < maxlen &&
+ mat.row[len - 1] >= rand() % (int)(mat.rows * 0.25)) {
+ mat.row[len] = 0;
+ mat.shade[len++] = 0;
+ }
fflush(stdout);
nanosleep(&ts, &ts);
}
term_reset();
- tab_free(&tab);
- free(mat.code);
+ mat_free(&mat);
return 0;
}