main.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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. SDL_Event e;
  26. SDL_UserEvent ue;
  27. /* NOTE: snake_step is not called here directly for multithreaded concerns. */
  28. (void)payload;
  29. ue.type = SDL_EVENT_USER;
  30. ue.code = 0;
  31. ue.data1 = NULL;
  32. ue.data2 = NULL;
  33. e.type = SDL_EVENT_USER;
  34. e.user = ue;
  35. SDL_PushEvent(&e);
  36. return interval;
  37. }
  38. static int handle_key_event_(SnakeContext *ctx, SDL_Scancode key_code)
  39. {
  40. switch (key_code) {
  41. /* Quit. */
  42. case SDL_SCANCODE_ESCAPE:
  43. case SDL_SCANCODE_Q:
  44. return SDL_APP_SUCCESS;
  45. /* Restart the game as if the program was launched. */
  46. case SDL_SCANCODE_R:
  47. snake_initialize(ctx, SDL_rand);
  48. break;
  49. /* Decide new direction of the snake. */
  50. case SDL_SCANCODE_RIGHT:
  51. snake_redir(ctx, SNAKE_DIR_RIGHT);
  52. break;
  53. case SDL_SCANCODE_UP:
  54. snake_redir(ctx, SNAKE_DIR_UP);
  55. break;
  56. case SDL_SCANCODE_LEFT:
  57. snake_redir(ctx, SNAKE_DIR_LEFT);
  58. break;
  59. case SDL_SCANCODE_DOWN:
  60. snake_redir(ctx, SNAKE_DIR_DOWN);
  61. break;
  62. default:
  63. break;
  64. }
  65. return SDL_APP_CONTINUE;
  66. }
  67. static void set_rect_xy_(SDL_FRect *r, short x, short y)
  68. {
  69. r->x = (float)(x * SNAKE_BLOCK_SIZE_IN_PIXELS);
  70. r->y = (float)(y * SNAKE_BLOCK_SIZE_IN_PIXELS);
  71. }
  72. SDL_AppResult SDL_AppIterate(void *appstate)
  73. {
  74. AppState *as;
  75. SnakeContext *ctx;
  76. SDL_FRect r;
  77. unsigned i;
  78. unsigned j;
  79. int ct;
  80. as = (AppState *)appstate;
  81. ctx = &as->snake_ctx;
  82. r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS;
  83. SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, 255);
  84. SDL_RenderClear(as->renderer);
  85. for (i = 0; i < SNAKE_GAME_WIDTH; i++) {
  86. for (j = 0; j < SNAKE_GAME_HEIGHT; j++) {
  87. ct = snake_cell_at(ctx, i, j);
  88. if (ct == SNAKE_CELL_NOTHING)
  89. continue;
  90. set_rect_xy_(&r, i, j);
  91. if (ct == SNAKE_CELL_FOOD)
  92. SDL_SetRenderDrawColor(as->renderer, 0, 0, 128, 255);
  93. else /* body */
  94. SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, 255);
  95. SDL_RenderFillRect(as->renderer, &r);
  96. }
  97. }
  98. SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, 255); /*head*/
  99. set_rect_xy_(&r, ctx->head_xpos, ctx->head_ypos);
  100. SDL_RenderFillRect(as->renderer, &r);
  101. SDL_RenderPresent(as->renderer);
  102. return SDL_APP_CONTINUE;
  103. }
  104. SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
  105. {
  106. (void)argc;
  107. (void)argv;
  108. if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
  109. return SDL_APP_FAILURE;
  110. }
  111. SDL_SetHint("SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR", "0");
  112. AppState *as = malloc(sizeof(AppState));
  113. *appstate = as;
  114. as->step_timer = 0;
  115. if (SDL_CreateWindowAndRenderer("examples/game/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer) == -1) {
  116. return SDL_APP_FAILURE;
  117. }
  118. snake_initialize(&as->snake_ctx, SDL_rand);
  119. as->step_timer = SDL_AddTimer(
  120. STEP_RATE_IN_MILLISECONDS,
  121. sdl_timer_callback_,
  122. NULL);
  123. if (as->step_timer == 0) {
  124. return SDL_APP_FAILURE;
  125. }
  126. return SDL_APP_CONTINUE;
  127. }
  128. SDL_AppResult SDL_AppEvent(void *appstate, const SDL_Event *event)
  129. {
  130. SnakeContext *ctx = &((AppState *)appstate)->snake_ctx;
  131. switch (event->type) {
  132. case SDL_EVENT_QUIT:
  133. return SDL_APP_SUCCESS;
  134. case SDL_EVENT_USER:
  135. snake_step(ctx, SDL_rand);
  136. break;
  137. case SDL_EVENT_KEY_DOWN:
  138. return handle_key_event_(ctx, event->key.scancode);
  139. }
  140. return SDL_APP_CONTINUE;
  141. }
  142. void SDL_AppQuit(void *appstate)
  143. {
  144. if (appstate != NULL) {
  145. AppState *as = (AppState *)appstate;
  146. SDL_RemoveTimer(as->step_timer);
  147. SDL_DestroyRenderer(as->renderer);
  148. SDL_DestroyWindow(as->window);
  149. free(as);
  150. }
  151. }