chess_clock.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #include <furi.h>
  2. #include <gui/gui.h>
  3. #include <input/input.h>
  4. #include <stdlib.h>
  5. #include <notification/notification_messages.h>
  6. #include <gui/elements.h>
  7. #include <furi_hal.h>
  8. typedef enum {
  9. EventTypeTick,
  10. EventTypeKey,
  11. } EventType;
  12. typedef struct {
  13. EventType type;
  14. InputEvent input;
  15. } PluginEvent;
  16. typedef enum {
  17. ChessClockStateSetup,
  18. ChessClockStatePlayer1,
  19. ChessClockStatePlayer2,
  20. ChessClockStateGameOver,
  21. } ChessClockState;
  22. typedef struct {
  23. ChessClockState state;
  24. uint32_t total_time;
  25. uint32_t time_left_p1;
  26. uint32_t time_left_p2;
  27. uint32_t last_tick;
  28. } PluginState;
  29. static void render_callback(Canvas* const canvas, void* ctx) {
  30. furi_assert(ctx);
  31. PluginState* plugin_state = ctx;
  32. char text[50];
  33. canvas_set_font(canvas, FontPrimary);
  34. switch(plugin_state->state) {
  35. case ChessClockStateSetup:
  36. case ChessClockStatePlayer1:
  37. case ChessClockStatePlayer2:
  38. snprintf(
  39. text,
  40. sizeof(text),
  41. "Player 1: %02lu:%02lu:%02lu\nPlayer 2: %02lu:%02lu:%02lu",
  42. plugin_state->time_left_p1 / 60000,
  43. (plugin_state->time_left_p1 / 1000) % 60,
  44. (plugin_state->time_left_p1 % 1000) / 10,
  45. plugin_state->time_left_p2 / 60000,
  46. (plugin_state->time_left_p2 / 1000) % 60,
  47. (plugin_state->time_left_p2 % 1000) / 10);
  48. break;
  49. case ChessClockStateGameOver:
  50. snprintf(text, sizeof(text), "Player %d wins!", plugin_state->time_left_p1 > 0 ? 1 : 2);
  51. break;
  52. }
  53. elements_multiline_text_aligned(canvas, 64, 32, AlignCenter, AlignCenter, text);
  54. }
  55. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  56. furi_assert(event_queue);
  57. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  58. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  59. }
  60. int32_t chess_clock_app() {
  61. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  62. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  63. PluginState* plugin_state = malloc(sizeof(PluginState));
  64. plugin_state->state = ChessClockStateSetup;
  65. plugin_state->total_time = 300000;
  66. plugin_state->time_left_p1 = plugin_state->total_time;
  67. plugin_state->time_left_p2 = plugin_state->total_time;
  68. plugin_state->last_tick = 0;
  69. ViewPort* view_port = view_port_alloc();
  70. view_port_draw_callback_set(view_port, render_callback, plugin_state);
  71. view_port_input_callback_set(view_port, input_callback, event_queue);
  72. Gui* gui = furi_record_open(RECORD_GUI);
  73. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  74. PluginEvent event;
  75. for(bool processing = true; processing;) {
  76. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 10);
  77. uint32_t now = furi_get_tick();
  78. if(event_status == FuriStatusOk && event.type == EventTypeKey &&
  79. event.input.key == InputKeyBack) {
  80. if(event.input.type == InputTypeShort) {
  81. plugin_state->state = ChessClockStateSetup;
  82. plugin_state->time_left_p1 = plugin_state->total_time;
  83. plugin_state->time_left_p2 = plugin_state->total_time;
  84. notification_message(notification, &sequence_reset_rgb);
  85. } else if(event.input.type == InputTypeLong) {
  86. processing = false;
  87. }
  88. }
  89. switch(plugin_state->state) {
  90. case ChessClockStateSetup:
  91. if(event_status == FuriStatusOk && event.type == EventTypeKey) {
  92. if(event.input.key == InputKeyUp && event.input.type == InputTypePress) {
  93. plugin_state->total_time += 15000;
  94. plugin_state->time_left_p1 = plugin_state->total_time;
  95. plugin_state->time_left_p2 = plugin_state->total_time;
  96. } else if(event.input.key == InputKeyDown && event.input.type == InputTypePress) {
  97. if(plugin_state->total_time > 15000) {
  98. plugin_state->total_time -= 15000;
  99. plugin_state->time_left_p1 = plugin_state->total_time;
  100. plugin_state->time_left_p2 = plugin_state->total_time;
  101. }
  102. } else if(event.input.key == InputKeyOk && event.input.type == InputTypePress) {
  103. plugin_state->state = ChessClockStatePlayer1;
  104. plugin_state->last_tick = now;
  105. }
  106. }
  107. break;
  108. case ChessClockStatePlayer1:
  109. if(event_status == FuriStatusOk && event.type == EventTypeKey &&
  110. event.input.key == InputKeyOk && event.input.type == InputTypePress) {
  111. plugin_state->state = ChessClockStatePlayer2;
  112. plugin_state->last_tick = now;
  113. } else {
  114. plugin_state->time_left_p1 -= now - plugin_state->last_tick;
  115. if(plugin_state->time_left_p1 <= 0 ||
  116. plugin_state->time_left_p1 >= UINT32_MAX - 1000) {
  117. plugin_state->time_left_p1 = 0;
  118. plugin_state->state = ChessClockStateGameOver;
  119. notification_message(notification, &sequence_set_only_blue_255);
  120. notification_message(notification, &sequence_single_vibro);
  121. }
  122. plugin_state->last_tick = now;
  123. }
  124. break;
  125. case ChessClockStatePlayer2:
  126. if(event_status == FuriStatusOk && event.type == EventTypeKey &&
  127. event.input.key == InputKeyOk && event.input.type == InputTypePress) {
  128. plugin_state->state = ChessClockStatePlayer1;
  129. plugin_state->last_tick = now;
  130. } else {
  131. plugin_state->time_left_p2 -= now - plugin_state->last_tick;
  132. if(plugin_state->time_left_p2 <= 0 ||
  133. plugin_state->time_left_p2 >= UINT32_MAX - 1000) {
  134. plugin_state->time_left_p2 = 0;
  135. plugin_state->state = ChessClockStateGameOver;
  136. notification_message(notification, &sequence_set_only_green_255);
  137. notification_message(notification, &sequence_single_vibro);
  138. }
  139. plugin_state->last_tick = now;
  140. }
  141. break;
  142. case ChessClockStateGameOver:
  143. if(event_status == FuriStatusOk && event.type == EventTypeKey &&
  144. event.input.key == InputKeyBack && event.input.type == InputTypeShort) {
  145. plugin_state->state = ChessClockStateSetup;
  146. plugin_state->time_left_p1 = plugin_state->total_time;
  147. plugin_state->time_left_p2 = plugin_state->total_time;
  148. }
  149. break;
  150. }
  151. view_port_update(view_port);
  152. }
  153. view_port_enabled_set(view_port, false);
  154. gui_remove_view_port(gui, view_port);
  155. furi_record_close(RECORD_GUI);
  156. view_port_free(view_port);
  157. furi_message_queue_free(event_queue);
  158. free(plugin_state);
  159. return 0;
  160. }