97 if (num_columns <= 0 || num_rows <= 0) {
98 log_error(
"digital_rain_init: invalid dimensions %dx%d", num_columns, num_rows);
102 digital_rain_t *rain = SAFE_CALLOC(1,
sizeof(digital_rain_t), digital_rain_t *);
104 log_error(
"digital_rain_init: failed to allocate context");
108 rain->num_columns = num_columns;
109 rain->num_rows = num_rows;
112 rain->columns = SAFE_CALLOC((
size_t)num_columns,
sizeof(digital_rain_column_t), digital_rain_column_t *);
113 if (!rain->columns) {
114 log_error(
"digital_rain_init: failed to allocate column array");
120 size_t grid_size = (size_t)num_columns * (
size_t)num_rows;
121 rain->previous_brightness = SAFE_CALLOC(grid_size,
sizeof(
float),
float *);
122 if (!rain->previous_brightness) {
123 log_error(
"digital_rain_init: failed to allocate brightness array");
124 SAFE_FREE(rain->columns);
130 for (
int col = 0; col < num_columns; col++) {
131 digital_rain_column_t *column = &rain->columns[col];
132 column->time_offset = random_float((
float)col, 0.0f) * 1000.0f;
133 column->speed_multiplier = random_float((
float)col + 0.1f, 0.0f) * 0.5f + 0.5f;
134 column->phase_offset = random_float((
float)col + 0.2f, 0.0f) * (float)
M_PI * 2.0f;
138 rain->fall_speed = DIGITAL_RAIN_DEFAULT_FALL_SPEED;
139 rain->raindrop_length = DIGITAL_RAIN_DEFAULT_RAINDROP_LENGTH;
140 rain->brightness_decay = DIGITAL_RAIN_DEFAULT_BRIGHTNESS_DECAY;
141 rain->animation_speed = DIGITAL_RAIN_DEFAULT_ANIMATION_SPEED;
142 rain->color_r = DIGITAL_RAIN_DEFAULT_COLOR_R;
143 rain->color_g = DIGITAL_RAIN_DEFAULT_COLOR_G;
144 rain->color_b = DIGITAL_RAIN_DEFAULT_COLOR_B;
145 rain->cursor_brightness = DIGITAL_RAIN_DEFAULT_CURSOR_BRIGHTNESS;
146 rain->rainbow_mode =
false;
147 rain->first_frame =
true;
150 log_info(
"Digital rain initialized: %dx%d grid", num_columns, num_rows);
370 if (!rain || !frame) {
371 log_error(
"digital_rain_apply: NULL parameter");
376 rain->time += delta_time * rain->animation_speed;
377 float sim_time = rain->time;
380 if (rain->rainbow_mode) {
386 size_t input_len = strlen(frame);
387 size_t output_capacity = input_len * 20 + 1024;
388 char *output = SAFE_MALLOC(output_capacity,
char *);
390 log_error(
"digital_rain_apply: failed to allocate output buffer");
394 const char *src = frame;
396 size_t remaining = output_capacity;
401 while (*src && remaining > 100) {
403 if (*src ==
'\033') {
406 const char *after = parse_ansi_color(src, &r, &g, &b, &is_foreground);
410 float brightness = get_rain_brightness(rain, col, row, sim_time);
411 float brightness_below = get_rain_brightness(rain, col, row + 1, sim_time);
412 bool is_cursor = brightness > brightness_below;
415 if (!rain->first_frame && row < rain->num_rows && col < rain->num_columns) {
416 size_t idx = (size_t)row * (
size_t)rain->num_columns + (size_t)col;
417 float prev_brightness = rain->previous_brightness[idx];
418 brightness = prev_brightness + (brightness - prev_brightness) * rain->brightness_decay;
419 rain->previous_brightness[idx] = brightness;
420 }
else if (row < rain->num_rows && col < rain->num_columns) {
421 size_t idx = (size_t)row * (
size_t)rain->num_columns + (size_t)col;
422 rain->previous_brightness[idx] = brightness;
428 generate_modulated_color(ansi_buf,
sizeof(ansi_buf), r, g, b, brightness, is_foreground, is_cursor);
430 if ((
size_t)ansi_len < remaining) {
431 memcpy(dst, ansi_buf, (
size_t)ansi_len);
433 remaining -= (size_t)ansi_len;
440 const char *after_skip = skip_ansi_sequence(src);
441 size_t ansi_len = (size_t)(after_skip - src);
442 if (ansi_len < remaining) {
443 memcpy(dst, src, ansi_len);
445 remaining -= ansi_len;
466 float brightness = get_rain_brightness(rain, col, row, sim_time);
467 float brightness_below = get_rain_brightness(rain, col, row + 1, sim_time);
468 bool is_cursor = brightness > brightness_below;
471 if (!rain->first_frame && row < rain->num_rows && col < rain->num_columns) {
472 size_t idx = (size_t)row * (
size_t)rain->num_columns + (size_t)col;
473 float prev_brightness = rain->previous_brightness[idx];
474 brightness = prev_brightness + (brightness - prev_brightness) * rain->brightness_decay;
475 rain->previous_brightness[idx] = brightness;
476 }
else if (row < rain->num_rows && col < rain->num_columns) {
477 size_t idx = (size_t)row * (
size_t)rain->num_columns + (size_t)col;
478 rain->previous_brightness[idx] = brightness;
483 int ansi_len = generate_modulated_color(ansi_buf,
sizeof(ansi_buf), rain->color_r, rain->color_g, rain->color_b,
484 brightness,
true, is_cursor);
486 if ((
size_t)ansi_len < remaining) {
487 memcpy(dst, ansi_buf, (
size_t)ansi_len);
489 remaining -= (size_t)ansi_len;
494 int utf8_len =
utf8_decode((
const uint8_t *)src, &codepoint);
501 for (
int i = 0; i < utf8_len && *src && remaining > 0; i++) {
517 output[output_capacity - 1] =
'\0';
520 rain->first_frame =
false;