main.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*
  2. * This example code implements a Snake game that showcases some of the
  3. * functionalities of SDL, such as timer callbacks and event handling.
  4. *
  5. * This code is public domain. Feel free to use it for any purpose!
  6. */
  7. #define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
  8. #include <SDL3/SDL.h>
  9. #include <SDL3/SDL_main.h>
  10. #include <stdlib.h> /* malloc(), free() */
  11. #include "snake.h"
  12. #define STEP_RATE_IN_MILLISECONDS 125
  13. #define SNAKE_BLOCK_SIZE_IN_PIXELS 24
  14. #define SDL_WINDOW_WIDTH (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_WIDTH)
  15. #define SDL_WINDOW_HEIGHT (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_HEIGHT)
  16. typedef struct
  17. {
  18. SDL_Window *window;
  19. SDL_Renderer *renderer;
  20. SDL_TimerID step_timer;
  21. SnakeContext snake_ctx;
  22. } AppState;
  23. static Uint32 sdl_timer_callback_(void *payload, SDL_TimerID timer_id, Uint32 interval)
  24. {
  25. /* NOTE: snake_step is not called here directly for multithreaded concerns. */
  26. SDL_Event event;
  27. SDL_zero(event);
  28. event.type = SDL_EVENT_USER;
  29. SDL_PushEvent(&event);
  30. return interval;
  31. }
  32. static int handle_key_event_(SnakeContext *ctx, SDL_Scancode key_code)
  33. {
  34. switch (key_code) {
  35. /* Quit. */
  36. case SDL_SCANCODE_ESCAPE:
  37. case SDL_SCANCODE_Q:
  38. return SDL_APP_SUCCESS;
  39. /* Restart the game as if the program was launched. */
  40. case SDL_SCANCODE_R:
  41. snake_initialize(ctx, SDL_rand);
  42. break;
  43. /* Decide new direction of the snake. */
  44. case SDL_SCANCODE_RIGHT:
  45. snake_redir(ctx, SNAKE_DIR_RIGHT);
  46. break;
  47. case SDL_SCANCODE_UP:
  48. snake_redir(ctx, SNAKE_DIR_UP);
  49. break;
  50. case SDL_SCANCODE_LEFT:
  51. snake_redir(ctx, SNAKE_DIR_LEFT);
  52. break;
  53. case SDL_SCANCODE_DOWN:
  54. snake_redir(ctx, SNAKE_DIR_DOWN);
  55. break;
  56. default:
  57. break;
  58. }
  59. return SDL_APP_CONTINUE;
  60. }
  61. static void set_rect_xy_(SDL_FRect *r, short x, short y)
  62. {
  63. r->x = (float)(x * SNAKE_BLOCK_SIZE_IN_PIXELS);
  64. r->y = (float)(y * SNAKE_BLOCK_SIZE_IN_PIXELS);
  65. }
  66. SDL_AppResult SDL_AppIterate(void *appstate)
  67. {
  68. AppState *as;
  69. SnakeContext *ctx;
  70. SDL_FRect r;
  71. unsigned i;
  72. unsigned j;
  73. int ct;
  74. as = (AppState *)appstate;
  75. ctx = &as->snake_ctx;
  76. r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS;
  77. SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, 255);
  78. SDL_RenderClear(as->renderer);
  79. for (i = 0; i < SNAKE_GAME_WIDTH; i++) {
  80. for (j = 0; j < SNAKE_GAME_HEIGHT; j++) {
  81. ct = snake_cell_at(ctx, i, j);
  82. if (ct == SNAKE_CELL_NOTHING)
  83. continue;
  84. set_rect_xy_(&r, i, j);
  85. if (ct == SNAKE_CELL_FOOD)
  86. SDL_SetRenderDrawColor(as->renderer, 0, 0, 128, 255);
  87. else /* body */
  88. SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, 255);
  89. SDL_RenderFillRect(as->renderer, &r);
  90. }
  91. }
  92. SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, 255); /*head*/
  93. set_rect_xy_(&r, ctx->head_xpos, ctx->head_ypos);
  94. SDL_RenderFillRect(as->renderer, &r);
  95. SDL_RenderPresent(as->renderer);
  96. return SDL_APP_CONTINUE;
  97. }
  98. SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
  99. {
  100. (void)argc;
  101. (void)argv;
  102. if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {
  103. return SDL_APP_FAILURE;
  104. }
  105. SDL_SetHint("SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR", "0");
  106. AppState *as = malloc(sizeof(AppState));
  107. *appstate = as;
  108. as->step_timer = 0;
  109. if (!SDL_CreateWindowAndRenderer("examples/game/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer)) {
  110. return SDL_APP_FAILURE;
  111. }
  112. snake_initialize(&as->snake_ctx, SDL_rand);
  113. as->step_timer = SDL_AddTimer(
  114. STEP_RATE_IN_MILLISECONDS,
  115. sdl_timer_callback_,
  116. NULL);
  117. if (as->step_timer == 0) {
  118. return SDL_APP_FAILURE;
  119. }
  120. return SDL_APP_CONTINUE;
  121. }
  122. SDL_AppResult SDL_AppEvent(void *appstate, const SDL_Event *event)
  123. {
  124. SnakeContext *ctx = &((AppState *)appstate)->snake_ctx;
  125. switch (event->type) {
  126. case SDL_EVENT_QUIT:
  127. return SDL_APP_SUCCESS;
  128. case SDL_EVENT_USER:
  129. snake_step(ctx, SDL_rand);
  130. break;
  131. case SDL_EVENT_KEY_DOWN:
  132. return handle_key_event_(ctx, event->key.scancode);
  133. }
  134. return SDL_APP_CONTINUE;
  135. }
  136. void SDL_AppQuit(void *appstate)
  137. {
  138. if (appstate != NULL) {
  139. AppState *as = (AppState *)appstate;
  140. SDL_RemoveTimer(as->step_timer);
  141. SDL_DestroyRenderer(as->renderer);
  142. SDL_DestroyWindow(as->window);
  143. free(as);
  144. }
  145. }