game_engine.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #include "game_engine.h"
  2. #include <furi.h>
  3. #include <gui/gui.h>
  4. #include <input/input.h>
  5. #include "clock_timer.h"
  6. typedef _Atomic uint32_t AtomicUint32;
  7. GameEngineSettings game_engine_settings_init() {
  8. GameEngineSettings settings;
  9. settings.fps = 60.0f;
  10. settings.show_fps = false;
  11. return settings;
  12. }
  13. struct GameEngine {
  14. Gui* gui;
  15. FuriPubSub* input_pubsub;
  16. FuriThreadId thread_id;
  17. GameEngineSettings settings;
  18. volatile bool running;
  19. };
  20. typedef enum {
  21. GameThreadFlagUpdate = 1 << 0,
  22. GameThreadFlagStop = 1 << 1,
  23. } GameThreadFlag;
  24. #define GameThreadFlagMask (GameThreadFlagUpdate | GameThreadFlagStop)
  25. GameEngine* game_engine_alloc(GameEngineSettings settings) {
  26. GameEngine* engine = malloc(sizeof(GameEngine));
  27. engine->gui = furi_record_open(RECORD_GUI);
  28. engine->input_pubsub = furi_record_open(RECORD_INPUT_EVENTS);
  29. engine->thread_id = furi_thread_get_current_id();
  30. engine->settings = settings;
  31. engine->running = false;
  32. return engine;
  33. }
  34. void game_engine_free(GameEngine* engine) {
  35. furi_record_close(RECORD_GUI);
  36. furi_record_close(RECORD_INPUT_EVENTS);
  37. free(engine);
  38. }
  39. static void canvas_printf(Canvas* canvas, uint8_t x, uint8_t y, const char* format, ...) {
  40. FuriString* string = furi_string_alloc();
  41. va_list args;
  42. va_start(args, format);
  43. furi_string_vprintf(string, format, args);
  44. va_end(args);
  45. canvas_draw_str(canvas, x, y, furi_string_get_cstr(string));
  46. furi_string_free(string);
  47. }
  48. static void clock_timer_callback(void* context) {
  49. GameEngine* engine = context;
  50. furi_thread_flags_set(engine->thread_id, GameThreadFlagUpdate);
  51. }
  52. static void input_events_callback(const void* value, void* context) {
  53. AtomicUint32* input_state = context;
  54. const InputEvent* event = value;
  55. if(event->type == InputTypePress) {
  56. *input_state |= (1 << event->key);
  57. } else if(event->type == InputTypeRelease) {
  58. *input_state &= ~(1 << event->key);
  59. }
  60. }
  61. typedef struct {
  62. uint32_t current;
  63. uint32_t pressed;
  64. uint32_t released;
  65. } InputState;
  66. void game_engine_run(GameEngine* engine) {
  67. engine->running = true;
  68. // input state
  69. AtomicUint32 input_state = 0;
  70. uint32_t input_prev_state = 0;
  71. // acquire gui canvas
  72. Canvas* canvas = gui_direct_draw_acquire(engine->gui);
  73. // subscribe to input events
  74. FuriPubSubSubscription* input_subscription =
  75. furi_pubsub_subscribe(engine->input_pubsub, input_events_callback, &input_state);
  76. // start "game update" timer
  77. clock_timer_start(clock_timer_callback, engine, engine->settings.fps);
  78. // init fps counter
  79. uint32_t time_start = DWT->CYCCNT;
  80. while(true) {
  81. uint32_t flags =
  82. furi_thread_flags_wait(GameThreadFlagMask, FuriFlagWaitAny, FuriWaitForever);
  83. furi_check((flags & FuriFlagError) == 0);
  84. if(flags & GameThreadFlagUpdate) {
  85. uint32_t input_current_state = input_state;
  86. InputState input = {
  87. .current = input_current_state,
  88. .pressed = input_current_state & ~input_prev_state,
  89. .released = ~input_current_state & input_prev_state,
  90. };
  91. input_prev_state = input_current_state;
  92. canvas_reset(canvas);
  93. uint32_t time_end = DWT->CYCCNT;
  94. uint32_t time_delta = time_end - time_start;
  95. time_start = time_end;
  96. if(engine->settings.show_fps) {
  97. float fps = 1.0f / (time_delta / (float)SystemCoreClock);
  98. canvas_set_color(canvas, ColorXOR);
  99. canvas_printf(canvas, 0, 7, "%u", (uint32_t)roundf(fps));
  100. }
  101. if(input.pressed != 0) {
  102. FURI_LOG_I("input", "pressed: %lu", input.pressed);
  103. if(input.pressed & (1 << InputKeyBack)) {
  104. game_engine_stop(engine);
  105. }
  106. }
  107. if(input.released != 0) {
  108. FURI_LOG_I("input", "released: %lu", input.released);
  109. }
  110. canvas_commit(canvas);
  111. } else if(flags & GameThreadFlagStop) {
  112. break;
  113. }
  114. }
  115. clock_timer_stop();
  116. gui_direct_draw_release(engine->gui);
  117. furi_pubsub_unsubscribe(engine->input_pubsub, input_subscription);
  118. engine->running = false;
  119. }
  120. void game_engine_stop(GameEngine* engine) {
  121. furi_thread_flags_set(engine->thread_id, GameThreadFlagStop);
  122. }