mirror of
				https://github.com/thunderbrewhq/thunderbrew
				synced 2025-10-31 08:16:03 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			378 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			378 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * This code is public domain. Feel free to use it for any purpose!
 | |
|  */
 | |
| 
 | |
| #define SDL_MAIN_USE_CALLBACKS 1  /* use the callbacks instead of main() */
 | |
| #include <SDL3/SDL.h>
 | |
| #include <SDL3/SDL_main.h>
 | |
| 
 | |
| /* We will use this renderer to draw into this window every frame. */
 | |
| static SDL_Window *window = NULL;
 | |
| static SDL_Renderer *renderer = NULL;
 | |
| static char *text;
 | |
| static const char *end;
 | |
| static const char *progress;
 | |
| static SDL_Time start_time;
 | |
| static SDL_Time end_time;
 | |
| typedef struct {
 | |
|     Uint32 *text;
 | |
|     int length;
 | |
| } Line;
 | |
| int row = 0;
 | |
| int rows = 0;
 | |
| int cols = 0;
 | |
| static Line **lines;
 | |
| static Line monkey_chars;
 | |
| static int monkeys = 100;
 | |
| 
 | |
| /* The highest and lowest scancodes a monkey can hit */
 | |
| #define MIN_MONKEY_SCANCODE SDL_SCANCODE_A
 | |
| #define MAX_MONKEY_SCANCODE SDL_SCANCODE_SLASH
 | |
| 
 | |
| static const char *default_text =
 | |
| "Jabberwocky, by Lewis Carroll\n"
 | |
| "\n"
 | |
| "'Twas brillig, and the slithy toves\n"
 | |
| "      Did gyre and gimble in the wabe:\n"
 | |
| "All mimsy were the borogoves,\n"
 | |
| "      And the mome raths outgrabe.\n"
 | |
| "\n"
 | |
| "\"Beware the Jabberwock, my son!\n"
 | |
| "      The jaws that bite, the claws that catch!\n"
 | |
| "Beware the Jubjub bird, and shun\n"
 | |
| "      The frumious Bandersnatch!\"\n"
 | |
| "\n"
 | |
| "He took his vorpal sword in hand;\n"
 | |
| "      Long time the manxome foe he sought-\n"
 | |
| "So rested he by the Tumtum tree\n"
 | |
| "      And stood awhile in thought.\n"
 | |
| "\n"
 | |
| "And, as in uffish thought he stood,\n"
 | |
| "      The Jabberwock, with eyes of flame,\n"
 | |
| "Came whiffling through the tulgey wood,\n"
 | |
| "      And burbled as it came!\n"
 | |
| "\n"
 | |
| "One, two! One, two! And through and through\n"
 | |
| "      The vorpal blade went snicker-snack!\n"
 | |
| "He left it dead, and with its head\n"
 | |
| "      He went galumphing back.\n"
 | |
| "\n"
 | |
| "\"And hast thou slain the Jabberwock?\n"
 | |
| "      Come to my arms, my beamish boy!\n"
 | |
| "O frabjous day! Callooh! Callay!\"\n"
 | |
| "      He chortled in his joy.\n"
 | |
| "\n"
 | |
| "'Twas brillig, and the slithy toves\n"
 | |
| "      Did gyre and gimble in the wabe:\n"
 | |
| "All mimsy were the borogoves,\n"
 | |
| "      And the mome raths outgrabe.\n";
 | |
| 
 | |
| 
 | |
| static void FreeLines(void)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     if (rows > 0 && cols > 0) {
 | |
|         for (i = 0; i < rows; ++i) {
 | |
|             SDL_free(lines[i]->text);
 | |
|             SDL_free(lines[i]);
 | |
|         }
 | |
|         SDL_free(lines);
 | |
|         lines = NULL;
 | |
|     }
 | |
|     SDL_free(monkey_chars.text);
 | |
|     monkey_chars.text = NULL;
 | |
| }
 | |
| 
 | |
| static void OnWindowSizeChanged(void)
 | |
| {
 | |
|     int w, h;
 | |
| 
 | |
|     if (!SDL_GetCurrentRenderOutputSize(renderer, &w, &h)) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     FreeLines();
 | |
| 
 | |
|     row = 0;
 | |
|     rows = (h / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) - 4;
 | |
|     cols = (w / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
 | |
|     if (rows > 0 && cols > 0) {
 | |
|         int i;
 | |
| 
 | |
|         lines = (Line **)SDL_malloc(rows * sizeof(Line *));
 | |
|         if (lines) {
 | |
|             for (i = 0; i < rows; ++i) {
 | |
|                 lines[i] = (Line *)SDL_malloc(sizeof(Line));
 | |
|                 if (!lines[i]) {
 | |
|                     FreeLines();
 | |
|                     break;
 | |
|                 }
 | |
|                 lines[i]->text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32));
 | |
|                 if (!lines[i]->text) {
 | |
|                     FreeLines();
 | |
|                     break;
 | |
|                 }
 | |
|                 lines[i]->length = 0;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         monkey_chars.text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32));
 | |
|         if (monkey_chars.text) {
 | |
|             for (i = 0; i < cols; ++i) {
 | |
|                 monkey_chars.text[i] = ' ';
 | |
|             }
 | |
|             monkey_chars.length = cols;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* This function runs once at startup. */
 | |
| SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
 | |
| {
 | |
|     int arg = 1;
 | |
| 
 | |
|     SDL_SetAppMetadata("Infinite Monkeys", "1.0", "com.example.infinite-monkeys");
 | |
| 
 | |
|     if (!SDL_Init(SDL_INIT_VIDEO)) {
 | |
|         SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
 | |
|         return SDL_APP_FAILURE;
 | |
|     }
 | |
| 
 | |
|     if (!SDL_CreateWindowAndRenderer("examples/demo/infinite-monkeys", 640, 480, 0, &window, &renderer)) {
 | |
|         SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
 | |
|         return SDL_APP_FAILURE;
 | |
|     }
 | |
|     SDL_SetRenderVSync(renderer, 1);
 | |
| 
 | |
|     if (argv[arg] && SDL_strcmp(argv[arg], "--monkeys") == 0) {
 | |
|         ++arg;
 | |
|         if (argv[arg]) {
 | |
|             monkeys = SDL_atoi(argv[arg]);
 | |
|             ++arg;
 | |
|         } else {
 | |
|             SDL_Log("Usage: %s [--monkeys N] [file.txt]", argv[0]);
 | |
|             return SDL_APP_FAILURE;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (argv[arg]) {
 | |
|         const char *file = argv[arg];
 | |
|         size_t size;
 | |
|         text = (char *)SDL_LoadFile(file, &size);
 | |
|         if (!text) {
 | |
|             SDL_Log("Couldn't open %s: %s", file, SDL_GetError());
 | |
|             return SDL_APP_FAILURE;
 | |
|         }
 | |
|         end = text + size;
 | |
|     } else {
 | |
|         text = SDL_strdup(default_text);
 | |
|         end = text + SDL_strlen(text);
 | |
|     }
 | |
|     progress = text;
 | |
| 
 | |
|     SDL_GetCurrentTime(&start_time);
 | |
| 
 | |
|     OnWindowSizeChanged();
 | |
| 
 | |
|     return SDL_APP_CONTINUE;  /* carry on with the program! */
 | |
| }
 | |
| 
 | |
| /* This function runs when a new event (mouse input, keypresses, etc) occurs. */
 | |
| SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
 | |
| {
 | |
|     switch (event->type) {
 | |
|     case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
 | |
|         OnWindowSizeChanged();
 | |
|         break;
 | |
|     case SDL_EVENT_QUIT:
 | |
|         return SDL_APP_SUCCESS;  /* end the program, reporting success to the OS. */
 | |
|     }
 | |
|     return SDL_APP_CONTINUE;  /* carry on with the program! */
 | |
| }
 | |
| 
 | |
| static void DisplayLine(float x, float y, Line *line)
 | |
| {
 | |
|     /* Allocate maximum space potentially needed for this line */
 | |
|     char *utf8 = (char *)SDL_malloc(line->length * 4 + 1);
 | |
|     if (utf8) {
 | |
|         char *spot = utf8;
 | |
|         int i;
 | |
| 
 | |
|         for (i = 0; i < line->length; ++i) {
 | |
|             spot = SDL_UCS4ToUTF8(line->text[i], spot);
 | |
|         }
 | |
|         *spot = '\0';
 | |
| 
 | |
|         SDL_RenderDebugText(renderer, x, y, utf8);
 | |
|         SDL_free(utf8);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static bool CanMonkeyType(Uint32 ch)
 | |
| {
 | |
|     SDL_Keymod modstate;
 | |
|     SDL_Scancode scancode = SDL_GetScancodeFromKey(ch, &modstate);
 | |
|     if (scancode < MIN_MONKEY_SCANCODE || scancode > MAX_MONKEY_SCANCODE) {
 | |
|         return false;
 | |
|     }
 | |
|     /* Monkeys can hit the shift key, but nothing else */
 | |
|     if ((modstate & ~SDL_KMOD_SHIFT) != 0) {
 | |
|         return false;
 | |
|     }
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| static void AdvanceRow(void)
 | |
| {
 | |
|     Line *line;
 | |
| 
 | |
|     ++row;
 | |
|     line = lines[row % rows];
 | |
|     line->length = 0;
 | |
| }
 | |
| 
 | |
| static void AddMonkeyChar(int monkey, Uint32 ch)
 | |
| {
 | |
|     if (monkey >= 0 && monkey_chars.text) {
 | |
|         monkey_chars.text[(monkey % cols)] = ch;
 | |
|     }
 | |
| 
 | |
|     if (lines) {
 | |
|         if (ch == '\n') {
 | |
|             AdvanceRow();
 | |
|         } else {
 | |
|             Line *line = lines[row % rows];
 | |
|             line->text[line->length++] = ch;
 | |
|             if (line->length == cols) {
 | |
|                 AdvanceRow();
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     SDL_StepUTF8(&progress, NULL);
 | |
| }
 | |
| 
 | |
| static Uint32 GetNextChar(void)
 | |
| {
 | |
|     Uint32 ch = 0;
 | |
|     while (progress < end) {
 | |
|         const char *spot = progress;
 | |
|         ch = SDL_StepUTF8(&spot, NULL);
 | |
|         if (CanMonkeyType(ch)) {
 | |
|             break;
 | |
|         } else {
 | |
|             /* This is a freebie, monkeys can't type this */
 | |
|             AddMonkeyChar(-1, ch);
 | |
|         }
 | |
|     }
 | |
|     return ch;
 | |
| }
 | |
| 
 | |
| static Uint32 MonkeyPlay(void)
 | |
| {
 | |
|     int count = (MAX_MONKEY_SCANCODE - MIN_MONKEY_SCANCODE + 1);
 | |
|     SDL_Scancode scancode = (SDL_Scancode)(MIN_MONKEY_SCANCODE + SDL_rand(count));
 | |
|     SDL_Keymod modstate = (SDL_rand(2) ? SDL_KMOD_SHIFT : 0);
 | |
| 
 | |
|     return SDL_GetKeyFromScancode(scancode, modstate, false);
 | |
| }
 | |
| 
 | |
| /* This function runs once per frame, and is the heart of the program. */
 | |
| SDL_AppResult SDL_AppIterate(void *appstate)
 | |
| {
 | |
|     int i, monkey;
 | |
|     Uint32 next_char = 0, ch;
 | |
|     float x, y;
 | |
|     char *caption = NULL;
 | |
|     SDL_Time now, elapsed;
 | |
|     int hours, minutes, seconds;
 | |
|     SDL_FRect rect;
 | |
| 
 | |
|     for (monkey = 0; monkey < monkeys; ++monkey) {
 | |
|         if (next_char == 0) {
 | |
|             next_char = GetNextChar();
 | |
|             if (!next_char) {
 | |
|                 /* All done! */
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         ch = MonkeyPlay();
 | |
|         if (ch == next_char) {
 | |
|             AddMonkeyChar(monkey, ch);
 | |
|             next_char = 0;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Clear the screen */
 | |
|     SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
 | |
|     SDL_RenderClear(renderer);
 | |
| 
 | |
|     /* Show the text already decoded */
 | |
|     SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
 | |
|     x = 0.0f;
 | |
|     y = 0.0f;
 | |
|     if (lines) {
 | |
|         int row_offset = row - rows + 1;
 | |
|         if (row_offset < 0) {
 | |
|             row_offset = 0;
 | |
|         }
 | |
|         for (i = 0; i < rows; ++i) {
 | |
|             Line *line = lines[(row_offset + i) % rows];
 | |
|             DisplayLine(x, y, line);
 | |
|             y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
 | |
|         }
 | |
| 
 | |
|         /* Show the caption */
 | |
|         y = (float)((rows + 1) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
 | |
|         if (progress == end) {
 | |
|             if (!end_time) {
 | |
|                 SDL_GetCurrentTime(&end_time);
 | |
|             }
 | |
|             now = end_time;
 | |
|         } else {
 | |
|             SDL_GetCurrentTime(&now);
 | |
|         }
 | |
|         elapsed = (now - start_time);
 | |
|         elapsed /= SDL_NS_PER_SECOND;
 | |
|         seconds = (int)(elapsed % 60);
 | |
|         elapsed /= 60;
 | |
|         minutes = (int)(elapsed % 60);
 | |
|         elapsed /= 60;
 | |
|         hours = (int)elapsed;
 | |
|         SDL_asprintf(&caption, "Monkeys: %d - %dH:%dM:%dS", monkeys, hours, minutes, seconds);
 | |
|         if (caption) {
 | |
|             SDL_RenderDebugText(renderer, x, y, caption);
 | |
|             SDL_free(caption);
 | |
|         }
 | |
|         y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
 | |
| 
 | |
|         /* Show the characters currently typed */
 | |
|         DisplayLine(x, y, &monkey_chars);
 | |
|         y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
 | |
|     }
 | |
| 
 | |
|     /* Show the current progress */
 | |
|     SDL_SetRenderDrawColor(renderer, 0, 255, 0, SDL_ALPHA_OPAQUE);
 | |
|     rect.x = x;
 | |
|     rect.y = y;
 | |
|     rect.w = ((float)(progress - text) / (end - text)) * (cols * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
 | |
|     rect.h = (float)SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
 | |
|     SDL_RenderFillRect(renderer, &rect);
 | |
| 
 | |
|     SDL_RenderPresent(renderer);
 | |
| 
 | |
|     return SDL_APP_CONTINUE;  /* carry on with the program! */
 | |
| }
 | |
| 
 | |
| /* This function runs once at shutdown. */
 | |
| void SDL_AppQuit(void *appstate, SDL_AppResult result)
 | |
| {
 | |
|     /* SDL will clean up the window/renderer for us. */
 | |
| 
 | |
|     FreeLines();
 | |
|     SDL_free(text);
 | |
| }
 | |
| 
 | 
