|
73 | 73 | * > lum must not exceed 1.0f */ |
74 | 74 | #define MENU_SS_PARTICLE_COLOR(tint_r, tint_g, tint_b, lum) (((uint32_t)(tint_r * lum) << 24) | ((uint32_t)(tint_g * lum) << 16) | ((uint32_t)(tint_b * lum) << 8) | 0xFF) |
75 | 75 |
|
| 76 | +/* Specifies the precise number of active rendering particles for the Matrix effect. |
| 77 | + * Kept under the system cap of 256 to ensure complete memory safety and isolate |
| 78 | + * this high-density effect from other default screen savers like Snow. */ |
| 79 | +#define MENU_SS_MATRIX_DENSE_COUNT 230 |
| 80 | + |
76 | 81 | /* Definition of screensaver 'particle': |
77 | 82 | * - symbol: string representation of a font glyph |
78 | 83 | * - x: centre x-coordinate of draw position |
@@ -160,6 +165,29 @@ static const char * const menu_ss_vortex_symbols[] = { |
160 | 165 | "\xE2\x97\x86" /* Black Diamond, U+25C6 */ |
161 | 166 | }; |
162 | 167 |
|
| 168 | +/* Matrix character symbols mapped exclusively to Unicode Private Use Area (PUA) |
| 169 | + * U+E000 to U+E00F. This ensures 100% isolation from stock ASCII character font buffers. */ |
| 170 | +#define MENU_SS_NUM_MATRIX_SYMBOLS 17 |
| 171 | +static const char * const menu_ss_matrix_symbols[] = { |
| 172 | + "\xEE\x80\x80", /* PUA 0x00 (Custom glyph for '0') */ |
| 173 | + "\xEE\x80\x81", /* PUA 0x01 (Custom glyph for '1') */ |
| 174 | + "\xEE\x80\x82", /* PUA 0x02 (Custom glyph for '2') */ |
| 175 | + "\xEE\x80\x83", /* PUA 0x03 (Custom glyph for '3') */ |
| 176 | + "\xEE\x80\x84", /* PUA 0x04 (Custom glyph for '4') */ |
| 177 | + "\xEE\x80\x85", /* PUA 0x05 (Custom glyph for '5') */ |
| 178 | + "\xEE\x80\x86", /* PUA 0x06 (Custom glyph for '6') */ |
| 179 | + "\xEE\x80\x87", /* PUA 0x07 (Custom glyph for '7') */ |
| 180 | + "\xEE\x80\x88", /* PUA 0x08 (Custom glyph for '8') */ |
| 181 | + "\xEE\x80\x89", /* PUA 0x09 (Custom glyph for '9') */ |
| 182 | + "\xEE\x80\x8A", /* PUA 0x0A (Custom glyph for 'ウ') */ |
| 183 | + "\xEE\x80\x8B", /* PUA 0x0B (Custom glyph for 'カ') */ |
| 184 | + "\xEE\x80\x8C", /* PUA 0x0C (Custom glyph for 'メ') */ |
| 185 | + "\xEE\x80\x8D", /* PUA 0x0D (Custom glyph for 'モ') */ |
| 186 | + "\xEE\x80\x8E", /* PUA 0x0E (Custom glyph for 'ワ') */ |
| 187 | + "\xEE\x80\x8F", /* PUA 0x0F (Custom glyph for 'レ') */ |
| 188 | + "\xEE\x80\x90" /* PUA 0x10 (Custom glyph for '*') */ |
| 189 | +}; |
| 190 | + |
163 | 191 | /***********************/ |
164 | 192 | /* Pseudo-random numbers */ |
165 | 193 | /***********************/ |
@@ -195,6 +223,16 @@ static INLINE uint32_t menu_ss_rand(void) |
195 | 223 | return x; |
196 | 224 | } |
197 | 225 |
|
| 226 | +/* Forward declarations for Matrix screen saver functions to avoid implicit declaration errors */ |
| 227 | +static void menu_screensaver_init_matrix( |
| 228 | + menu_screensaver_t *screensaver, |
| 229 | + unsigned width, unsigned height); |
| 230 | + |
| 231 | +static void menu_screensaver_animate_matrix( |
| 232 | + menu_screensaver_t *screensaver, |
| 233 | + float base_particle_size, float global_speed_factor, |
| 234 | + unsigned width, unsigned height); |
| 235 | + |
198 | 236 | /******************/ |
199 | 237 | /* Initialisation */ |
200 | 238 | /******************/ |
@@ -432,6 +470,9 @@ static bool menu_screensaver_init_effect(menu_screensaver_t *screensaver) |
432 | 470 | } |
433 | 471 | } |
434 | 472 | break; |
| 473 | + case MENU_SCREENSAVER_MATRIX: |
| 474 | + menu_screensaver_init_matrix(screensaver, width, height); |
| 475 | + break; |
435 | 476 | default: |
436 | 477 | /* Error condition - do nothing */ |
437 | 478 | return false; |
@@ -773,12 +814,94 @@ void menu_screensaver_iterate( |
773 | 814 | (int (*)(const void *, const void *))menu_ss_vortex_qsort_func); |
774 | 815 | } |
775 | 816 | break; |
| 817 | + case MENU_SCREENSAVER_MATRIX: |
| 818 | + menu_screensaver_animate_matrix(screensaver, base_particle_size, global_speed_factor, width, height); |
| 819 | + break; |
776 | 820 | default: |
777 | 821 | /* Error condition - do nothing */ |
778 | 822 | break; |
779 | 823 | } |
780 | 824 | } |
781 | 825 |
|
| 826 | +/* --- Matrix Digital Rain Screen Saver --- */ |
| 827 | + |
| 828 | +/* Logic for updating and rendering the matrix rain effect */ |
| 829 | +static void menu_screensaver_animate_matrix( |
| 830 | + menu_screensaver_t *screensaver, |
| 831 | + float base_particle_size, float global_speed_factor, |
| 832 | + unsigned width, unsigned height) |
| 833 | +{ |
| 834 | + size_t i; |
| 835 | + |
| 836 | + for (i = 0; i < MENU_SS_MATRIX_DENSE_COUNT; i++) |
| 837 | + { |
| 838 | + menu_ss_particle_t *particle = &screensaver->particles[i]; |
| 839 | + float particle_size_px = particle->size * base_particle_size; |
| 840 | + |
| 841 | + /* 1. Move downwards based on randomized particle speed (stored in 'a') */ |
| 842 | + particle->y += global_speed_factor * particle->size * particle->a; |
| 843 | + |
| 844 | + /* 2. Periodically change characters to mimic glitchy matrix rain (stored in 'b') */ |
| 845 | + particle->b += global_speed_factor; |
| 846 | + if (particle->b > 9.5f) |
| 847 | + { |
| 848 | + particle->b = 0.0f; |
| 849 | + particle->symbol = menu_ss_matrix_symbols[(unsigned)(rand() % MENU_SS_NUM_MATRIX_SYMBOLS)]; |
| 850 | + } |
| 851 | + |
| 852 | + /* 3. Smoothly fade out the trails over time to create the iconic matrix fade effect */ |
| 853 | + particle->c -= 0.00395f * global_speed_factor; |
| 854 | + if (particle->c < 0.05f) |
| 855 | + particle->c = 0.05f; |
| 856 | + |
| 857 | + /* 4. Determine brightness/luminosity (stored in 'c' combined with scale size) */ |
| 858 | + float luminosity = particle->c * (0.4f + (particle->size / 3.0f)); |
| 859 | + if (luminosity > 1.0f) |
| 860 | + luminosity = 1.0f; |
| 861 | + |
| 862 | + /* Pure Matrix green (No Red, Full Green, No Blue) */ |
| 863 | + particle->color = MENU_SS_PARTICLE_COLOR(0, 255, 0, luminosity); |
| 864 | + |
| 865 | + /* 5. Reset particle if it falls off the bottom screen boundary */ |
| 866 | + if (particle->y > (float)height + particle_size_px) |
| 867 | + { |
| 868 | + particle->x = (float)(rand() % width); |
| 869 | + particle->y = -particle_size_px - (float)(rand() % 100); |
| 870 | + particle->a = 0.6f + ((float)(rand() % 20) * 0.1f); /* downward velocity */ |
| 871 | + particle->b = 0.0f; /* glitch timer */ |
| 872 | + particle->c = 1.0f; /* reset tail fade opacity to full */ |
| 873 | + particle->size = 2.0f + ((float)(rand() % 150) / 100.0f); /* randomized font size scale */ |
| 874 | + particle->symbol = menu_ss_matrix_symbols[(unsigned)(rand() % MENU_SS_NUM_MATRIX_SYMBOLS)]; |
| 875 | + } |
| 876 | + } |
| 877 | +} |
| 878 | + |
| 879 | +/* Initialization logic for the matrix rain effect */ |
| 880 | +static void menu_screensaver_init_matrix( |
| 881 | + menu_screensaver_t *screensaver, |
| 882 | + unsigned width, unsigned height) |
| 883 | +{ |
| 884 | + size_t i; |
| 885 | + float screen_size = (float)((width < height) ? width : height); |
| 886 | + float font_size = ((screen_size * MENU_SS_FONT_SIZE_FACTOR) + 0.5f) * 0.5f; |
| 887 | + float particle_scale = (screen_size * MENU_SS_PARTICLE_SIZE_FACTOR) / font_size; |
| 888 | + float base_particle_size = particle_scale * font_size; |
| 889 | + |
| 890 | + for (i = 0; i < MENU_SS_MATRIX_DENSE_COUNT; i++) |
| 891 | + { |
| 892 | + menu_ss_particle_t *particle = &screensaver->particles[i]; |
| 893 | + |
| 894 | + particle->x = (float)(rand() % width); |
| 895 | + /* Stagger initial vertical positions to allow trail structures to form naturally */ |
| 896 | + particle->y = (float)(rand() % height) - (base_particle_size * 4.0f); |
| 897 | + particle->a = 0.6f + ((float)(rand() % 20) * 0.1f); /* speed */ |
| 898 | + particle->b = 0.0f; /* character swap timer */ |
| 899 | + particle->c = (float)(rand() % 100) / 100.0f; /* randomized initial fade */ |
| 900 | + particle->size = 2.0f + ((float)(rand() % 150) / 100.0f); /* scale */ |
| 901 | + particle->symbol = menu_ss_matrix_symbols[(unsigned)(rand() % MENU_SS_NUM_MATRIX_SYMBOLS)]; |
| 902 | + } |
| 903 | +} |
| 904 | + |
782 | 905 | /* Draws screensaver |
783 | 906 | * Called every frame (on the video thread, |
784 | 907 | * if threaded video is on) */ |
@@ -829,7 +952,11 @@ void menu_screensaver_frame(menu_screensaver_t *screensaver, |
829 | 952 | screensaver->font_data.raster_block.carr.coords.vertices = 0; |
830 | 953 |
|
831 | 954 | /* Render text */ |
832 | | - for (i = 0; i < MENU_SS_NUM_PARTICLES; i++) |
| 955 | + size_t max_particles = MENU_SS_NUM_PARTICLES; |
| 956 | + if (screensaver->effect == MENU_SCREENSAVER_MATRIX) |
| 957 | + max_particles = MENU_SS_MATRIX_DENSE_COUNT; |
| 958 | + |
| 959 | + for (i = 0; i < max_particles; i++) |
833 | 960 | { |
834 | 961 | menu_ss_particle_t *particle = &screensaver->particles[i]; |
835 | 962 |
|
|
0 commit comments