7373 * > lum must not exceed 1.0f */
7474#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)
7575
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+
7681/* Definition of screensaver 'particle':
7782 * - symbol: string representation of a font glyph
7883 * - x: centre x-coordinate of draw position
@@ -160,6 +165,29 @@ static const char * const menu_ss_vortex_symbols[] = {
160165 "\xE2\x97\x86" /* Black Diamond, U+25C6 */
161166};
162167
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+
163191/***********************/
164192/* Pseudo-random numbers */
165193/***********************/
@@ -195,6 +223,16 @@ static INLINE uint32_t menu_ss_rand(void)
195223 return x ;
196224}
197225
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+
198236/******************/
199237/* Initialisation */
200238/******************/
@@ -432,6 +470,9 @@ static bool menu_screensaver_init_effect(menu_screensaver_t *screensaver)
432470 }
433471 }
434472 break ;
473+ case MENU_SCREENSAVER_MATRIX :
474+ menu_screensaver_init_matrix (screensaver , width , height );
475+ break ;
435476 default :
436477 /* Error condition - do nothing */
437478 return false;
@@ -773,12 +814,95 @@ void menu_screensaver_iterate(
773814 (int (* )(const void * , const void * ))menu_ss_vortex_qsort_func );
774815 }
775816 break ;
817+ case MENU_SCREENSAVER_MATRIX :
818+ menu_screensaver_animate_matrix (screensaver , base_particle_size , global_speed_factor , width , height );
819+ break ;
776820 default :
777821 /* Error condition - do nothing */
778822 break ;
779823 }
780824}
781825
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+ float luminosity ;
836+
837+ for (i = 0 ; i < MENU_SS_MATRIX_DENSE_COUNT ; i ++ )
838+ {
839+ menu_ss_particle_t * particle = & screensaver -> particles [i ];
840+ float particle_size_px = particle -> size * base_particle_size ;
841+
842+ /* 1. Move downwards based on randomized particle speed (stored in 'a') */
843+ particle -> y += global_speed_factor * particle -> size * particle -> a ;
844+
845+ /* 2. Periodically change characters to mimic glitchy matrix rain (stored in 'b') */
846+ particle -> b += global_speed_factor ;
847+ if (particle -> b > 9.5f )
848+ {
849+ particle -> b = 0.0f ;
850+ particle -> symbol = menu_ss_matrix_symbols [(unsigned )(rand () % MENU_SS_NUM_MATRIX_SYMBOLS )];
851+ }
852+
853+ /* 3. Smoothly fade out the trails over time to create the iconic matrix fade effect */
854+ particle -> c -= 0.00395f * global_speed_factor ;
855+ if (particle -> c < 0.05f )
856+ particle -> c = 0.05f ;
857+
858+ /* 4. Determine brightness/luminosity (stored in 'c' combined with scale size) */
859+ luminosity = particle -> c * (0.4f + (particle -> size / 3.0f ));
860+ if (luminosity > 1.0f )
861+ luminosity = 1.0f ;
862+
863+ /* Pure Matrix green (No Red, Full Green, No Blue) */
864+ particle -> color = MENU_SS_PARTICLE_COLOR (0 , 255 , 0 , luminosity );
865+
866+ /* 5. Reset particle if it falls off the bottom screen boundary */
867+ if (particle -> y > (float )height + particle_size_px )
868+ {
869+ particle -> x = (float )(rand () % width );
870+ particle -> y = - particle_size_px - (float )(rand () % 100 );
871+ particle -> a = 0.6f + ((float )(rand () % 20 ) * 0.1f ); /* downward velocity */
872+ particle -> b = 0.0f ; /* glitch timer */
873+ particle -> c = 1.0f ; /* reset tail fade opacity to full */
874+ particle -> size = 2.0f + ((float )(rand () % 150 ) / 100.0f ); /* randomized font size scale */
875+ particle -> symbol = menu_ss_matrix_symbols [(unsigned )(rand () % MENU_SS_NUM_MATRIX_SYMBOLS )];
876+ }
877+ }
878+ }
879+
880+ /* Initialization logic for the matrix rain effect */
881+ static void menu_screensaver_init_matrix (
882+ menu_screensaver_t * screensaver ,
883+ unsigned width , unsigned height )
884+ {
885+ size_t i ;
886+ float screen_size = (float )((width < height ) ? width : height );
887+ float font_size = ((screen_size * MENU_SS_FONT_SIZE_FACTOR ) + 0.5f ) * 0.5f ;
888+ float particle_scale = (screen_size * MENU_SS_PARTICLE_SIZE_FACTOR ) / font_size ;
889+ float base_particle_size = particle_scale * font_size ;
890+
891+ for (i = 0 ; i < MENU_SS_MATRIX_DENSE_COUNT ; i ++ )
892+ {
893+ menu_ss_particle_t * particle = & screensaver -> particles [i ];
894+
895+ particle -> x = (float )(rand () % width );
896+ /* Stagger initial vertical positions to allow trail structures to form naturally */
897+ particle -> y = (float )(rand () % height ) - (base_particle_size * 4.0f );
898+ particle -> a = 0.6f + ((float )(rand () % 20 ) * 0.1f ); /* speed */
899+ particle -> b = 0.0f ; /* character swap timer */
900+ particle -> c = (float )(rand () % 100 ) / 100.0f ; /* randomized initial fade */
901+ particle -> size = 2.0f + ((float )(rand () % 150 ) / 100.0f ); /* scale */
902+ particle -> symbol = menu_ss_matrix_symbols [(unsigned )(rand () % MENU_SS_NUM_MATRIX_SYMBOLS )];
903+ }
904+ }
905+
782906/* Draws screensaver
783907 * Called every frame (on the video thread,
784908 * if threaded video is on) */
@@ -821,6 +945,7 @@ void menu_screensaver_frame(menu_screensaver_t *screensaver,
821945 && screensaver -> particles )
822946 {
823947 size_t i ;
948+ size_t max_particles ;
824949 float y_centre_offset = screensaver -> font_data .y_centre_offset ;
825950 float particle_scale = screensaver -> particle_scale ;
826951
@@ -829,7 +954,11 @@ void menu_screensaver_frame(menu_screensaver_t *screensaver,
829954 screensaver -> font_data .raster_block .carr .coords .vertices = 0 ;
830955
831956 /* Render text */
832- for (i = 0 ; i < MENU_SS_NUM_PARTICLES ; i ++ )
957+ max_particles = MENU_SS_NUM_PARTICLES ;
958+ if (screensaver -> effect == MENU_SCREENSAVER_MATRIX )
959+ max_particles = MENU_SS_MATRIX_DENSE_COUNT ;
960+
961+ for (i = 0 ; i < max_particles ; i ++ )
833962 {
834963 menu_ss_particle_t * particle = & screensaver -> particles [i ];
835964
0 commit comments