fgeo.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. #include "fgeo.h"
  2. #include <furi.h>
  3. #include <gui/gui.h>
  4. #include <input/input.h>
  5. #include "clock_timer.h"
  6. GameEngineSettings game_engine_settings_init() {
  7. GameEngineSettings settings;
  8. settings.fps = 60.0f;
  9. settings.show_fps = false;
  10. return settings;
  11. }
  12. struct GameEngine {
  13. Gui* gui;
  14. FuriPubSub* input_pubsub;
  15. FuriThreadId thread_id;
  16. GameEngineSettings settings;
  17. volatile bool running;
  18. };
  19. typedef enum {
  20. GameThreadFlagUpdate = 1 << 0,
  21. GameThreadFlagStop = 1 << 1,
  22. } GameThreadFlag;
  23. #define GameThreadFlagMask (GameThreadFlagUpdate | GameThreadFlagStop)
  24. static void clock_timer_tick(void* context) {
  25. GameEngine* engine = context;
  26. furi_thread_flags_set(engine->thread_id, GameThreadFlagUpdate);
  27. }
  28. static void input_events_callback(const void* value, void* context) {
  29. GameEngine* engine = context;
  30. const InputEvent* event = value;
  31. if(event->key == InputKeyBack && event->type == InputTypePress) {
  32. furi_thread_flags_set(engine->thread_id, GameThreadFlagStop);
  33. }
  34. }
  35. GameEngine* game_engine_alloc(GameEngineSettings settings) {
  36. GameEngine* engine = malloc(sizeof(GameEngine));
  37. engine->gui = furi_record_open(RECORD_GUI);
  38. engine->input_pubsub = furi_record_open(RECORD_INPUT_EVENTS);
  39. engine->thread_id = furi_thread_get_current_id();
  40. engine->settings = settings;
  41. engine->running = false;
  42. return engine;
  43. }
  44. void game_engine_free(GameEngine* engine) {
  45. furi_record_close(RECORD_GUI);
  46. furi_record_close(RECORD_INPUT_EVENTS);
  47. free(engine);
  48. }
  49. static void canvas_printf(Canvas* canvas, uint8_t x, uint8_t y, const char* format, ...) {
  50. FuriString* string = furi_string_alloc();
  51. va_list args;
  52. va_start(args, format);
  53. furi_string_vprintf(string, format, args);
  54. va_end(args);
  55. canvas_draw_str(canvas, x, y, furi_string_get_cstr(string));
  56. furi_string_free(string);
  57. }
  58. void game_engine_run(GameEngine* engine) {
  59. engine->running = true;
  60. Canvas* canvas = gui_direct_draw_acquire(engine->gui);
  61. FuriPubSubSubscription* input_subscription =
  62. furi_pubsub_subscribe(engine->input_pubsub, input_events_callback, engine);
  63. clock_timer_init(clock_timer_tick, engine, engine->settings.fps);
  64. clock_timer_start();
  65. uint32_t time_start = DWT->CYCCNT;
  66. while(true) {
  67. uint32_t flags =
  68. furi_thread_flags_wait(GameThreadFlagMask, FuriFlagWaitAny, FuriWaitForever);
  69. furi_check((flags & FuriFlagError) == 0);
  70. if(flags & GameThreadFlagUpdate) {
  71. canvas_reset(canvas);
  72. uint32_t time_end = DWT->CYCCNT;
  73. uint32_t time_delta = time_end - time_start;
  74. time_start = time_end;
  75. if(engine->settings.show_fps) {
  76. float fps = 1.0f / (time_delta / (float)SystemCoreClock);
  77. canvas_set_color(canvas, ColorXOR);
  78. canvas_printf(canvas, 0, 7, "%u", (uint32_t)roundf(fps));
  79. }
  80. canvas_commit(canvas);
  81. } else if(flags & GameThreadFlagStop) {
  82. break;
  83. }
  84. }
  85. clock_timer_stop();
  86. clock_timer_deinit();
  87. gui_direct_draw_release(engine->gui);
  88. furi_pubsub_unsubscribe(engine->input_pubsub, input_subscription);
  89. engine->running = false;
  90. }