infinite-monkeys.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /*
  2. * This code is public domain. Feel free to use it for any purpose!
  3. */
  4. #define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
  5. #include <SDL3/SDL.h>
  6. #include <SDL3/SDL_main.h>
  7. /* We will use this renderer to draw into this window every frame. */
  8. static SDL_Window *window = NULL;
  9. static SDL_Renderer *renderer = NULL;
  10. static char *text;
  11. static const char *end;
  12. static const char *progress;
  13. static SDL_Time start_time;
  14. static SDL_Time end_time;
  15. typedef struct {
  16. Uint32 *text;
  17. int length;
  18. } Line;
  19. int row = 0;
  20. int rows = 0;
  21. int cols = 0;
  22. static Line **lines;
  23. static Line monkey_chars;
  24. static int monkeys = 100;
  25. /* The highest and lowest scancodes a monkey can hit */
  26. #define MIN_MONKEY_SCANCODE SDL_SCANCODE_A
  27. #define MAX_MONKEY_SCANCODE SDL_SCANCODE_SLASH
  28. static const char *default_text =
  29. "Jabberwocky, by Lewis Carroll\n"
  30. "\n"
  31. "'Twas brillig, and the slithy toves\n"
  32. " Did gyre and gimble in the wabe:\n"
  33. "All mimsy were the borogoves,\n"
  34. " And the mome raths outgrabe.\n"
  35. "\n"
  36. "\"Beware the Jabberwock, my son!\n"
  37. " The jaws that bite, the claws that catch!\n"
  38. "Beware the Jubjub bird, and shun\n"
  39. " The frumious Bandersnatch!\"\n"
  40. "\n"
  41. "He took his vorpal sword in hand;\n"
  42. " Long time the manxome foe he sought-\n"
  43. "So rested he by the Tumtum tree\n"
  44. " And stood awhile in thought.\n"
  45. "\n"
  46. "And, as in uffish thought he stood,\n"
  47. " The Jabberwock, with eyes of flame,\n"
  48. "Came whiffling through the tulgey wood,\n"
  49. " And burbled as it came!\n"
  50. "\n"
  51. "One, two! One, two! And through and through\n"
  52. " The vorpal blade went snicker-snack!\n"
  53. "He left it dead, and with its head\n"
  54. " He went galumphing back.\n"
  55. "\n"
  56. "\"And hast thou slain the Jabberwock?\n"
  57. " Come to my arms, my beamish boy!\n"
  58. "O frabjous day! Callooh! Callay!\"\n"
  59. " He chortled in his joy.\n"
  60. "\n"
  61. "'Twas brillig, and the slithy toves\n"
  62. " Did gyre and gimble in the wabe:\n"
  63. "All mimsy were the borogoves,\n"
  64. " And the mome raths outgrabe.\n";
  65. static void FreeLines(void)
  66. {
  67. int i;
  68. if (rows > 0 && cols > 0) {
  69. for (i = 0; i < rows; ++i) {
  70. SDL_free(lines[i]->text);
  71. SDL_free(lines[i]);
  72. }
  73. SDL_free(lines);
  74. lines = NULL;
  75. }
  76. SDL_free(monkey_chars.text);
  77. monkey_chars.text = NULL;
  78. }
  79. static void OnWindowSizeChanged(void)
  80. {
  81. int w, h;
  82. if (!SDL_GetCurrentRenderOutputSize(renderer, &w, &h)) {
  83. return;
  84. }
  85. FreeLines();
  86. row = 0;
  87. rows = (h / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) - 4;
  88. cols = (w / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
  89. if (rows > 0 && cols > 0) {
  90. int i;
  91. lines = (Line **)SDL_malloc(rows * sizeof(Line *));
  92. if (lines) {
  93. for (i = 0; i < rows; ++i) {
  94. lines[i] = (Line *)SDL_malloc(sizeof(Line));
  95. if (!lines[i]) {
  96. FreeLines();
  97. break;
  98. }
  99. lines[i]->text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32));
  100. if (!lines[i]->text) {
  101. FreeLines();
  102. break;
  103. }
  104. lines[i]->length = 0;
  105. }
  106. }
  107. monkey_chars.text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32));
  108. if (monkey_chars.text) {
  109. for (i = 0; i < cols; ++i) {
  110. monkey_chars.text[i] = ' ';
  111. }
  112. monkey_chars.length = cols;
  113. }
  114. }
  115. }
  116. /* This function runs once at startup. */
  117. SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
  118. {
  119. int arg = 1;
  120. SDL_SetAppMetadata("Infinite Monkeys", "1.0", "com.example.infinite-monkeys");
  121. if (!SDL_Init(SDL_INIT_VIDEO)) {
  122. SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
  123. return SDL_APP_FAILURE;
  124. }
  125. if (!SDL_CreateWindowAndRenderer("examples/demo/infinite-monkeys", 640, 480, 0, &window, &renderer)) {
  126. SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
  127. return SDL_APP_FAILURE;
  128. }
  129. SDL_SetRenderVSync(renderer, 1);
  130. if (argv[arg] && SDL_strcmp(argv[arg], "--monkeys") == 0) {
  131. ++arg;
  132. if (argv[arg]) {
  133. monkeys = SDL_atoi(argv[arg]);
  134. ++arg;
  135. } else {
  136. SDL_Log("Usage: %s [--monkeys N] [file.txt]", argv[0]);
  137. return SDL_APP_FAILURE;
  138. }
  139. }
  140. if (argv[arg]) {
  141. const char *file = argv[arg];
  142. size_t size;
  143. text = (char *)SDL_LoadFile(file, &size);
  144. if (!text) {
  145. SDL_Log("Couldn't open %s: %s", file, SDL_GetError());
  146. return SDL_APP_FAILURE;
  147. }
  148. end = text + size;
  149. } else {
  150. text = SDL_strdup(default_text);
  151. end = text + SDL_strlen(text);
  152. }
  153. progress = text;
  154. SDL_GetCurrentTime(&start_time);
  155. OnWindowSizeChanged();
  156. return SDL_APP_CONTINUE; /* carry on with the program! */
  157. }
  158. /* This function runs when a new event (mouse input, keypresses, etc) occurs. */
  159. SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
  160. {
  161. switch (event->type) {
  162. case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
  163. OnWindowSizeChanged();
  164. break;
  165. case SDL_EVENT_QUIT:
  166. return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
  167. }
  168. return SDL_APP_CONTINUE; /* carry on with the program! */
  169. }
  170. static void DisplayLine(float x, float y, Line *line)
  171. {
  172. /* Allocate maximum space potentially needed for this line */
  173. char *utf8 = (char *)SDL_malloc(line->length * 4 + 1);
  174. if (utf8) {
  175. char *spot = utf8;
  176. int i;
  177. for (i = 0; i < line->length; ++i) {
  178. spot = SDL_UCS4ToUTF8(line->text[i], spot);
  179. }
  180. *spot = '\0';
  181. SDL_RenderDebugText(renderer, x, y, utf8);
  182. SDL_free(utf8);
  183. }
  184. }
  185. static bool CanMonkeyType(Uint32 ch)
  186. {
  187. SDL_Keymod modstate;
  188. SDL_Scancode scancode = SDL_GetScancodeFromKey(ch, &modstate);
  189. if (scancode < MIN_MONKEY_SCANCODE || scancode > MAX_MONKEY_SCANCODE) {
  190. return false;
  191. }
  192. /* Monkeys can hit the shift key, but nothing else */
  193. if ((modstate & ~SDL_KMOD_SHIFT) != 0) {
  194. return false;
  195. }
  196. return true;
  197. }
  198. static void AdvanceRow(void)
  199. {
  200. Line *line;
  201. ++row;
  202. line = lines[row % rows];
  203. line->length = 0;
  204. }
  205. static void AddMonkeyChar(int monkey, Uint32 ch)
  206. {
  207. if (monkey >= 0 && monkey_chars.text) {
  208. monkey_chars.text[(monkey % cols)] = ch;
  209. }
  210. if (lines) {
  211. if (ch == '\n') {
  212. AdvanceRow();
  213. } else {
  214. Line *line = lines[row % rows];
  215. line->text[line->length++] = ch;
  216. if (line->length == cols) {
  217. AdvanceRow();
  218. }
  219. }
  220. }
  221. SDL_StepUTF8(&progress, NULL);
  222. }
  223. static Uint32 GetNextChar(void)
  224. {
  225. Uint32 ch = 0;
  226. while (progress < end) {
  227. const char *spot = progress;
  228. ch = SDL_StepUTF8(&spot, NULL);
  229. if (CanMonkeyType(ch)) {
  230. break;
  231. } else {
  232. /* This is a freebie, monkeys can't type this */
  233. AddMonkeyChar(-1, ch);
  234. }
  235. }
  236. return ch;
  237. }
  238. static Uint32 MonkeyPlay(void)
  239. {
  240. int count = (MAX_MONKEY_SCANCODE - MIN_MONKEY_SCANCODE + 1);
  241. SDL_Scancode scancode = (SDL_Scancode)(MIN_MONKEY_SCANCODE + SDL_rand(count));
  242. SDL_Keymod modstate = (SDL_rand(2) ? SDL_KMOD_SHIFT : 0);
  243. return SDL_GetKeyFromScancode(scancode, modstate, false);
  244. }
  245. /* This function runs once per frame, and is the heart of the program. */
  246. SDL_AppResult SDL_AppIterate(void *appstate)
  247. {
  248. int i, monkey;
  249. Uint32 next_char = 0, ch;
  250. float x, y;
  251. char *caption = NULL;
  252. SDL_Time now, elapsed;
  253. int hours, minutes, seconds;
  254. SDL_FRect rect;
  255. for (monkey = 0; monkey < monkeys; ++monkey) {
  256. if (next_char == 0) {
  257. next_char = GetNextChar();
  258. if (!next_char) {
  259. /* All done! */
  260. break;
  261. }
  262. }
  263. ch = MonkeyPlay();
  264. if (ch == next_char) {
  265. AddMonkeyChar(monkey, ch);
  266. next_char = 0;
  267. }
  268. }
  269. /* Clear the screen */
  270. SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
  271. SDL_RenderClear(renderer);
  272. /* Show the text already decoded */
  273. SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
  274. x = 0.0f;
  275. y = 0.0f;
  276. if (lines) {
  277. int row_offset = row - rows + 1;
  278. if (row_offset < 0) {
  279. row_offset = 0;
  280. }
  281. for (i = 0; i < rows; ++i) {
  282. Line *line = lines[(row_offset + i) % rows];
  283. DisplayLine(x, y, line);
  284. y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
  285. }
  286. /* Show the caption */
  287. y = (float)((rows + 1) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
  288. if (progress == end) {
  289. if (!end_time) {
  290. SDL_GetCurrentTime(&end_time);
  291. }
  292. now = end_time;
  293. } else {
  294. SDL_GetCurrentTime(&now);
  295. }
  296. elapsed = (now - start_time);
  297. elapsed /= SDL_NS_PER_SECOND;
  298. seconds = (int)(elapsed % 60);
  299. elapsed /= 60;
  300. minutes = (int)(elapsed % 60);
  301. elapsed /= 60;
  302. hours = (int)elapsed;
  303. SDL_asprintf(&caption, "Monkeys: %d - %dH:%dM:%dS", monkeys, hours, minutes, seconds);
  304. if (caption) {
  305. SDL_RenderDebugText(renderer, x, y, caption);
  306. SDL_free(caption);
  307. }
  308. y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
  309. /* Show the characters currently typed */
  310. DisplayLine(x, y, &monkey_chars);
  311. y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
  312. }
  313. /* Show the current progress */
  314. SDL_SetRenderDrawColor(renderer, 0, 255, 0, SDL_ALPHA_OPAQUE);
  315. rect.x = x;
  316. rect.y = y;
  317. rect.w = ((float)(progress - text) / (end - text)) * (cols * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
  318. rect.h = (float)SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
  319. SDL_RenderFillRect(renderer, &rect);
  320. SDL_RenderPresent(renderer);
  321. return SDL_APP_CONTINUE; /* carry on with the program! */
  322. }
  323. /* This function runs once at shutdown. */
  324. void SDL_AppQuit(void *appstate, SDL_AppResult result)
  325. {
  326. /* SDL will clean up the window/renderer for us. */
  327. FreeLines();
  328. SDL_free(text);
  329. }