minesweeper.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <gui/gui.h>
  4. #include <input/input.h>
  5. #include <stdlib.h>
  6. #include "assets.h"
  7. #define PLAYFIELD_WIDTH 16
  8. #define PLAYFIELD_HEIGHT 7
  9. #define TILE_WIDTH 8
  10. #define TILE_HEIGHT 8
  11. typedef enum {
  12. EventTypeTick,
  13. EventTypeKey,
  14. } EventType;
  15. typedef struct {
  16. EventType type;
  17. InputEvent input;
  18. } PluginEvent;
  19. typedef enum {
  20. TileTypeUncleared,
  21. TileTypeCleared,
  22. TileType0,
  23. TileType1,
  24. TileType2,
  25. TileType3,
  26. TileType4,
  27. TileType5,
  28. TileType6,
  29. TileType7,
  30. TileType8,
  31. TileTypeFlag,
  32. TileTypeMine
  33. } TileType;
  34. typedef enum {
  35. FieldMine,
  36. FieldEmpty
  37. } Field;
  38. typedef struct {
  39. Field minefield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT];
  40. TileType playfield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT];
  41. int cursor_cell_x;
  42. int cursor_cell_y;
  43. } Minesweeper;
  44. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  45. furi_assert(event_queue);
  46. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  47. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  48. }
  49. static void render_callback(Canvas* const canvas, void* ctx) {
  50. const Minesweeper* minesweeper_state = acquire_mutex((ValueMutex*)ctx, 25);
  51. if (minesweeper_state == NULL) {
  52. return;
  53. }
  54. canvas_set_font(canvas, FontPrimary);
  55. for (int y = 0; y < PLAYFIELD_HEIGHT; y++) {
  56. for (int x = 0; x < PLAYFIELD_WIDTH; x++) {
  57. if ( x == minesweeper_state->cursor_cell_x && y == minesweeper_state->cursor_cell_y) {
  58. canvas_invert_color(canvas);
  59. }
  60. canvas_draw_xbm(
  61. canvas,
  62. x*TILE_HEIGHT, // x
  63. 8 + (y * TILE_WIDTH), // y
  64. TILE_WIDTH,
  65. TILE_HEIGHT,
  66. tile_uncleared_bits);
  67. if ( x == minesweeper_state->cursor_cell_x && y == minesweeper_state->cursor_cell_y) {
  68. canvas_invert_color(canvas);
  69. }
  70. }
  71. }
  72. release_mutex((ValueMutex*)ctx, minesweeper_state);
  73. }
  74. static void minesweeper_state_init(Minesweeper* const plugin_state) {
  75. plugin_state->cursor_cell_x = plugin_state->cursor_cell_y = 0;
  76. }
  77. int32_t minesweeper_app(void* p) {
  78. UNUSED(p);
  79. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  80. Minesweeper* minesweeper_state = malloc(sizeof(Minesweeper));
  81. // setup
  82. minesweeper_state_init(minesweeper_state);
  83. ValueMutex state_mutex;
  84. if (!init_mutex(&state_mutex, minesweeper_state, sizeof(minesweeper_state))) {
  85. FURI_LOG_E("Minesweeper", "cannot create mutex\r\n");
  86. free(minesweeper_state);
  87. return 255;
  88. }
  89. // BEGIN IMPLEMENTATION
  90. // Set system callbacks
  91. ViewPort* view_port = view_port_alloc();
  92. view_port_draw_callback_set(view_port, render_callback, &state_mutex);
  93. view_port_input_callback_set(view_port, input_callback, event_queue);
  94. // Open GUI and register view_port
  95. Gui* gui = furi_record_open("gui");
  96. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  97. PluginEvent event;
  98. for (bool processing = true; processing;) {
  99. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  100. Minesweeper* minesweeper_state = (Minesweeper*)acquire_mutex_block(&state_mutex);
  101. if(event_status == FuriStatusOk) {
  102. // press events
  103. if(event.type == EventTypeKey) {
  104. if(event.input.type == InputTypePress) {
  105. switch(event.input.key) {
  106. case InputKeyUp:
  107. minesweeper_state->cursor_cell_y--;
  108. if(minesweeper_state->cursor_cell_y < 0) {
  109. minesweeper_state->cursor_cell_y = 0;
  110. }
  111. break;
  112. case InputKeyDown:
  113. minesweeper_state->cursor_cell_y++;
  114. if(minesweeper_state->cursor_cell_y > PLAYFIELD_HEIGHT) {
  115. minesweeper_state->cursor_cell_y = PLAYFIELD_HEIGHT;
  116. }
  117. break;
  118. case InputKeyRight:
  119. minesweeper_state->cursor_cell_x++;
  120. if(minesweeper_state->cursor_cell_x > PLAYFIELD_WIDTH) {
  121. minesweeper_state->cursor_cell_x = PLAYFIELD_WIDTH;
  122. }
  123. break;
  124. case InputKeyLeft:
  125. minesweeper_state->cursor_cell_x--;
  126. if(minesweeper_state->cursor_cell_x < 0) {
  127. minesweeper_state->cursor_cell_x = 0;
  128. }
  129. break;
  130. case InputKeyOk:
  131. break;
  132. case InputKeyBack:
  133. // Exit the plugin
  134. processing = false;
  135. break;
  136. }
  137. }
  138. }
  139. } else {
  140. FURI_LOG_D("Minesweeper", "FuriMessageQueue: event timeout");
  141. // event timeout
  142. }
  143. view_port_update(view_port);
  144. release_mutex(&state_mutex, minesweeper_state);
  145. }
  146. view_port_enabled_set(view_port, false);
  147. gui_remove_view_port(gui, view_port);
  148. furi_record_close("gui");
  149. view_port_free(view_port);
  150. furi_message_queue_free(event_queue);
  151. delete_mutex(&state_mutex);
  152. free(minesweeper_state);
  153. return 0;
  154. }