|
|
@@ -0,0 +1,197 @@
|
|
|
+#include <furi.h>
|
|
|
+#include <furi_hal.h>
|
|
|
+#include <gui/gui.h>
|
|
|
+#include <gui/elements.h>
|
|
|
+#include <gui/icon.h>
|
|
|
+#include <input/input.h>
|
|
|
+#include <notification/notification.h>
|
|
|
+#include <notification/notification_messages.h>
|
|
|
+#include <stdbool.h> // Header-file for boolean data-type.
|
|
|
+#include <stdio.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+/* generated by fbt from .png files in images folder */
|
|
|
+#include <simon_says_icons.h>
|
|
|
+
|
|
|
+#define WIDTH 64
|
|
|
+#define HEIGHT 144
|
|
|
+
|
|
|
+enum {
|
|
|
+ up,
|
|
|
+ down,
|
|
|
+ left,
|
|
|
+ right,
|
|
|
+} direction;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ bool isPlayerTurn;
|
|
|
+} SimonSaysData;
|
|
|
+
|
|
|
+// Sequence to indicate that this is the beginning of a turn
|
|
|
+const NotificationSequence sequence_begin_turn = {
|
|
|
+ &message_display_backlight_on,
|
|
|
+ &message_vibro_on,
|
|
|
+ &message_note_g5,
|
|
|
+ &message_delay_50,
|
|
|
+ &message_note_c6,
|
|
|
+ &message_delay_50,
|
|
|
+ &message_note_e5,
|
|
|
+ &message_vibro_off,
|
|
|
+ &message_sound_off,
|
|
|
+ NULL,
|
|
|
+};
|
|
|
+
|
|
|
+// sequence to indicate that we've reached the end of a turn
|
|
|
+const NotificationSequence sequence_end_turn = {
|
|
|
+ &message_display_backlight_on,
|
|
|
+ &message_red_0,
|
|
|
+ &message_vibro_on,
|
|
|
+ &message_note_g5,
|
|
|
+ &message_delay_50,
|
|
|
+ &message_note_e5,
|
|
|
+ &message_delay_50,
|
|
|
+ &message_vibro_off,
|
|
|
+ &message_sound_off,
|
|
|
+ &message_do_not_reset,
|
|
|
+ NULL,
|
|
|
+};
|
|
|
+
|
|
|
+// Indicate that drawing is enabled.
|
|
|
+const NotificationSequence sequence_player_turn_enabled = {
|
|
|
+ &message_red_255,
|
|
|
+ &message_do_not_reset,
|
|
|
+ NULL,
|
|
|
+};
|
|
|
+
|
|
|
+// Indicate that drawing is disabled.
|
|
|
+const NotificationSequence sequence_player_turn_disabled = {
|
|
|
+ &message_red_0,
|
|
|
+ &message_do_not_reset,
|
|
|
+ NULL,
|
|
|
+};
|
|
|
+
|
|
|
+const NotificationSequence sequence_cleanup = {
|
|
|
+ &message_red_0,
|
|
|
+ &message_green_0,
|
|
|
+ &message_blue_0,
|
|
|
+ &message_sound_off,
|
|
|
+ &message_vibro_off,
|
|
|
+ NULL,
|
|
|
+};
|
|
|
+
|
|
|
+void simon_says_draw_callback(Canvas* canvas, void* ctx) {
|
|
|
+ const SimonSaysData* game_state = acquire_mutex((ValueMutex*)ctx, 25);
|
|
|
+ UNUSED(ctx);
|
|
|
+
|
|
|
+ canvas_clear(canvas);
|
|
|
+
|
|
|
+ canvas_draw_icon(canvas, 2, 2, &I_board); // Draw board
|
|
|
+
|
|
|
+ //release the mutex
|
|
|
+ release_mutex((ValueMutex*)ctx, game_state);
|
|
|
+}
|
|
|
+
|
|
|
+void game_tick(void* ctx) {
|
|
|
+ SimonSaysData* game_state = acquire_mutex((ValueMutex*)ctx, 25);
|
|
|
+ UNUSED(ctx);
|
|
|
+
|
|
|
+ //release the mutex
|
|
|
+ release_mutex((ValueMutex*)ctx, game_state);
|
|
|
+}
|
|
|
+
|
|
|
+void simon_says_input_callback(InputEvent* input_event, void* ctx) {
|
|
|
+ furi_assert(ctx);
|
|
|
+ FuriMessageQueue* event_queue = ctx;
|
|
|
+ furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
|
|
+}
|
|
|
+
|
|
|
+int32_t simon_says_app(void* p) {
|
|
|
+ UNUSED(p);
|
|
|
+ FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
|
|
+
|
|
|
+ SimonSaysData* simon_says_state = malloc(sizeof(SimonSaysData));
|
|
|
+ ValueMutex simon_value_mutex;
|
|
|
+ if(!init_mutex(&simon_value_mutex, simon_says_state, sizeof(SimonSaysData))) {
|
|
|
+ FURI_LOG_E("simon_says", "cannot create mutex\r\n");
|
|
|
+ free(simon_says_state);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Configure view port
|
|
|
+ ViewPort* view_port = view_port_alloc();
|
|
|
+ view_port_draw_callback_set(view_port, simon_says_draw_callback, &simon_value_mutex);
|
|
|
+ view_port_input_callback_set(view_port, simon_says_input_callback, event_queue);
|
|
|
+
|
|
|
+ // Register view port in GUI
|
|
|
+ Gui* gui = furi_record_open(RECORD_GUI);
|
|
|
+ gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
|
|
+
|
|
|
+ NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
|
|
+
|
|
|
+ InputEvent event;
|
|
|
+
|
|
|
+ /* Create a timer. We do data analysis in the callback. */
|
|
|
+ FuriTimer* timer = furi_timer_alloc(game_tick, FuriTimerTypePeriodic, simon_says_state);
|
|
|
+ furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10);
|
|
|
+
|
|
|
+ while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
|
|
+ //break out of the loop if the back key is pressed
|
|
|
+ if(event.key == InputKeyBack && event.type == InputTypeLong) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Placholder button states
|
|
|
+ if(event.key == InputKeyBack && event.type == InputTypeLong) {
|
|
|
+ view_port_update(view_port);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Keep LED on while drawing
|
|
|
+ if(simon_says_state->isPlayerTurn) {
|
|
|
+ notification_message(notification, &sequence_player_turn_enabled);
|
|
|
+ } else {
|
|
|
+ notification_message(notification, &sequence_player_turn_disabled);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Placholder button states
|
|
|
+ if(event.key == InputKeyOk && event.type == InputTypeShort) {
|
|
|
+ // Do Nothing
|
|
|
+ }
|
|
|
+
|
|
|
+ // Placholder button states
|
|
|
+ if(event.key == InputKeyOk && event.type == InputTypeLong) {
|
|
|
+ // notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_begin_turn);
|
|
|
+ notification_message(notification, &sequence_begin_turn);
|
|
|
+ view_port_update(view_port);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Placholder button states
|
|
|
+ if(event.type == InputTypeShort || event.type == InputTypeRepeat ||
|
|
|
+ event.type == InputTypeLong) {
|
|
|
+ switch(event.key) {
|
|
|
+ case InputKeyUp:
|
|
|
+ break;
|
|
|
+ case InputKeyDown:
|
|
|
+ break;
|
|
|
+ case InputKeyLeft:
|
|
|
+ break;
|
|
|
+ case InputKeyRight:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ view_port_update(view_port);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ furi_timer_free(timer);
|
|
|
+ notification_message(notification, &sequence_cleanup);
|
|
|
+ gui_remove_view_port(gui, view_port);
|
|
|
+ view_port_free(view_port);
|
|
|
+ furi_message_queue_free(event_queue);
|
|
|
+ free(simon_says_state);
|
|
|
+ furi_record_close(RECORD_NOTIFICATION);
|
|
|
+ furi_record_close(RECORD_GUI);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|